aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/test.yml5
-rw-r--r--client/src/app/+about/about-instance/contact-admin-modal.component.ts3
-rw-r--r--client/src/app/+accounts/accounts.component.ts3
-rw-r--r--client/src/app/+admin/admin.component.ts4
-rw-r--r--client/src/app/+admin/admin.module.ts3
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.html4
-rw-r--r--client/src/app/+admin/follows/following-list/follow-modal.component.html42
-rw-r--r--client/src/app/+admin/follows/following-list/follow-modal.component.scss3
-rw-r--r--client/src/app/+admin/follows/following-list/follow-modal.component.ts69
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.html21
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.ts22
-rw-r--r--client/src/app/+admin/follows/following-list/index.ts1
-rw-r--r--client/src/app/+admin/follows/follows.routes.ts4
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts8
-rw-r--r--client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts2
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.ts16
-rw-r--r--client/src/app/+login/login.component.html4
-rw-r--r--client/src/app/+login/login.component.ts4
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts7
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts4
-rw-r--r--client/src/app/+my-library/my-videos/my-videos.component.html2
-rw-r--r--client/src/app/+page-not-found/page-not-found.component.ts3
-rw-r--r--client/src/app/+search/search-filters.component.html16
-rw-r--r--client/src/app/+search/search-filters.component.ts8
-rw-r--r--client/src/app/+search/search.component.html2
-rw-r--r--client/src/app/+search/search.component.scss4
-rw-r--r--client/src/app/+search/search.component.ts32
-rw-r--r--client/src/app/+video-channels/video-channels.component.ts2
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.html2
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts3
-rw-r--r--client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts2
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.html62
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.scss18
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.ts4
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts3
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.ts14
-rw-r--r--client/src/app/core/auth/auth.service.ts3
-rw-r--r--client/src/app/core/menu/menu.service.ts2
-rw-r--r--client/src/app/core/renderer/markdown.service.ts10
-rw-r--r--client/src/app/core/rest/rest-extractor.service.ts3
-rw-r--r--client/src/app/helpers/utils.ts2
-rw-r--r--client/src/app/shared/form-validators/batch-domains-validators.ts60
-rw-r--r--client/src/app/shared/form-validators/host-validators.ts105
-rw-r--r--client/src/app/shared/form-validators/host.ts8
-rw-r--r--client/src/app/shared/form-validators/index.ts3
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts7
-rw-r--r--client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts7
-rw-r--r--client/src/app/shared/shared-forms/markdown-textarea.component.ts5
-rw-r--r--client/src/app/shared/shared-forms/timestamp-input.component.ts2
-rw-r--r--client/src/app/shared/shared-instance/instance-follow.service.ts13
-rw-r--r--client/src/app/shared/shared-main/auth/auth-interceptor.service.ts8
-rw-r--r--client/src/app/shared/shared-main/users/user-notification.model.ts6
-rw-r--r--client/src/app/shared/shared-main/users/user-notifications.component.ts2
-rw-r--r--client/src/app/shared/shared-main/video/video.model.ts3
-rw-r--r--client/src/app/shared/shared-moderation/batch-domains-modal.component.html14
-rw-r--r--client/src/app/shared/shared-moderation/batch-domains-modal.component.ts8
-rw-r--r--client/src/app/shared/shared-moderation/report-modals/video-report.component.ts8
-rw-r--r--client/src/app/shared/shared-search/advanced-search.model.ts34
-rw-r--r--client/src/app/shared/shared-search/search.service.ts35
-rw-r--r--client/src/app/shared/shared-share-modal/video-share.component.ts33
-rw-r--r--client/src/app/shared/shared-video-miniature/abstract-video-list.ts2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist.model.ts3
-rw-r--r--client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts2
-rw-r--r--client/src/assets/player/peertube-player-manager.ts43
-rw-r--r--client/src/assets/player/peertube-plugin.ts12
-rw-r--r--client/src/assets/player/peertube-videojs-typings.ts5
-rw-r--r--client/src/assets/player/playlist/playlist-menu-item.ts2
-rw-r--r--client/src/assets/player/stats/stats-card.ts3
-rw-r--r--client/src/assets/player/utils.ts136
-rw-r--r--client/src/assets/player/videojs-components/peertube-link-button.ts20
-rw-r--r--client/src/assets/player/webtorrent/webtorrent-plugin.ts11
-rw-r--r--client/src/locale/angular.ar.xlf617
-rw-r--r--client/src/locale/angular.ca-ES.xlf731
-rw-r--r--client/src/locale/angular.cs-CZ.xlf611
-rw-r--r--client/src/locale/angular.da-DK.xlf523
-rw-r--r--client/src/locale/angular.de-DE.xlf609
-rw-r--r--client/src/locale/angular.el-GR.xlf541
-rw-r--r--client/src/locale/angular.en-GB.xlf426
-rw-r--r--client/src/locale/angular.en-US.xlf424
-rw-r--r--client/src/locale/angular.eo.xlf521
-rw-r--r--client/src/locale/angular.es-ES.xlf711
-rw-r--r--client/src/locale/angular.eu-ES.xlf523
-rw-r--r--client/src/locale/angular.fa-IR.xlf569
-rw-r--r--client/src/locale/angular.fi-FI.xlf426
-rw-r--r--client/src/locale/angular.fr-FR.xlf711
-rw-r--r--client/src/locale/angular.gd.xlf711
-rw-r--r--client/src/locale/angular.gl-ES.xlf711
-rw-r--r--client/src/locale/angular.hu-HU.xlf541
-rw-r--r--client/src/locale/angular.it-IT.xlf521
-rw-r--r--client/src/locale/angular.ja-JP.xlf711
-rw-r--r--client/src/locale/angular.jbo.xlf426
-rw-r--r--client/src/locale/angular.kab.xlf541
-rw-r--r--client/src/locale/angular.ko-KR.xlf523
-rw-r--r--client/src/locale/angular.lt-LT.xlf426
-rw-r--r--client/src/locale/angular.nb-NO.xlf517
-rw-r--r--client/src/locale/angular.nl-NL.xlf512
-rw-r--r--client/src/locale/angular.oc.xlf569
-rw-r--r--client/src/locale/angular.pl-PL.xlf713
-rw-r--r--client/src/locale/angular.pt-BR.xlf569
-rw-r--r--client/src/locale/angular.pt-PT.xlf426
-rw-r--r--client/src/locale/angular.ru-RU.xlf711
-rw-r--r--client/src/locale/angular.sk-SK.xlf426
-rw-r--r--client/src/locale/angular.sl-SI.xlf543
-rw-r--r--client/src/locale/angular.sv-SE.xlf711
-rw-r--r--client/src/locale/angular.ta.xlf426
-rw-r--r--client/src/locale/angular.th-TH.xlf711
-rw-r--r--client/src/locale/angular.tr-TR.xlf525
-rw-r--r--client/src/locale/angular.uk-UA.xlf523
-rw-r--r--client/src/locale/angular.vi-VN.xlf711
-rw-r--r--client/src/locale/angular.xlf433
-rw-r--r--client/src/locale/angular.zh-Hans-CN.xlf611
-rw-r--r--client/src/locale/angular.zh-Hant-TW.xlf729
-rw-r--r--client/src/sass/application.scss8
-rw-r--r--client/src/standalone/videos/embed.ts3
-rw-r--r--package.json8
-rw-r--r--scripts/benchmark.ts44
-rwxr-xr-xscripts/ci.sh5
-rwxr-xr-xscripts/generate-code-contributors.ts4
-rw-r--r--scripts/optimize-old-videos.ts32
-rwxr-xr-xscripts/parse-log.ts3
-rwxr-xr-xscripts/prune-storage.ts62
-rwxr-xr-xscripts/update-host.ts13
-rw-r--r--server.ts14
-rw-r--r--server/controllers/activitypub/client.ts4
-rw-r--r--server/controllers/activitypub/inbox.ts2
-rw-r--r--server/controllers/api/abuse.ts2
-rw-r--r--server/controllers/api/accounts.ts20
-rw-r--r--server/controllers/api/bulk.ts6
-rw-r--r--server/controllers/api/config.ts2
-rw-r--r--server/controllers/api/custom-page.ts3
-rw-r--r--server/controllers/api/index.ts2
-rw-r--r--server/controllers/api/oauth-clients.ts2
-rw-r--r--server/controllers/api/overviews.ts25
-rw-r--r--server/controllers/api/plugins.ts2
-rw-r--r--server/controllers/api/search/search-video-channels.ts24
-rw-r--r--server/controllers/api/search/search-video-playlists.ts20
-rw-r--r--server/controllers/api/search/search-videos.ts27
-rw-r--r--server/controllers/api/server/contact.ts10
-rw-r--r--server/controllers/api/server/debug.ts8
-rw-r--r--server/controllers/api/server/follows.ts23
-rw-r--r--server/controllers/api/server/index.ts8
-rw-r--r--server/controllers/api/server/logs.ts11
-rw-r--r--server/controllers/api/server/redundancy.ts16
-rw-r--r--server/controllers/api/server/server-blocklist.ts4
-rw-r--r--server/controllers/api/server/stats.ts4
-rw-r--r--server/controllers/api/users/index.ts6
-rw-r--r--server/controllers/api/users/me.ts4
-rw-r--r--server/controllers/api/users/my-blocklist.ts10
-rw-r--r--server/controllers/api/users/my-history.ts6
-rw-r--r--server/controllers/api/users/my-notifications.ts2
-rw-r--r--server/controllers/api/users/my-subscriptions.ts17
-rw-r--r--server/controllers/api/users/my-video-playlists.ts2
-rw-r--r--server/controllers/api/video-channel.ts18
-rw-r--r--server/controllers/api/video-playlist.ts9
-rw-r--r--server/controllers/api/videos/blacklist.ts2
-rw-r--r--server/controllers/api/videos/captions.ts2
-rw-r--r--server/controllers/api/videos/comment.ts8
-rw-r--r--server/controllers/api/videos/index.ts18
-rw-r--r--server/controllers/api/videos/live.ts2
-rw-r--r--server/controllers/api/videos/ownership.ts14
-rw-r--r--server/controllers/api/videos/rate.ts4
-rw-r--r--server/controllers/api/videos/update.ts4
-rw-r--r--server/controllers/api/videos/upload.ts18
-rw-r--r--server/controllers/api/videos/watching.ts2
-rw-r--r--server/controllers/bots.ts18
-rw-r--r--server/controllers/client.ts2
-rw-r--r--server/controllers/download.ts3
-rw-r--r--server/controllers/feeds.ts26
-rw-r--r--server/controllers/lazy-static.ts2
-rw-r--r--server/controllers/live.ts2
-rw-r--r--server/controllers/plugins.ts2
-rw-r--r--server/controllers/static.ts14
-rw-r--r--server/helpers/custom-validators/follows.ts20
-rw-r--r--server/helpers/custom-validators/misc.ts15
-rw-r--r--server/helpers/custom-validators/servers.ts1
-rw-r--r--server/helpers/custom-validators/video-ownership.ts2
-rw-r--r--server/helpers/database-utils.ts34
-rw-r--r--server/helpers/express-utils.ts2
-rw-r--r--server/helpers/ffmpeg-utils.ts21
-rw-r--r--server/helpers/logger.ts23
-rw-r--r--server/helpers/query.ts74
-rw-r--r--server/helpers/webtorrent.ts5
-rw-r--r--server/helpers/youtube-dl.ts2
-rw-r--r--server/initializers/constants.ts4
-rw-r--r--server/initializers/migrations/0655-streaming-playlist-filenames.ts66
-rw-r--r--server/lib/activitypub/actors/refresh.ts2
-rw-r--r--server/lib/activitypub/crawl.ts3
-rw-r--r--server/lib/activitypub/follow.ts17
-rw-r--r--server/lib/activitypub/playlists/refresh.ts2
-rw-r--r--server/lib/activitypub/videos/refresh.ts2
-rw-r--r--server/lib/activitypub/videos/shared/abstract-builder.ts12
-rw-r--r--server/lib/activitypub/videos/shared/object-to-model-attributes.ts13
-rw-r--r--server/lib/client-html.ts4
-rw-r--r--server/lib/hls.ts39
-rw-r--r--server/lib/job-queue/handlers/activitypub-cleaner.ts2
-rw-r--r--server/lib/job-queue/handlers/video-file-import.ts7
-rw-r--r--server/lib/job-queue/handlers/video-import.ts4
-rw-r--r--server/lib/job-queue/handlers/video-live-ending.ts15
-rw-r--r--server/lib/job-queue/handlers/video-transcoding.ts3
-rw-r--r--server/lib/live/live-manager.ts32
-rw-r--r--server/lib/live/shared/muxing-session.ts7
-rw-r--r--server/lib/moderation.ts2
-rw-r--r--server/lib/plugins/register-helpers.ts222
-rw-r--r--server/lib/plugins/video-constant-manager-factory.ts139
-rw-r--r--server/lib/schedulers/plugins-check-scheduler.ts10
-rw-r--r--server/lib/schedulers/videos-redundancy-scheduler.ts7
-rw-r--r--server/lib/transcoding/video-transcoding.ts81
-rw-r--r--server/lib/video-paths.ts56
-rw-r--r--server/lib/video.ts4
-rw-r--r--server/middlewares/activitypub.ts2
-rw-r--r--server/middlewares/auth.ts2
-rw-r--r--server/middlewares/cache.ts28
-rw-r--r--server/middlewares/cache/cache.ts32
-rw-r--r--server/middlewares/cache/index.ts1
-rw-r--r--server/middlewares/cache/shared/api-cache.ts269
-rw-r--r--server/middlewares/cache/shared/index.ts1
-rw-r--r--server/middlewares/error.ts2
-rw-r--r--server/middlewares/index.ts1
-rw-r--r--server/middlewares/servers.ts2
-rw-r--r--server/middlewares/user-right.ts2
-rw-r--r--server/middlewares/validators/abuse.ts2
-rw-r--r--server/middlewares/validators/activitypub/activity.ts4
-rw-r--r--server/middlewares/validators/blocklist.ts2
-rw-r--r--server/middlewares/validators/bulk.ts3
-rw-r--r--server/middlewares/validators/feeds.ts3
-rw-r--r--server/middlewares/validators/follows.ts39
-rw-r--r--server/middlewares/validators/oembed.ts2
-rw-r--r--server/middlewares/validators/plugins.ts2
-rw-r--r--server/middlewares/validators/redundancy.ts2
-rw-r--r--server/middlewares/validators/search.ts58
-rw-r--r--server/middlewares/validators/server.ts2
-rw-r--r--server/middlewares/validators/shared/abuses.ts2
-rw-r--r--server/middlewares/validators/shared/accounts.ts2
-rw-r--r--server/middlewares/validators/shared/video-blacklists.ts2
-rw-r--r--server/middlewares/validators/shared/video-captions.ts2
-rw-r--r--server/middlewares/validators/shared/video-channels.ts2
-rw-r--r--server/middlewares/validators/shared/video-comments.ts2
-rw-r--r--server/middlewares/validators/shared/video-imports.ts2
-rw-r--r--server/middlewares/validators/shared/video-ownerships.ts2
-rw-r--r--server/middlewares/validators/shared/video-playlists.ts2
-rw-r--r--server/middlewares/validators/shared/videos.ts3
-rw-r--r--server/middlewares/validators/themes.ts2
-rw-r--r--server/middlewares/validators/user-subscriptions.ts2
-rw-r--r--server/middlewares/validators/users.ts2
-rw-r--r--server/middlewares/validators/videos/video-blacklist.ts2
-rw-r--r--server/middlewares/validators/videos/video-channels.ts2
-rw-r--r--server/middlewares/validators/videos/video-comments.ts2
-rw-r--r--server/middlewares/validators/videos/video-imports.ts2
-rw-r--r--server/middlewares/validators/videos/video-live.ts3
-rw-r--r--server/middlewares/validators/videos/video-ownership-changes.ts10
-rw-r--r--server/middlewares/validators/videos/video-playlists.ts2
-rw-r--r--server/middlewares/validators/videos/video-rates.ts2
-rw-r--r--server/middlewares/validators/videos/video-shares.ts2
-rw-r--r--server/middlewares/validators/videos/video-watch.ts2
-rw-r--r--server/middlewares/validators/videos/videos.ts2
-rw-r--r--server/middlewares/validators/webfinger.ts2
-rw-r--r--server/models/account/account.ts8
-rw-r--r--server/models/actor/actor-follow.ts36
-rw-r--r--server/models/redundancy/video-redundancy.ts4
-rw-r--r--server/models/shared/index.ts2
-rw-r--r--server/models/shared/query.ts17
-rw-r--r--server/models/shared/update.ts18
-rw-r--r--server/models/user/user-notification.ts3
-rw-r--r--server/models/video/formatter/video-format-utils.ts8
-rw-r--r--server/models/video/sql/shared/video-tables.ts3
-rw-r--r--server/models/video/sql/videos-id-list-query-builder.ts35
-rw-r--r--server/models/video/video-channel.ts165
-rw-r--r--server/models/video/video-file.ts45
-rw-r--r--server/models/video/video-playlist.ts64
-rw-r--r--server/models/video/video-streaming-playlist.ts85
-rw-r--r--server/models/video/video.ts131
-rw-r--r--server/tests/api/activitypub/cleaner.ts105
-rw-r--r--server/tests/api/activitypub/client.ts24
-rw-r--r--server/tests/api/activitypub/fetch.ts51
-rw-r--r--server/tests/api/activitypub/helpers.ts5
-rw-r--r--server/tests/api/activitypub/refresher.ts96
-rw-r--r--server/tests/api/activitypub/security.ts46
-rw-r--r--server/tests/api/check-params/abuses.ts205
-rw-r--r--server/tests/api/check-params/accounts.ts19
-rw-r--r--server/tests/api/check-params/blocklist.ts107
-rw-r--r--server/tests/api/check-params/bulk.ts30
-rw-r--r--server/tests/api/check-params/config.ts65
-rw-r--r--server/tests/api/check-params/contact-form.ts84
-rw-r--r--server/tests/api/check-params/custom-pages.ts31
-rw-r--r--server/tests/api/check-params/debug.ts27
-rw-r--r--server/tests/api/check-params/follows.ts112
-rw-r--r--server/tests/api/check-params/jobs.ts33
-rw-r--r--server/tests/api/check-params/live.ts225
-rw-r--r--server/tests/api/check-params/logs.ts35
-rw-r--r--server/tests/api/check-params/plugins.ts95
-rw-r--r--server/tests/api/check-params/redundancy.ts60
-rw-r--r--server/tests/api/check-params/search.ts201
-rw-r--r--server/tests/api/check-params/services.ts49
-rw-r--r--server/tests/api/check-params/upload-quota.ts97
-rw-r--r--server/tests/api/check-params/user-notifications.ts57
-rw-r--r--server/tests/api/check-params/user-subscriptions.ts80
-rw-r--r--server/tests/api/check-params/users.ts425
-rw-r--r--server/tests/api/check-params/video-blacklist.ts140
-rw-r--r--server/tests/api/check-params/video-captions.ts60
-rw-r--r--server/tests/api/check-params/video-channels.ts88
-rw-r--r--server/tests/api/check-params/video-comments.ts107
-rw-r--r--server/tests/api/check-params/video-imports.ts124
-rw-r--r--server/tests/api/check-params/video-playlists.ts394
-rw-r--r--server/tests/api/check-params/videos-filter.ts44
-rw-r--r--server/tests/api/check-params/videos-history.ts43
-rw-r--r--server/tests/api/check-params/videos-overviews.ts13
-rw-r--r--server/tests/api/check-params/videos.ts243
-rw-r--r--server/tests/api/live/live-constraints.ts81
-rw-r--r--server/tests/api/live/live-permanent.ts99
-rw-r--r--server/tests/api/live/live-save-replay.ts126
-rw-r--r--server/tests/api/live/live-socket-messages.ts71
-rw-r--r--server/tests/api/live/live-views.ts51
-rw-r--r--server/tests/api/live/live.ts289
-rw-r--r--server/tests/api/moderation/abuses.ts756
-rw-r--r--server/tests/api/moderation/blocklist-notification.ts121
-rw-r--r--server/tests/api/moderation/blocklist.ts446
-rw-r--r--server/tests/api/moderation/video-blacklist.ts304
-rw-r--r--server/tests/api/notifications/admin-notifications.ts49
-rw-r--r--server/tests/api/notifications/comments-notifications.ts207
-rw-r--r--server/tests/api/notifications/moderation-notifications.ts288
-rw-r--r--server/tests/api/notifications/notifications-api.ts132
-rw-r--r--server/tests/api/notifications/user-notifications.ts197
-rw-r--r--server/tests/api/redundancy/manage-redundancy.ts193
-rw-r--r--server/tests/api/redundancy/redundancy-constraints.ts69
-rw-r--r--server/tests/api/redundancy/redundancy.ts298
-rw-r--r--server/tests/api/search/search-activitypub-video-channels.ts171
-rw-r--r--server/tests/api/search/search-activitypub-video-playlists.ts150
-rw-r--r--server/tests/api/search/search-activitypub-videos.ts124
-rw-r--r--server/tests/api/search/search-channels.ts116
-rw-r--r--server/tests/api/search/search-index.ts422
-rw-r--r--server/tests/api/search/search-playlists.ts159
-rw-r--r--server/tests/api/search/search-videos.ts393
-rw-r--r--server/tests/api/server/auto-follows.ts71
-rw-r--r--server/tests/api/server/bulk.ts123
-rw-r--r--server/tests/api/server/config.ts86
-rw-r--r--server/tests/api/server/contact-form.ts37
-rw-r--r--server/tests/api/server/email.ts116
-rw-r--r--server/tests/api/server/follow-constraints.ts178
-rw-r--r--server/tests/api/server/follows-moderation.ts109
-rw-r--r--server/tests/api/server/follows.ts689
-rw-r--r--server/tests/api/server/handle-down.ts218
-rw-r--r--server/tests/api/server/homepage.ts45
-rw-r--r--server/tests/api/server/jobs.ts61
-rw-r--r--server/tests/api/server/logs.ts88
-rw-r--r--server/tests/api/server/no-client.ts9
-rw-r--r--server/tests/api/server/plugins.ts252
-rw-r--r--server/tests/api/server/reverse-proxy.ts68
-rw-r--r--server/tests/api/server/services.ts56
-rw-r--r--server/tests/api/server/stats.ts170
-rw-r--r--server/tests/api/server/tracker.ts31
-rw-r--r--server/tests/api/users/user-subscriptions.ts230
-rw-r--r--server/tests/api/users/users-multiple-servers.ts148
-rw-r--r--server/tests/api/users/users-verification.ts107
-rw-r--r--server/tests/api/users/users.ts657
-rw-r--r--server/tests/api/videos/audio-only.ts37
-rw-r--r--server/tests/api/videos/multiple-servers.ts472
-rw-r--r--server/tests/api/videos/resumable-upload.ts70
-rw-r--r--server/tests/api/videos/single-server.ts287
-rw-r--r--server/tests/api/videos/video-captions.ts96
-rw-r--r--server/tests/api/videos/video-change-ownership.ts320
-rw-r--r--server/tests/api/videos/video-channels.ts352
-rw-r--r--server/tests/api/videos/video-comments.ts225
-rw-r--r--server/tests/api/videos/video-description.ts47
-rw-r--r--server/tests/api/videos/video-hls.ts121
-rw-r--r--server/tests/api/videos/video-imports.ts172
-rw-r--r--server/tests/api/videos/video-nsfw.ts226
-rw-r--r--server/tests/api/videos/video-playlist-thumbnails.ts144
-rw-r--r--server/tests/api/videos/video-playlists.ts727
-rw-r--r--server/tests/api/videos/video-privacy.ts157
-rw-r--r--server/tests/api/videos/video-schedule-update.ts77
-rw-r--r--server/tests/api/videos/video-transcoder.ts373
-rw-r--r--server/tests/api/videos/videos-filter.ts49
-rw-r--r--server/tests/api/videos/videos-history.ts139
-rw-r--r--server/tests/api/videos/videos-overview.ts97
-rw-r--r--server/tests/api/videos/videos-views-cleaner.ts47
-rw-r--r--server/tests/cli/create-import-video-file-job.ts70
-rw-r--r--server/tests/cli/create-transcoding-job.ts110
-rw-r--r--server/tests/cli/optimize-old-videos.ts65
-rw-r--r--server/tests/cli/peertube.ts163
-rw-r--r--server/tests/cli/plugins.ts42
-rw-r--r--server/tests/cli/print-transcode-command.ts7
-rw-r--r--server/tests/cli/prune-storage.ts124
-rw-r--r--server/tests/cli/regenerate-thumbnails.ts52
-rw-r--r--server/tests/cli/reset-password.ts25
-rw-r--r--server/tests/cli/update-host.ts90
-rw-r--r--server/tests/client.ts200
-rw-r--r--server/tests/external-plugins/auth-ldap.ts69
-rw-r--r--server/tests/external-plugins/auto-block-videos.ts75
-rw-r--r--server/tests/external-plugins/auto-mute.ts98
-rw-r--r--server/tests/feeds/feeds.ts275
-rw-r--r--server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js7
-rw-r--r--server/tests/fixtures/peertube-plugin-test-video-constants/main.js46
-rw-r--r--server/tests/fixtures/peertube-plugin-test/main.js9
-rw-r--r--server/tests/fixtures/video_very_short_240p.mp4bin0 -> 9352 bytes
-rw-r--r--server/tests/helpers/comment-model.ts2
-rw-r--r--server/tests/helpers/core-utils.ts4
-rw-r--r--server/tests/helpers/image.ts2
-rw-r--r--server/tests/helpers/request.ts10
-rw-r--r--server/tests/index.ts1
-rw-r--r--server/tests/lib/index.ts1
-rw-r--r--server/tests/lib/video-constant-registry-factory.ts155
-rw-r--r--server/tests/misc-endpoints.ts56
-rw-r--r--server/tests/plugins/action-hooks.ts111
-rw-r--r--server/tests/plugins/external-auth.ts184
-rw-r--r--server/tests/plugins/filter-hooks.ts408
-rw-r--r--server/tests/plugins/html-injection.ts41
-rw-r--r--server/tests/plugins/id-and-pass-auth.ts141
-rw-r--r--server/tests/plugins/plugin-helpers.ts114
-rw-r--r--server/tests/plugins/plugin-router.ts43
-rw-r--r--server/tests/plugins/plugin-storage.ts45
-rw-r--r--server/tests/plugins/plugin-transcoding.ts259
-rw-r--r--server/tests/plugins/plugin-unloading.ts45
-rw-r--r--server/tests/plugins/translations.ts51
-rw-r--r--server/tests/plugins/video-constants.ts118
-rw-r--r--server/tools/cli.ts44
-rw-r--r--server/tools/peertube-auth.ts6
-rw-r--r--server/tools/peertube-get-access-token.ts23
-rw-r--r--server/tools/peertube-import-videos.ts110
-rw-r--r--server/tools/peertube-plugins.ts49
-rw-r--r--server/tools/peertube-redundancy.ts60
-rw-r--r--server/tools/peertube-upload.ts17
-rw-r--r--server/tools/test-live.ts (renamed from server/tools/test.ts)61
-rw-r--r--server/types/models/video/video-streaming-playlist.ts2
-rw-r--r--server/typings/express/index.d.ts14
-rw-r--r--shared/core-utils/common/date.ts (renamed from shared/core-utils/miscs/date.ts)47
-rw-r--r--shared/core-utils/common/index.ts6
-rw-r--r--shared/core-utils/common/miscs.ts (renamed from shared/core-utils/miscs/miscs.ts)10
-rw-r--r--shared/core-utils/common/promises.ts12
-rw-r--r--shared/core-utils/common/regexp.ts5
-rw-r--r--shared/core-utils/common/types.ts (renamed from shared/core-utils/miscs/types.ts)0
-rw-r--r--shared/core-utils/common/url.ts130
-rw-r--r--shared/core-utils/index.ts4
-rw-r--r--shared/core-utils/logs/index.ts1
-rw-r--r--shared/core-utils/logs/logs.ts25
-rw-r--r--shared/core-utils/miscs/index.ts5
-rw-r--r--shared/core-utils/plugins/hooks.ts2
-rw-r--r--shared/core-utils/utils/index.ts1
-rw-r--r--shared/core-utils/utils/object.ts15
-rw-r--r--shared/extra-utils/bulk/bulk-command.ts20
-rw-r--r--shared/extra-utils/bulk/bulk.ts25
-rw-r--r--shared/extra-utils/bulk/index.ts1
-rw-r--r--shared/extra-utils/cli/cli-command.ts23
-rw-r--r--shared/extra-utils/cli/cli.ts24
-rw-r--r--shared/extra-utils/cli/index.ts1
-rw-r--r--shared/extra-utils/custom-pages/custom-pages-command.ts33
-rw-r--r--shared/extra-utils/custom-pages/custom-pages.ts31
-rw-r--r--shared/extra-utils/custom-pages/index.ts1
-rw-r--r--shared/extra-utils/feeds/feeds-command.ts44
-rw-r--r--shared/extra-utils/feeds/feeds.ts33
-rw-r--r--shared/extra-utils/feeds/index.ts1
-rw-r--r--shared/extra-utils/index.ts66
-rw-r--r--shared/extra-utils/logs/index.ts1
-rw-r--r--shared/extra-utils/logs/logs-command.ts43
-rw-r--r--shared/extra-utils/logs/logs.ts32
-rw-r--r--shared/extra-utils/miscs/checks.ts46
-rw-r--r--shared/extra-utils/miscs/generate.ts61
-rw-r--r--shared/extra-utils/miscs/index.ts5
-rw-r--r--shared/extra-utils/miscs/miscs.ts170
-rw-r--r--shared/extra-utils/miscs/sql-command.ts142
-rw-r--r--shared/extra-utils/miscs/sql.ts161
-rw-r--r--shared/extra-utils/miscs/stubs.ts14
-rw-r--r--shared/extra-utils/miscs/tests.ts94
-rw-r--r--shared/extra-utils/miscs/webtorrent.ts31
-rw-r--r--shared/extra-utils/mock-servers/index.ts4
-rw-r--r--shared/extra-utils/mock-servers/mock-email.ts (renamed from shared/extra-utils/miscs/email.ts)4
-rw-r--r--shared/extra-utils/mock-servers/mock-joinpeertube-versions.ts (renamed from shared/extra-utils/mock-servers/joinpeertube-versions.ts)0
-rw-r--r--shared/extra-utils/mock-servers/mock-plugin-blocklist.ts (renamed from shared/extra-utils/plugins/mock-blocklist.ts)0
-rw-r--r--shared/extra-utils/moderation/abuses-command.ts228
-rw-r--r--shared/extra-utils/moderation/abuses.ts244
-rw-r--r--shared/extra-utils/moderation/index.ts1
-rw-r--r--shared/extra-utils/overviews/index.ts1
-rw-r--r--shared/extra-utils/overviews/overviews-command.ts23
-rw-r--r--shared/extra-utils/overviews/overviews.ts34
-rw-r--r--shared/extra-utils/requests/activitypub.ts2
-rw-r--r--shared/extra-utils/requests/check-api-params.ts19
-rw-r--r--shared/extra-utils/requests/index.ts3
-rw-r--r--shared/extra-utils/requests/requests.ts238
-rw-r--r--shared/extra-utils/search/index.ts1
-rw-r--r--shared/extra-utils/search/search-command.ts98
-rw-r--r--shared/extra-utils/search/video-channels.ts36
-rw-r--r--shared/extra-utils/search/video-playlists.ts36
-rw-r--r--shared/extra-utils/search/videos.ts64
-rw-r--r--shared/extra-utils/server/activitypub.ts15
-rw-r--r--shared/extra-utils/server/clients.ts20
-rw-r--r--shared/extra-utils/server/config-command.ts263
-rw-r--r--shared/extra-utils/server/config.ts260
-rw-r--r--shared/extra-utils/server/contact-form-command.ts31
-rw-r--r--shared/extra-utils/server/contact-form.ts31
-rw-r--r--shared/extra-utils/server/debug-command.ts33
-rw-r--r--shared/extra-utils/server/debug.ts33
-rw-r--r--shared/extra-utils/server/directories.ts34
-rw-r--r--shared/extra-utils/server/follows-command.ts139
-rw-r--r--shared/extra-utils/server/follows.ts133
-rw-r--r--shared/extra-utils/server/index.ts15
-rw-r--r--shared/extra-utils/server/jobs-command.ts36
-rw-r--r--shared/extra-utils/server/jobs.ts82
-rw-r--r--shared/extra-utils/server/plugins-command.ts256
-rw-r--r--shared/extra-utils/server/plugins.ts299
-rw-r--r--shared/extra-utils/server/redundancy-command.ts80
-rw-r--r--shared/extra-utils/server/redundancy.ts88
-rw-r--r--shared/extra-utils/server/server.ts369
-rw-r--r--shared/extra-utils/server/servers-command.ts88
-rw-r--r--shared/extra-utils/server/servers.ts375
-rw-r--r--shared/extra-utils/server/stats-command.ts25
-rw-r--r--shared/extra-utils/server/stats.ts23
-rw-r--r--shared/extra-utils/shared/abstract-command.ts199
-rw-r--r--shared/extra-utils/shared/index.ts1
-rw-r--r--shared/extra-utils/socket/index.ts1
-rw-r--r--shared/extra-utils/socket/socket-io-command.ts15
-rw-r--r--shared/extra-utils/socket/socket-io.ts18
-rw-r--r--shared/extra-utils/users/accounts-command.ts56
-rw-r--r--shared/extra-utils/users/accounts.ts87
-rw-r--r--shared/extra-utils/users/actors.ts73
-rw-r--r--shared/extra-utils/users/blocklist-command.ts139
-rw-r--r--shared/extra-utils/users/blocklist.ts238
-rw-r--r--shared/extra-utils/users/index.ts9
-rw-r--r--shared/extra-utils/users/login-command.ts132
-rw-r--r--shared/extra-utils/users/login.ts124
-rw-r--r--shared/extra-utils/users/notifications-command.ts86
-rw-r--r--shared/extra-utils/users/notifications.ts (renamed from shared/extra-utils/users/user-notifications.ts)628
-rw-r--r--shared/extra-utils/users/subscriptions-command.ts99
-rw-r--r--shared/extra-utils/users/user-subscriptions.ts93
-rw-r--r--shared/extra-utils/users/users-command.ts415
-rw-r--r--shared/extra-utils/users/users.ts415
-rw-r--r--shared/extra-utils/videos/blacklist-command.ts76
-rw-r--r--shared/extra-utils/videos/captions-command.ts65
-rw-r--r--shared/extra-utils/videos/captions.ts17
-rw-r--r--shared/extra-utils/videos/change-ownership-command.ts68
-rw-r--r--shared/extra-utils/videos/channels-command.ts156
-rw-r--r--shared/extra-utils/videos/channels.ts18
-rw-r--r--shared/extra-utils/videos/comments-command.ts152
-rw-r--r--shared/extra-utils/videos/history-command.ts58
-rw-r--r--shared/extra-utils/videos/imports-command.ts47
-rw-r--r--shared/extra-utils/videos/index.ts19
-rw-r--r--shared/extra-utils/videos/live-command.ts154
-rw-r--r--shared/extra-utils/videos/live.ts159
-rw-r--r--shared/extra-utils/videos/playlists-command.ts280
-rw-r--r--shared/extra-utils/videos/playlists.ts25
-rw-r--r--shared/extra-utils/videos/services-command.ts29
-rw-r--r--shared/extra-utils/videos/services.ts24
-rw-r--r--shared/extra-utils/videos/streaming-playlists-command.ts44
-rw-r--r--shared/extra-utils/videos/streaming-playlists.ts78
-rw-r--r--shared/extra-utils/videos/video-blacklist.ts79
-rw-r--r--shared/extra-utils/videos/video-captions.ts72
-rw-r--r--shared/extra-utils/videos/video-change-ownership.ts72
-rw-r--r--shared/extra-utils/videos/video-channels.ts192
-rw-r--r--shared/extra-utils/videos/video-comments.ts138
-rw-r--r--shared/extra-utils/videos/video-history.ts49
-rw-r--r--shared/extra-utils/videos/video-imports.ts90
-rw-r--r--shared/extra-utils/videos/video-playlists.ts320
-rw-r--r--shared/extra-utils/videos/video-streaming-playlists.ts82
-rw-r--r--shared/extra-utils/videos/videos-command.ts599
-rw-r--r--shared/extra-utils/videos/videos.ts816
-rw-r--r--shared/models/http/http-error-codes.ts (renamed from shared/core-utils/miscs/http-error-codes.ts)0
-rw-r--r--shared/models/http/http-methods.ts (renamed from shared/core-utils/miscs/http-methods.ts)0
-rw-r--r--shared/models/http/index.ts2
-rw-r--r--shared/models/index.ts1
-rw-r--r--shared/models/plugins/server/managers/plugin-playlist-privacy-manager.model.ts12
-rw-r--r--shared/models/plugins/server/managers/plugin-video-category-manager.model.ts10
-rw-r--r--shared/models/plugins/server/managers/plugin-video-language-manager.model.ts10
-rw-r--r--shared/models/plugins/server/managers/plugin-video-licence-manager.model.ts10
-rw-r--r--shared/models/plugins/server/managers/plugin-video-privacy-manager.model.ts14
-rw-r--r--shared/models/plugins/server/plugin-constant-manager.model.ts7
-rw-r--r--shared/models/plugins/server/server-hook.model.ts4
-rw-r--r--shared/models/search/video-channels-search-query.model.ts11
-rw-r--r--shared/models/search/video-playlists-search-query.model.ts13
-rw-r--r--shared/models/search/videos-common-query.model.ts8
-rw-r--r--shared/models/search/videos-search-query.model.ts11
-rw-r--r--shared/models/server/debug.model.ts1
-rw-r--r--shared/models/server/index.ts1
-rw-r--r--shared/models/server/peertube-problem-document.model.ts2
-rw-r--r--shared/models/server/server-follow-create.model.ts4
-rw-r--r--shared/models/users/index.ts1
-rw-r--r--shared/models/users/user-create-result.model.ts7
-rw-r--r--shared/models/users/user-notification.model.ts7
-rw-r--r--shared/models/videos/channel/index.ts1
-rw-r--r--shared/models/videos/channel/video-channel-create-result.model.ts3
-rw-r--r--shared/models/videos/comment/index.ts1
-rw-r--r--shared/models/videos/comment/video-comment-create.model.ts3
-rw-r--r--shared/models/videos/comment/video-comment.model.ts7
-rw-r--r--shared/models/videos/playlist/index.ts1
-rw-r--r--shared/models/videos/playlist/video-playlist-element-create-result.model.ts3
-rw-r--r--shared/models/videos/video-update.model.ts1
-rw-r--r--support/doc/api/openapi.yaml18
-rw-r--r--support/doc/dependencies.md45
-rw-r--r--support/doc/plugins/guide.md28
-rw-r--r--tsconfig.json1
-rw-r--r--yarn.lock901
590 files changed, 31406 insertions, 29286 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 46b243244..c5bbd9e2c 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -2,11 +2,6 @@ name: Test Suite
2 2
3on: 3on:
4 push: 4 push:
5 branches:
6 - develop
7 - master
8 - ci
9 - next
10 pull_request: 5 pull_request:
11 types: [synchronize, opened] 6 types: [synchronize, opened]
12 schedule: 7 schedule:
diff --git a/client/src/app/+about/about-instance/contact-admin-modal.component.ts b/client/src/app/+about/about-instance/contact-admin-modal.component.ts
index a528faa20..37e9feacb 100644
--- a/client/src/app/+about/about-instance/contact-admin-modal.component.ts
+++ b/client/src/app/+about/about-instance/contact-admin-modal.component.ts
@@ -11,8 +11,7 @@ import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
11import { InstanceService } from '@app/shared/shared-instance' 11import { InstanceService } from '@app/shared/shared-instance'
12import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 12import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
13import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 13import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
14import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 14import { HTMLServerConfig, HttpStatusCode } from '@shared/models'
15import { HTMLServerConfig } from '@shared/models'
16 15
17type Prefill = { 16type Prefill = {
18 subject?: string 17 subject?: string
diff --git a/client/src/app/+accounts/accounts.component.ts b/client/src/app/+accounts/accounts.component.ts
index c69b04a01..5b59f3cd0 100644
--- a/client/src/app/+accounts/accounts.component.ts
+++ b/client/src/app/+accounts/accounts.component.ts
@@ -13,8 +13,7 @@ import {
13 VideoService 13 VideoService
14} from '@app/shared/shared-main' 14} from '@app/shared/shared-main'
15import { AccountReportComponent } from '@app/shared/shared-moderation' 15import { AccountReportComponent } from '@app/shared/shared-moderation'
16import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 16import { HttpStatusCode, User, UserRight } from '@shared/models'
17import { User, UserRight } from '@shared/models'
18import { AccountSearchComponent } from './account-search/account-search.component' 17import { AccountSearchComponent } from './account-search/account-search.component'
19 18
20@Component({ 19@Component({
diff --git a/client/src/app/+admin/admin.component.ts b/client/src/app/+admin/admin.component.ts
index dd92ed2ca..4b6fab6ed 100644
--- a/client/src/app/+admin/admin.component.ts
+++ b/client/src/app/+admin/admin.component.ts
@@ -26,12 +26,12 @@ export class AdminComponent implements OnInit {
26 label: $localize`Federation`, 26 label: $localize`Federation`,
27 children: [ 27 children: [
28 { 28 {
29 label: $localize`Instances you follow`, 29 label: $localize`Following`,
30 routerLink: '/admin/follows/following-list', 30 routerLink: '/admin/follows/following-list',
31 iconName: 'following' 31 iconName: 'following'
32 }, 32 },
33 { 33 {
34 label: $localize`Instances following you`, 34 label: $localize`Followers`,
35 routerLink: '/admin/follows/followers-list', 35 routerLink: '/admin/follows/followers-list',
36 iconName: 'follower' 36 iconName: 'follower'
37 }, 37 },
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts
index a7fe20b07..1ea7b9784 100644
--- a/client/src/app/+admin/admin.module.ts
+++ b/client/src/app/+admin/admin.module.ts
@@ -25,7 +25,7 @@ import {
25 EditVODTranscodingComponent 25 EditVODTranscodingComponent
26} from './config' 26} from './config'
27import { ConfigService } from './config/shared/config.service' 27import { ConfigService } from './config/shared/config.service'
28import { FollowersListComponent, FollowsComponent, VideoRedundanciesListComponent } from './follows' 28import { FollowersListComponent, FollowModalComponent, FollowsComponent, VideoRedundanciesListComponent } from './follows'
29import { FollowingListComponent } from './follows/following-list/following-list.component' 29import { FollowingListComponent } from './follows/following-list/following-list.component'
30import { RedundancyCheckboxComponent } from './follows/shared/redundancy-checkbox.component' 30import { RedundancyCheckboxComponent } from './follows/shared/redundancy-checkbox.component'
31import { VideoRedundancyInformationComponent } from './follows/video-redundancies-list/video-redundancy-information.component' 31import { VideoRedundancyInformationComponent } from './follows/video-redundancies-list/video-redundancy-information.component'
@@ -68,6 +68,7 @@ import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersCom
68 FollowsComponent, 68 FollowsComponent,
69 FollowersListComponent, 69 FollowersListComponent,
70 FollowingListComponent, 70 FollowingListComponent,
71 FollowModalComponent,
71 RedundancyCheckboxComponent, 72 RedundancyCheckboxComponent,
72 VideoRedundanciesListComponent, 73 VideoRedundanciesListComponent,
73 VideoRedundancyInformationComponent, 74 VideoRedundancyInformationComponent,
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.html b/client/src/app/+admin/follows/followers-list/followers-list.component.html
index c2e9a4df6..08459634d 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.html
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.html
@@ -1,6 +1,6 @@
1<h1> 1<h1>
2 <my-global-icon iconName="follower" aria-hidden="true"></my-global-icon> 2 <my-global-icon iconName="follower" aria-hidden="true"></my-global-icon>
3 <ng-container i18n>Instances following you</ng-container> 3 <ng-container i18n>Followers of your instance</ng-container>
4</h1> 4</h1>
5 5
6<p-table 6<p-table
@@ -21,7 +21,7 @@
21 <ng-template pTemplate="header"> 21 <ng-template pTemplate="header">
22 <tr> 22 <tr>
23 <th style="width: 150px;" i18n>Actions</th> 23 <th style="width: 150px;" i18n>Actions</th>
24 <th i18n>Follower handle</th> 24 <th i18n>Follower</th>
25 <th style="width: 100px;" i18n pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th> 25 <th style="width: 100px;" i18n pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th>
26 <th style="width: 100px;" i18n pSortableColumn="score">Score <p-sortIcon field="score"></p-sortIcon></th> 26 <th style="width: 100px;" i18n pSortableColumn="score">Score <p-sortIcon field="score"></p-sortIcon></th>
27 <th style="width: 150px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> 27 <th style="width: 150px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
diff --git a/client/src/app/+admin/follows/following-list/follow-modal.component.html b/client/src/app/+admin/follows/following-list/follow-modal.component.html
new file mode 100644
index 000000000..d0761b718
--- /dev/null
+++ b/client/src/app/+admin/follows/following-list/follow-modal.component.html
@@ -0,0 +1,42 @@
1<ng-template #modal>
2 <div class="modal-header">
3 <h4 i18n class="modal-title">Follow</h4>
4
5 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
6 </div>
7
8 <div class="modal-body">
9 <form novalidate [formGroup]="form" (ngSubmit)="submit()">
10 <div class="form-group">
11 <label i18n for="hostsOrHandles">1 host (without "http://"), account handle or channel handle per line</label>
12
13 <textarea
14 [placeholder]="placeholder" formControlName="hostsOrHandles" type="text" id="hostsOrHandles" name="hostsOrHandles"
15 class="form-control" [ngClass]="{ 'input-error': formErrors['hostsOrHandles'] }" ngbAutofocus
16 ></textarea>
17
18 <div *ngIf="formErrors.hostsOrHandles" class="form-error">
19 {{ formErrors.hostsOrHandles }}
20
21 <div *ngIf="form.controls['hostsOrHandles'].errors.validHostsOrHandles">
22 {{ form.controls['hostsOrHandles'].errors.validHostsOrHandles.value }}
23 </div>
24 </div>
25 </div>
26
27 <div i18n *ngIf="httpEnabled() === false" class="alert alert-warning">
28 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
29 </div>
30
31 <div class="form-group inputs">
32 <input
33 type="button" role="button" i18n-value value="Cancel" class="peertube-button grey-button"
34 (click)="hide()" (key.enter)="hide()"
35 >
36
37 <input type="submit" i18n-value value="Follow" class="peertube-button orange-button" [disabled]="!form.valid" />
38 </div>
39 </form>
40 </div>
41
42</ng-template>
diff --git a/client/src/app/+admin/follows/following-list/follow-modal.component.scss b/client/src/app/+admin/follows/following-list/follow-modal.component.scss
new file mode 100644
index 000000000..9621a566f
--- /dev/null
+++ b/client/src/app/+admin/follows/following-list/follow-modal.component.scss
@@ -0,0 +1,3 @@
1textarea {
2 height: 200px;
3}
diff --git a/client/src/app/+admin/follows/following-list/follow-modal.component.ts b/client/src/app/+admin/follows/following-list/follow-modal.component.ts
new file mode 100644
index 000000000..dc6909200
--- /dev/null
+++ b/client/src/app/+admin/follows/following-list/follow-modal.component.ts
@@ -0,0 +1,69 @@
1import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
2import { Notifier } from '@app/core'
3import { splitAndGetNotEmpty, UNIQUE_HOSTS_OR_HANDLE_VALIDATOR } from '@app/shared/form-validators/host-validators'
4import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
5import { InstanceFollowService } from '@app/shared/shared-instance'
6import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
7import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
8
9@Component({
10 selector: 'my-follow-modal',
11 templateUrl: './follow-modal.component.html',
12 styleUrls: [ './follow-modal.component.scss' ]
13})
14export class FollowModalComponent extends FormReactive implements OnInit {
15 @ViewChild('modal', { static: true }) modal: NgbModal
16
17 @Output() newFollow = new EventEmitter<void>()
18
19 placeholder = 'example.com\nchocobozzz@example.com\nchocobozzz_channel@example.com'
20
21 private openedModal: NgbModalRef
22
23 constructor (
24 protected formValidatorService: FormValidatorService,
25 private modalService: NgbModal,
26 private followService: InstanceFollowService,
27 private notifier: Notifier
28 ) {
29 super()
30 }
31
32 ngOnInit () {
33 this.buildForm({
34 hostsOrHandles: UNIQUE_HOSTS_OR_HANDLE_VALIDATOR
35 })
36 }
37
38 openModal () {
39 this.openedModal = this.modalService.open(this.modal, { centered: true })
40 }
41
42 hide () {
43 this.openedModal.close()
44 }
45
46 submit () {
47 this.addFollowing()
48
49 this.form.reset()
50 this.hide()
51 }
52
53 httpEnabled () {
54 return window.location.protocol === 'https:'
55 }
56
57 private async addFollowing () {
58 const hostsOrHandles = splitAndGetNotEmpty(this.form.value['hostsOrHandles'])
59
60 this.followService.follow(hostsOrHandles).subscribe(
61 () => {
62 this.notifier.success($localize`Follow request(s) sent!`)
63 this.newFollow.emit()
64 },
65
66 err => this.notifier.error(err.message)
67 )
68 }
69}
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.html b/client/src/app/+admin/follows/following-list/following-list.component.html
index e7c0c9088..75b0efca8 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.html
+++ b/client/src/app/+admin/follows/following-list/following-list.component.html
@@ -1,6 +1,6 @@
1<h1> 1<h1>
2 <my-global-icon iconName="following" aria-hidden="true"></my-global-icon> 2 <my-global-icon iconName="following" aria-hidden="true"></my-global-icon>
3 <ng-container i18n>Instances you follow</ng-container> 3 <ng-container i18n>Your instance subscriptions</ng-container>
4</h1> 4</h1>
5 5
6<p-table 6<p-table
@@ -13,9 +13,9 @@
13 <ng-template pTemplate="caption"> 13 <ng-template pTemplate="caption">
14 <div class="caption"> 14 <div class="caption">
15 <div class="left-buttons"> 15 <div class="left-buttons">
16 <a class="follow-button" (click)="addDomainsToFollow()" (key.enter)="addDomainsToFollow()"> 16 <a class="follow-button" (click)="openFollowModal()" (key.enter)="openFollowModal()">
17 <my-global-icon iconName="following" aria-hidden="true"></my-global-icon> 17 <my-global-icon iconName="following" aria-hidden="true"></my-global-icon>
18 <ng-container i18n>Follow instances</ng-container> 18 <ng-container i18n>Follow</ng-container>
19 </a> 19 </a>
20 </div> 20 </div>
21 21
@@ -28,7 +28,7 @@
28 <ng-template pTemplate="header"> 28 <ng-template pTemplate="header">
29 <tr> 29 <tr>
30 <th style="width: 150px;" i18n>Action</th> 30 <th style="width: 150px;" i18n>Action</th>
31 <th i18n>Host</th> 31 <th i18n>Following</th>
32 <th style="width: 100px;" i18n pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th> 32 <th style="width: 100px;" i18n pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th>
33 <th style="width: 150px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> 33 <th style="width: 150px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
34 <th style="width: 160px;" i18n pSortableColumn="redundancyAllowed">Redundancy allowed <p-sortIcon field="redundancyAllowed"></p-sortIcon></th> 34 <th style="width: 160px;" i18n pSortableColumn="redundancyAllowed">Redundancy allowed <p-sortIcon field="redundancyAllowed"></p-sortIcon></th>
@@ -41,8 +41,8 @@
41 <my-delete-button label="Unfollow" i18n-label (click)="removeFollowing(follow)"></my-delete-button> 41 <my-delete-button label="Unfollow" i18n-label (click)="removeFollowing(follow)"></my-delete-button>
42 </td> 42 </td>
43 <td> 43 <td>
44 <a [href]="'https://' + follow.following.host" i18n-title title="Open instance in a new tab" target="_blank" rel="noopener noreferrer"> 44 <a [href]="follow.following.url" i18n-title title="Open instance in a new tab" target="_blank" rel="noopener noreferrer">
45 {{ follow.following.host }} 45 {{ follow.following.name + '@' + follow.following.host }}
46 <span class="glyphicon glyphicon-new-window"></span> 46 <span class="glyphicon glyphicon-new-window"></span>
47 </a> 47 </a>
48 </td> 48 </td>
@@ -57,6 +57,7 @@
57 <td>{{ follow.createdAt | date: 'short' }}</td> 57 <td>{{ follow.createdAt | date: 'short' }}</td>
58 <td> 58 <td>
59 <my-redundancy-checkbox 59 <my-redundancy-checkbox
60 *ngIf="isInstanceFollowing(follow)"
60 [host]="follow.following.host" [redundancyAllowed]="follow.following.hostRedundancyAllowed" 61 [host]="follow.following.host" [redundancyAllowed]="follow.following.hostRedundancyAllowed"
61 ></my-redundancy-checkbox> 62 ></my-redundancy-checkbox>
62 </td> 63 </td>
@@ -75,10 +76,4 @@
75 </ng-template> 76 </ng-template>
76</p-table> 77</p-table>
77 78
78<my-batch-domains-modal #batchDomainsModal i18n-action action="Follow domains" (domains)="addFollowing($event)"> 79<my-follow-modal #followModal></my-follow-modal>
79 <ng-container ngProjectAs="warning">
80 <div i18n *ngIf="httpEnabled() === false" class="alert alert-warning">
81 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
82 </div>
83 </ng-container>
84</my-batch-domains-modal>
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts
index b63fe08c0..ba62dfa23 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.ts
+++ b/client/src/app/+admin/follows/following-list/following-list.component.ts
@@ -4,13 +4,14 @@ import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
4import { InstanceFollowService } from '@app/shared/shared-instance' 4import { InstanceFollowService } from '@app/shared/shared-instance'
5import { BatchDomainsModalComponent } from '@app/shared/shared-moderation' 5import { BatchDomainsModalComponent } from '@app/shared/shared-moderation'
6import { ActorFollow } from '@shared/models' 6import { ActorFollow } from '@shared/models'
7import { FollowModalComponent } from './follow-modal.component'
7 8
8@Component({ 9@Component({
9 templateUrl: './following-list.component.html', 10 templateUrl: './following-list.component.html',
10 styleUrls: [ '../follows.component.scss', './following-list.component.scss' ] 11 styleUrls: [ '../follows.component.scss', './following-list.component.scss' ]
11}) 12})
12export class FollowingListComponent extends RestTable implements OnInit { 13export class FollowingListComponent extends RestTable implements OnInit {
13 @ViewChild('batchDomainsModal') batchDomainsModal: BatchDomainsModalComponent 14 @ViewChild('followModal') followModal: FollowModalComponent
14 15
15 following: ActorFollow[] = [] 16 following: ActorFollow[] = []
16 totalRecords = 0 17 totalRecords = 0
@@ -33,23 +34,12 @@ export class FollowingListComponent extends RestTable implements OnInit {
33 return 'FollowingListComponent' 34 return 'FollowingListComponent'
34 } 35 }
35 36
36 addDomainsToFollow () { 37 openFollowModal () {
37 this.batchDomainsModal.openModal() 38 this.followModal.openModal()
38 } 39 }
39 40
40 httpEnabled () { 41 isInstanceFollowing (follow: ActorFollow) {
41 return window.location.protocol === 'https:' 42 return follow.following.name === 'peertube'
42 }
43
44 async addFollowing (hosts: string[]) {
45 this.followService.follow(hosts).subscribe(
46 () => {
47 this.notifier.success($localize`Follow request(s) sent!`)
48 this.reloadData()
49 },
50
51 err => this.notifier.error(err.message)
52 )
53 } 43 }
54 44
55 async removeFollowing (follow: ActorFollow) { 45 async removeFollowing (follow: ActorFollow) {
diff --git a/client/src/app/+admin/follows/following-list/index.ts b/client/src/app/+admin/follows/following-list/index.ts
index a70d46a7e..88be0ed4c 100644
--- a/client/src/app/+admin/follows/following-list/index.ts
+++ b/client/src/app/+admin/follows/following-list/index.ts
@@ -1 +1,2 @@
1export * from './follow-modal.component'
1export * from './following-list.component' 2export * from './following-list.component'
diff --git a/client/src/app/+admin/follows/follows.routes.ts b/client/src/app/+admin/follows/follows.routes.ts
index cd70daf77..3843b42b5 100644
--- a/client/src/app/+admin/follows/follows.routes.ts
+++ b/client/src/app/+admin/follows/follows.routes.ts
@@ -25,7 +25,7 @@ export const FollowsRoutes: Routes = [
25 component: FollowingListComponent, 25 component: FollowingListComponent,
26 data: { 26 data: {
27 meta: { 27 meta: {
28 title: $localize`Following list` 28 title: $localize`Following`
29 } 29 }
30 } 30 }
31 }, 31 },
@@ -34,7 +34,7 @@ export const FollowsRoutes: Routes = [
34 component: FollowersListComponent, 34 component: FollowersListComponent,
35 data: { 35 data: {
36 meta: { 36 meta: {
37 title: $localize`Followers list` 37 title: $localize`Followers`
38 } 38 }
39 } 39 }
40 }, 40 },
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
index 08500ef5c..4fe5ec441 100644
--- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
@@ -1,6 +1,6 @@
1import { SortMeta } from 'primeng/api' 1import { SortMeta } from 'primeng/api'
2import { switchMap } from 'rxjs/operators' 2import { switchMap } from 'rxjs/operators'
3import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' 3import { buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
4import { environment } from 'src/environments/environment' 4import { environment } from 'src/environments/environment'
5import { Component, OnInit } from '@angular/core' 5import { Component, OnInit } from '@angular/core'
6import { DomSanitizer } from '@angular/platform-browser' 6import { DomSanitizer } from '@angular/platform-browser'
@@ -9,6 +9,7 @@ import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, S
9import { AdvancedInputFilter } from '@app/shared/shared-forms' 9import { AdvancedInputFilter } from '@app/shared/shared-forms'
10import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' 10import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
11import { VideoBlockService } from '@app/shared/shared-moderation' 11import { VideoBlockService } from '@app/shared/shared-moderation'
12import { buildVideoEmbedLink, decorateVideoLink } from '@shared/core-utils'
12import { VideoBlacklist, VideoBlacklistType } from '@shared/models' 13import { VideoBlacklist, VideoBlacklistType } from '@shared/models'
13 14
14@Component({ 15@Component({
@@ -147,8 +148,9 @@ export class VideoBlockListComponent extends RestTable implements OnInit {
147 148
148 getVideoEmbed (entry: VideoBlacklist) { 149 getVideoEmbed (entry: VideoBlacklist) {
149 return buildVideoOrPlaylistEmbed( 150 return buildVideoOrPlaylistEmbed(
150 buildVideoLink({ 151 decorateVideoLink({
151 baseUrl: `${environment.originServerUrl}/videos/embed/${entry.video.uuid}`, 152 url: buildVideoEmbedLink(entry.video, environment.originServerUrl),
153
152 title: false, 154 title: false,
153 warningTitle: false 155 warningTitle: false
154 }), 156 }),
diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
index 6af224920..968abcbe5 100644
--- a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
+++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
@@ -4,7 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router'
4import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service' 4import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
5import { ComponentPagination, ConfirmService, hasMoreItems, Notifier } from '@app/core' 5import { ComponentPagination, ConfirmService, hasMoreItems, Notifier } from '@app/core'
6import { PluginService } from '@app/core/plugins/plugin.service' 6import { PluginService } from '@app/core/plugins/plugin.service'
7import { compareSemVer } from '@shared/core-utils/miscs/miscs' 7import { compareSemVer } from '@shared/core-utils'
8import { PeerTubePlugin, PluginType } from '@shared/models' 8import { PeerTubePlugin, PluginType } from '@shared/models'
9 9
10@Component({ 10@Component({
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts
index e02d8e1ad..e3ae68a93 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.ts
+++ b/client/src/app/+admin/users/user-list/user-list.component.ts
@@ -108,18 +108,18 @@ export class UserListComponent extends RestTable implements OnInit {
108 ] 108 ]
109 109
110 this.columns = [ 110 this.columns = [
111 { id: 'username', label: 'Username' }, 111 { id: 'username', label: $localize`Username` },
112 { id: 'email', label: 'Email' }, 112 { id: 'email', label: $localize`Email` },
113 { id: 'quota', label: 'Video quota' }, 113 { id: 'quota', label: $localize`Video quota` },
114 { id: 'role', label: 'Role' }, 114 { id: 'role', label: $localize`Role` },
115 { id: 'createdAt', label: 'Created' } 115 { id: 'createdAt', label: $localize`Created` }
116 ] 116 ]
117 117
118 this.selectedColumns = this.columns.map(c => c.id) 118 this.selectedColumns = this.columns.map(c => c.id)
119 119
120 this.columns.push({ id: 'quotaDaily', label: 'Daily quota' }) 120 this.columns.push({ id: 'quotaDaily', label: $localize`Daily quota` })
121 this.columns.push({ id: 'pluginAuth', label: 'Auth plugin' }) 121 this.columns.push({ id: 'pluginAuth', label: $localize`Auth plugin` })
122 this.columns.push({ id: 'lastLoginDate', label: 'Last login' }) 122 this.columns.push({ id: 'lastLoginDate', label: $localize`Last login` })
123 } 123 }
124 124
125 getIdentifier () { 125 getIdentifier () {
diff --git a/client/src/app/+login/login.component.html b/client/src/app/+login/login.component.html
index 5f5b0f565..27793ff0c 100644
--- a/client/src/app/+login/login.component.html
+++ b/client/src/app/+login/login.component.html
@@ -28,6 +28,10 @@
28 <div *ngIf="formErrors.username" class="form-error"> 28 <div *ngIf="formErrors.username" class="form-error">
29 {{ formErrors.username }} 29 {{ formErrors.username }}
30 </div> 30 </div>
31
32 <div *ngIf="hasUsernameUppercase()" i18n class="form-warning">
33 ⚠️ Most email addresses do not include capital letters.
34 </div>
31 </div> 35 </div>
32 36
33 <div class="form-group"> 37 <div class="form-group">
diff --git a/client/src/app/+login/login.component.ts b/client/src/app/+login/login.component.ts
index d8ad49081..9731383af 100644
--- a/client/src/app/+login/login.component.ts
+++ b/client/src/app/+login/login.component.ts
@@ -141,6 +141,10 @@ The link will expire within 1 hour.`
141 this.accordion = instanceAboutAccordion.accordion 141 this.accordion = instanceAboutAccordion.accordion
142 } 142 }
143 143
144 hasUsernameUppercase () {
145 return this.form.value['username'].match(/[A-Z]/)
146 }
147
144 private loadExternalAuthToken (username: string, token: string) { 148 private loadExternalAuthToken (username: string, token: string) {
145 this.isAuthenticatedWithExternalAuth = true 149 this.isAuthenticatedWithExternalAuth = true
146 150
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
index b3265210f..433475f66 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
@@ -1,3 +1,5 @@
1import { of } from 'rxjs'
2import { switchMap } from 'rxjs/operators'
1import { Component, OnInit } from '@angular/core' 3import { Component, OnInit } from '@angular/core'
2import { Router } from '@angular/router' 4import { Router } from '@angular/router'
3import { AuthService, Notifier } from '@app/core' 5import { AuthService, Notifier } from '@app/core'
@@ -9,11 +11,8 @@ import {
9} from '@app/shared/form-validators/video-channel-validators' 11} from '@app/shared/form-validators/video-channel-validators'
10import { FormValidatorService } from '@app/shared/shared-forms' 12import { FormValidatorService } from '@app/shared/shared-forms'
11import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' 13import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
12import { VideoChannelCreate } from '@shared/models' 14import { HttpStatusCode, VideoChannelCreate } from '@shared/models'
13import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
14import { MyVideoChannelEdit } from './my-video-channel-edit' 15import { MyVideoChannelEdit } from './my-video-channel-edit'
15import { switchMap } from 'rxjs/operators'
16import { of } from 'rxjs'
17 16
18@Component({ 17@Component({
19 templateUrl: './my-video-channel-edit.component.html', 18 templateUrl: './my-video-channel-edit.component.html',
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts
index 67b3ee496..b6a2f592d 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts
@@ -45,9 +45,9 @@ export class MyVideoChannelsComponent {
45It will delete ${videoChannel.videosCount} videos uploaded in this channel, and you will not be able to create another 45It will delete ${videoChannel.videosCount} videos uploaded in this channel, and you will not be able to create another
46channel with the same name (${videoChannel.name})!`, 46channel with the same name (${videoChannel.name})!`,
47 47
48 $localize`Please type the display name of the video channel (${videoChannel.displayName}) to confirm`, 48 $localize`Please type the name of the video channel (${videoChannel.name}) to confirm`,
49 49
50 videoChannel.displayName, 50 videoChannel.name,
51 51
52 $localize`Delete` 52 $localize`Delete`
53 ) 53 )
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.html b/client/src/app/+my-library/my-videos/my-videos.component.html
index 8d8b482ad..0552b8ce4 100644
--- a/client/src/app/+my-library/my-videos/my-videos.component.html
+++ b/client/src/app/+my-library/my-videos/my-videos.component.html
@@ -23,7 +23,7 @@
23 23
24 <div class="peertube-select-container peertube-select-button"> 24 <div class="peertube-select-container peertube-select-button">
25 <select [(ngModel)]="sort" (ngModelChange)="onChangeSortColumn()" class="form-control"> 25 <select [(ngModel)]="sort" (ngModelChange)="onChangeSortColumn()" class="form-control">
26 <option value="undefined" disabled>Sort by</option> 26 <option value="undefined" disabled i18n>Sort by</option>
27 <option value="-publishedAt" i18n>Last published first</option> 27 <option value="-publishedAt" i18n>Last published first</option>
28 <option value="-createdAt" i18n>Last created first</option> 28 <option value="-createdAt" i18n>Last created first</option>
29 <option value="-views" i18n>Most viewed first</option> 29 <option value="-views" i18n>Most viewed first</option>
diff --git a/client/src/app/+page-not-found/page-not-found.component.ts b/client/src/app/+page-not-found/page-not-found.component.ts
index 639e5db78..10645a634 100644
--- a/client/src/app/+page-not-found/page-not-found.component.ts
+++ b/client/src/app/+page-not-found/page-not-found.component.ts
@@ -1,7 +1,8 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { Title } from '@angular/platform-browser' 2import { Title } from '@angular/platform-browser'
3import { Router } from '@angular/router' 3import { Router } from '@angular/router'
4import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '@shared/models'
5
5@Component({ 6@Component({
6 selector: 'my-page-not-found', 7 selector: 'my-page-not-found',
7 templateUrl: './page-not-found.component.html', 8 templateUrl: './page-not-found.component.html',
diff --git a/client/src/app/+search/search-filters.component.html b/client/src/app/+search/search-filters.component.html
index 421bc7f6f..4b87a2102 100644
--- a/client/src/app/+search/search-filters.component.html
+++ b/client/src/app/+search/search-filters.component.html
@@ -63,7 +63,7 @@
63 </div> 63 </div>
64 64
65 <div class="peertube-radio-container" *ngFor="let date of publishedDateRanges"> 65 <div class="peertube-radio-container" *ngFor="let date of publishedDateRanges">
66 <input type="radio" (change)="onInputUpdated()" name="publishedDateRange" [id]="date.id" [value]="date.id" [(ngModel)]="publishedDateRange"> 66 <input type="radio" (change)="onDurationOrPublishedUpdated()" name="publishedDateRange" [id]="date.id" [value]="date.id" [(ngModel)]="publishedDateRange">
67 <label [for]="date.id" class="radio">{{ date.label }}</label> 67 <label [for]="date.id" class="radio">{{ date.label }}</label>
68 </div> 68 </div>
69 </div> 69 </div>
@@ -79,7 +79,7 @@
79 <div class="row"> 79 <div class="row">
80 <div class="pl-0 col-sm-6"> 80 <div class="pl-0 col-sm-6">
81 <input 81 <input
82 (change)="onInputUpdated()" 82 (change)="onDurationOrPublishedUpdated()"
83 (keydown.enter)="$event.preventDefault()" 83 (keydown.enter)="$event.preventDefault()"
84 type="text" id="original-publication-after" name="original-publication-after" 84 type="text" id="original-publication-after" name="original-publication-after"
85 i18n-placeholder placeholder="After..." 85 i18n-placeholder placeholder="After..."
@@ -89,7 +89,7 @@
89 </div> 89 </div>
90 <div class="pr-0 col-sm-6"> 90 <div class="pr-0 col-sm-6">
91 <input 91 <input
92 (change)="onInputUpdated()" 92 (change)="onDurationOrPublishedUpdated()"
93 (keydown.enter)="$event.preventDefault()" 93 (keydown.enter)="$event.preventDefault()"
94 type="text" id="original-publication-before" name="original-publication-before" 94 type="text" id="original-publication-before" name="original-publication-before"
95 i18n-placeholder placeholder="Before..." 95 i18n-placeholder placeholder="Before..."
@@ -112,7 +112,7 @@
112 </div> 112 </div>
113 113
114 <div class="peertube-radio-container" *ngFor="let duration of durationRanges"> 114 <div class="peertube-radio-container" *ngFor="let duration of durationRanges">
115 <input type="radio" (change)="onInputUpdated()" name="durationRange" [id]="duration.id" [value]="duration.id" [(ngModel)]="durationRange"> 115 <input type="radio" (change)="onDurationOrPublishedUpdated()" name="durationRange" [id]="duration.id" [value]="duration.id" [(ngModel)]="durationRange">
116 <label [for]="duration.id" class="radio">{{ duration.label }}</label> 116 <label [for]="duration.id" class="radio">{{ duration.label }}</label>
117 </div> 117 </div>
118 </div> 118 </div>
@@ -174,6 +174,14 @@
174 <my-select-tags name="tagsOneOf" labelForId="tagsOneOf" id="tagsOneOf" [(ngModel)]="advancedSearch.tagsOneOf"></my-select-tags> 174 <my-select-tags name="tagsOneOf" labelForId="tagsOneOf" id="tagsOneOf" [(ngModel)]="advancedSearch.tagsOneOf"></my-select-tags>
175 </div> 175 </div>
176 176
177 <div class="form-group">
178 <label i18n for="host">PeerTube instance host</label>
179
180 <input (change)="onDurationOrPublishedUpdated()" (keydown.enter)="$event.preventDefault()" type="text" id="host" name="host"
181 placeholder="example.com" [(ngModel)]="advancedSearch.host" class="form-control"
182 >
183 </div>
184
177 <div class="form-group" *ngIf="isSearchTargetEnabled()"> 185 <div class="form-group" *ngIf="isSearchTargetEnabled()">
178 <div class="radio-label label-container"> 186 <div class="radio-label label-container">
179 <label i18n>Search target</label> 187 <label i18n>Search target</label>
diff --git a/client/src/app/+search/search-filters.component.ts b/client/src/app/+search/search-filters.component.ts
index afa523b91..5972ba553 100644
--- a/client/src/app/+search/search-filters.component.ts
+++ b/client/src/app/+search/search-filters.component.ts
@@ -108,14 +108,14 @@ export class SearchFiltersComponent implements OnInit {
108 this.loadOriginallyPublishedAtYears() 108 this.loadOriginallyPublishedAtYears()
109 } 109 }
110 110
111 onInputUpdated () { 111 onDurationOrPublishedUpdated () {
112 this.updateModelFromDurationRange() 112 this.updateModelFromDurationRange()
113 this.updateModelFromPublishedRange() 113 this.updateModelFromPublishedRange()
114 this.updateModelFromOriginallyPublishedAtYears() 114 this.updateModelFromOriginallyPublishedAtYears()
115 } 115 }
116 116
117 formUpdated () { 117 formUpdated () {
118 this.onInputUpdated() 118 this.onDurationOrPublishedUpdated()
119 this.filtered.emit(this.advancedSearch) 119 this.filtered.emit(this.advancedSearch)
120 } 120 }
121 121
@@ -127,7 +127,7 @@ export class SearchFiltersComponent implements OnInit {
127 this.durationRange = undefined 127 this.durationRange = undefined
128 this.publishedDateRange = undefined 128 this.publishedDateRange = undefined
129 129
130 this.onInputUpdated() 130 this.onDurationOrPublishedUpdated()
131 } 131 }
132 132
133 resetField (fieldName: string, value?: any) { 133 resetField (fieldName: string, value?: any) {
@@ -136,7 +136,7 @@ export class SearchFiltersComponent implements OnInit {
136 136
137 resetLocalField (fieldName: string, value?: any) { 137 resetLocalField (fieldName: string, value?: any) {
138 this[fieldName] = value 138 this[fieldName] = value
139 this.onInputUpdated() 139 this.onDurationOrPublishedUpdated()
140 } 140 }
141 141
142 resetOriginalPublicationYears () { 142 resetOriginalPublicationYears () {
diff --git a/client/src/app/+search/search.component.html b/client/src/app/+search/search.component.html
index b28abca6a..dc8b4d595 100644
--- a/client/src/app/+search/search.component.html
+++ b/client/src/app/+search/search.component.html
@@ -24,6 +24,8 @@
24 24
25 <div class="results-filter collapse-transition" [ngbCollapse]="isSearchFilterCollapsed"> 25 <div class="results-filter collapse-transition" [ngbCollapse]="isSearchFilterCollapsed">
26 <my-search-filters [advancedSearch]="advancedSearch" (filtered)="onFiltered()"></my-search-filters> 26 <my-search-filters [advancedSearch]="advancedSearch" (filtered)="onFiltered()"></my-search-filters>
27
28 <div *ngIf="error" class="alert alert-danger">{{ error }}</div>
27 </div> 29 </div>
28 </div> 30 </div>
29 31
diff --git a/client/src/app/+search/search.component.scss b/client/src/app/+search/search.component.scss
index fca704d27..b521825e5 100644
--- a/client/src/app/+search/search.component.scss
+++ b/client/src/app/+search/search.component.scss
@@ -15,6 +15,10 @@
15 padding: 40px; 15 padding: 40px;
16} 16}
17 17
18.alert-danger {
19 margin-top: 10px;
20}
21
18.results-header { 22.results-header {
19 font-size: 16px; 23 font-size: 16px;
20 padding-bottom: 20px; 24 padding-bottom: 20px;
diff --git a/client/src/app/+search/search.component.ts b/client/src/app/+search/search.component.ts
index 235bbfa4c..7425b7016 100644
--- a/client/src/app/+search/search.component.ts
+++ b/client/src/app/+search/search.component.ts
@@ -1,9 +1,10 @@
1import { forkJoin, of, Subscription } from 'rxjs' 1import { forkJoin, Subscription } from 'rxjs'
2import { LinkType } from 'src/types/link.type' 2import { LinkType } from 'src/types/link.type'
3import { Component, OnDestroy, OnInit } from '@angular/core' 3import { Component, OnDestroy, OnInit } from '@angular/core'
4import { ActivatedRoute, Router } from '@angular/router' 4import { ActivatedRoute, Router } from '@angular/router'
5import { AuthService, HooksService, MetaService, Notifier, ServerService, User, UserService } from '@app/core' 5import { AuthService, HooksService, MetaService, Notifier, ServerService, User, UserService } from '@app/core'
6import { immutableAssign } from '@app/helpers' 6import { immutableAssign } from '@app/helpers'
7import { validateHost } from '@app/shared/form-validators/host-validators'
7import { Video, VideoChannel } from '@app/shared/shared-main' 8import { Video, VideoChannel } from '@app/shared/shared-main'
8import { AdvancedSearch, SearchService } from '@app/shared/shared-search' 9import { AdvancedSearch, SearchService } from '@app/shared/shared-search'
9import { MiniatureDisplayOptions } from '@app/shared/shared-video-miniature' 10import { MiniatureDisplayOptions } from '@app/shared/shared-video-miniature'
@@ -16,7 +17,9 @@ import { HTMLServerConfig, SearchTargetType } from '@shared/models'
16 templateUrl: './search.component.html' 17 templateUrl: './search.component.html'
17}) 18})
18export class SearchComponent implements OnInit, OnDestroy { 19export class SearchComponent implements OnInit, OnDestroy {
19 results: (Video | VideoChannel)[] = [] 20 error: string
21
22 results: (Video | VideoChannel | VideoPlaylist)[] = []
20 23
21 pagination = { 24 pagination = {
22 currentPage: 1, 25 currentPage: 1,
@@ -89,8 +92,10 @@ export class SearchComponent implements OnInit, OnDestroy {
89 this.advancedSearch.searchTarget = this.getDefaultSearchTarget() 92 this.advancedSearch.searchTarget = this.getDefaultSearchTarget()
90 } 93 }
91 94
92 // Don't hide filters if we have some of them AND the user just came on the webpage 95 this.error = this.checkFieldsAndGetError()
93 this.isSearchFilterCollapsed = this.isInitialLoad === false || !this.advancedSearch.containsValues() 96
97 // Don't hide filters if we have some of them AND the user just came on the webpage, or we have an error
98 this.isSearchFilterCollapsed = !this.error && (this.isInitialLoad === false || !this.advancedSearch.containsValues())
94 this.isInitialLoad = false 99 this.isInitialLoad = false
95 100
96 this.search() 101 this.search()
@@ -126,6 +131,9 @@ export class SearchComponent implements OnInit, OnDestroy {
126 } 131 }
127 132
128 search () { 133 search () {
134 this.error = this.checkFieldsAndGetError()
135 if (this.error) return
136
129 this.isSearching = true 137 this.isSearching = true
130 138
131 forkJoin([ 139 forkJoin([
@@ -275,12 +283,10 @@ export class SearchComponent implements OnInit, OnDestroy {
275 } 283 }
276 284
277 private getVideoChannelObs () { 285 private getVideoChannelObs () {
278 if (!this.currentSearch) return of({ data: [], total: 0 })
279
280 const params = { 286 const params = {
281 search: this.currentSearch, 287 search: this.currentSearch,
282 componentPagination: immutableAssign(this.pagination, { itemsPerPage: this.channelsPerPage }), 288 componentPagination: immutableAssign(this.pagination, { itemsPerPage: this.channelsPerPage }),
283 searchTarget: this.advancedSearch.searchTarget 289 advancedSearch: this.advancedSearch
284 } 290 }
285 291
286 return this.hooks.wrapObsFun( 292 return this.hooks.wrapObsFun(
@@ -293,12 +299,10 @@ export class SearchComponent implements OnInit, OnDestroy {
293 } 299 }
294 300
295 private getVideoPlaylistObs () { 301 private getVideoPlaylistObs () {
296 if (!this.currentSearch) return of({ data: [], total: 0 })
297
298 const params = { 302 const params = {
299 search: this.currentSearch, 303 search: this.currentSearch,
300 componentPagination: immutableAssign(this.pagination, { itemsPerPage: this.playlistsPerPage }), 304 componentPagination: immutableAssign(this.pagination, { itemsPerPage: this.playlistsPerPage }),
301 searchTarget: this.advancedSearch.searchTarget 305 advancedSearch: this.advancedSearch
302 } 306 }
303 307
304 return this.hooks.wrapObsFun( 308 return this.hooks.wrapObsFun(
@@ -319,4 +323,12 @@ export class SearchComponent implements OnInit, OnDestroy {
319 323
320 return 'local' 324 return 'local'
321 } 325 }
326
327 private checkFieldsAndGetError () {
328 if (this.advancedSearch.host && !validateHost(this.advancedSearch.host)) {
329 return $localize`PeerTube instance host filter is invalid`
330 }
331
332 return undefined
333 }
322} 334}
diff --git a/client/src/app/+video-channels/video-channels.component.ts b/client/src/app/+video-channels/video-channels.component.ts
index 3833d9c54..6479644f1 100644
--- a/client/src/app/+video-channels/video-channels.component.ts
+++ b/client/src/app/+video-channels/video-channels.component.ts
@@ -7,7 +7,7 @@ import { AuthService, MarkdownService, Notifier, RestExtractor, ScreenService }
7import { ListOverflowItem, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' 7import { ListOverflowItem, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
8import { SupportModalComponent } from '@app/shared/shared-support-modal' 8import { SupportModalComponent } from '@app/shared/shared-support-modal'
9import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' 9import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
10import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 10import { HttpStatusCode } from '@shared/models'
11 11
12@Component({ 12@Component({
13 templateUrl: './video-channels.component.html', 13 templateUrl: './video-channels.component.html',
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.html b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
index 50d030ac9..ee5a50611 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.html
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
@@ -45,7 +45,7 @@
45 </ng-template> 45 </ng-template>
46 </my-help> 46 </my-help>
47 47
48 <my-markdown-textarea [truncate]="250" formControlName="description" [markdownVideo]="true"></my-markdown-textarea> 48 <my-markdown-textarea [truncate]="250" formControlName="description" [markdownVideo]="videoToUpdate"></my-markdown-textarea>
49 49
50 <div *ngIf="formErrors.description" class="form-error"> 50 <div *ngIf="formErrors.description" class="form-error">
51 {{ formErrors.description }} 51 {{ formErrors.description }}
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
index d8d20a249..189bc9669 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
@@ -7,8 +7,7 @@ import { genericUploadErrorHandler, scrollToTop } from '@app/helpers'
7import { FormValidatorService } from '@app/shared/shared-forms' 7import { FormValidatorService } from '@app/shared/shared-forms'
8import { BytesPipe, Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' 8import { BytesPipe, Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
9import { LoadingBarService } from '@ngx-loading-bar/core' 9import { LoadingBarService } from '@ngx-loading-bar/core'
10import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 10import { HttpStatusCode, VideoPrivacy } from '@shared/models'
11import { VideoPrivacy } from '@shared/models'
12import { UploaderXFormData } from './uploaderx-form-data' 11import { UploaderXFormData } from './uploaderx-form-data'
13import { VideoSend } from './video-send' 12import { VideoSend } from './video-send'
14 13
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts b/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts
index 04f8f0d58..0e1c4c207 100644
--- a/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts
@@ -161,7 +161,7 @@ export class VideoCommentComponent implements OnInit, OnChanges {
161 // Before HTML rendering restore line feed for markdown list compatibility 161 // Before HTML rendering restore line feed for markdown list compatibility
162 const commentText = this.comment.text.replace(/<br.?\/?>/g, '\r\n') 162 const commentText = this.comment.text.replace(/<br.?\/?>/g, '\r\n')
163 const html = await this.markdownService.textMarkdownToHTML(commentText, true, true) 163 const html = await this.markdownService.textMarkdownToHTML(commentText, true, true)
164 this.sanitizedCommentHTML = this.markdownService.processVideoTimestamps(html) 164 this.sanitizedCommentHTML = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
165 this.newParentComments = this.parentComments.concat([ this.comment ]) 165 this.newParentComments = this.parentComments.concat([ this.comment ])
166 166
167 if (this.comment.account) { 167 if (this.comment.account) {
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.html b/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.html
index 598bc485d..362a21905 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.html
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.html
@@ -1,54 +1,62 @@
1<div class="video-attribute"> 1<div class="attribute">
2 <span i18n class="video-attribute-label">Privacy</span> 2 <span i18n class="attribute-label">Privacy</span>
3 <span class="video-attribute-value">{{ video.privacy.label }}</span> 3 <span class="attribute-value">{{ video.privacy.label }}</span>
4</div> 4</div>
5 5
6<div *ngIf="video.isLocal === false" class="video-attribute"> 6<div *ngIf="video.isLocal === false" class="attribute">
7 <span i18n class="video-attribute-label">Origin</span> 7 <span i18n class="attribute-label">Origin</span>
8 <a class="video-attribute-value" target="_blank" rel="noopener noreferrer" [href]="getVideoUrl()">{{ video.originInstanceHost }}</a> 8 <a
9 class="attribute-value" target="_blank" rel="noopener noreferrer"
10 routerLink="/search" [queryParams]="{ host: getVideoHost() }"
11 >{{ video.originInstanceHost }}</a>
12
13 <a
14 i18n-title title="Open the video on the origin instance" class="glyphicon glyphicon-new-window"
15 target="_blank" rel="noopener noreferrer" [href]="getVideoUrl()"
16 ></a>
9</div> 17</div>
10 18
11<div *ngIf="!!video.originallyPublishedAt" class="video-attribute"> 19<div *ngIf="!!video.originallyPublishedAt" class="attribute">
12 <span i18n class="video-attribute-label">Originally published</span> 20 <span i18n class="attribute-label">Originally published</span>
13 <span class="video-attribute-value">{{ video.originallyPublishedAt | date: 'dd MMMM yyyy' }}</span> 21 <span class="attribute-value">{{ video.originallyPublishedAt | date: 'dd MMMM yyyy' }}</span>
14</div> 22</div>
15 23
16<div class="video-attribute"> 24<div class="attribute">
17 <span i18n class="video-attribute-label">Category</span> 25 <span i18n class="attribute-label">Category</span>
18 <span *ngIf="!video.category.id" class="video-attribute-value">{{ video.category.label }}</span> 26 <span *ngIf="!video.category.id" class="attribute-value">{{ video.category.label }}</span>
19 <a 27 <a
20 *ngIf="video.category.id" class="video-attribute-value" 28 *ngIf="video.category.id" class="attribute-value"
21 [routerLink]="[ '/search' ]" [queryParams]="{ categoryOneOf: [ video.category.id ] }" 29 [routerLink]="[ '/search' ]" [queryParams]="{ categoryOneOf: [ video.category.id ] }"
22 >{{ video.category.label }}</a> 30 >{{ video.category.label }}</a>
23</div> 31</div>
24 32
25<div class="video-attribute"> 33<div class="attribute">
26 <span i18n class="video-attribute-label">Licence</span> 34 <span i18n class="attribute-label">Licence</span>
27 <span *ngIf="!video.licence.id" class="video-attribute-value">{{ video.licence.label }}</span> 35 <span *ngIf="!video.licence.id" class="attribute-value">{{ video.licence.label }}</span>
28 <a 36 <a
29 *ngIf="video.licence.id" class="video-attribute-value" 37 *ngIf="video.licence.id" class="attribute-value"
30 [routerLink]="[ '/search' ]" [queryParams]="{ licenceOneOf: [ video.licence.id ] }" 38 [routerLink]="[ '/search' ]" [queryParams]="{ licenceOneOf: [ video.licence.id ] }"
31 >{{ video.licence.label }}</a> 39 >{{ video.licence.label }}</a>
32</div> 40</div>
33 41
34<div class="video-attribute"> 42<div class="attribute">
35 <span i18n class="video-attribute-label">Language</span> 43 <span i18n class="attribute-label">Language</span>
36 <span *ngIf="!video.language.id" class="video-attribute-value">{{ video.language.label }}</span> 44 <span *ngIf="!video.language.id" class="attribute-value">{{ video.language.label }}</span>
37 <a 45 <a
38 *ngIf="video.language.id" class="video-attribute-value" 46 *ngIf="video.language.id" class="attribute-value"
39 [routerLink]="[ '/search' ]" [queryParams]="{ languageOneOf: [ video.language.id ] }" 47 [routerLink]="[ '/search' ]" [queryParams]="{ languageOneOf: [ video.language.id ] }"
40 >{{ video.language.label }}</a> 48 >{{ video.language.label }}</a>
41</div> 49</div>
42 50
43<div class="video-attribute video-attribute-tags"> 51<div class="attribute attribute-tags">
44 <span i18n class="video-attribute-label">Tags</span> 52 <span i18n class="attribute-label">Tags</span>
45 <a 53 <a
46 *ngFor="let tag of getVideoTags()" 54 *ngFor="let tag of getVideoTags()"
47 class="video-attribute-value" [routerLink]="[ '/search' ]" [queryParams]="{ tagsOneOf: [ tag ] }" 55 class="attribute-value" [routerLink]="[ '/search' ]" [queryParams]="{ tagsOneOf: [ tag ] }"
48 >{{ tag }}</a> 56 >{{ tag }}</a>
49</div> 57</div>
50 58
51<div class="video-attribute" *ngIf="!video.isLive"> 59<div class="attribute" *ngIf="!video.isLive">
52 <span i18n class="video-attribute-label">Duration</span> 60 <span i18n class="attribute-label">Duration</span>
53 <span class="video-attribute-value">{{ video.duration | myDurationFormatter }}</span> 61 <span class="attribute-value">{{ video.duration | myDurationFormatter }}</span>
54</div> 62</div>
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.scss b/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.scss
index 45190a3e3..26bead124 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.scss
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.scss
@@ -1,13 +1,13 @@
1@use '_variables' as *; 1@use '_variables' as *;
2@use '_mixins' as *; 2@use '_mixins' as *;
3 3
4.video-attribute { 4.attribute {
5 font-size: 13px; 5 font-size: 13px;
6 display: block; 6 display: block;
7 margin-bottom: 12px; 7 margin-bottom: 12px;
8} 8}
9 9
10.video-attribute-label { 10.attribute-label {
11 @include padding-right(5px); 11 @include padding-right(5px);
12 12
13 min-width: 142px; 13 min-width: 142px;
@@ -16,7 +16,7 @@
16 font-weight: $font-bold; 16 font-weight: $font-bold;
17} 17}
18 18
19a.video-attribute-value { 19a.attribute-value {
20 @include disable-default-a-behaviour; 20 @include disable-default-a-behaviour;
21 color: pvar(--mainForegroundColor); 21 color: pvar(--mainForegroundColor);
22 22
@@ -25,16 +25,22 @@ a.video-attribute-value {
25 } 25 }
26} 26}
27 27
28.video-attribute-tags { 28.attribute-tags {
29 .video-attribute-value:not(:nth-child(2)) { 29 .attribute-value:not(:nth-child(2)) {
30 &::before { 30 &::before {
31 content: ', '; 31 content: ', ';
32 } 32 }
33 } 33 }
34} 34}
35 35
36.glyphicon-new-window {
37 color: pvar(--inputPlaceholderColor);
38 margin-left: 5px;
39 font-size: 12px;
40}
41
36@media screen and (max-width: 1600px) { 42@media screen and (max-width: 1600px) {
37 .video-attributes .video-attribute { 43 .attributes .attribute {
38 margin-bottom: 5px; 44 margin-bottom: 5px;
39 } 45 }
40} 46}
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.ts b/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.ts
index 5cb77f0c8..9429581ac 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-attributes.component.ts
@@ -17,6 +17,10 @@ export class VideoAttributesComponent {
17 return this.video.url 17 return this.video.url
18 } 18 }
19 19
20 getVideoHost () {
21 return this.video.channel.host
22 }
23
20 getVideoTags () { 24 getVideoTags () {
21 if (!this.video || Array.isArray(this.video.tags) === false) return [] 25 if (!this.video || Array.isArray(this.video.tags) === false) return []
22 26
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
index 23d00d31a..870c7ae3f 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
@@ -80,6 +80,7 @@ export class VideoDescriptionComponent implements OnChanges {
80 80
81 private async setVideoDescriptionHTML () { 81 private async setVideoDescriptionHTML () {
82 const html = await this.markdownService.textMarkdownToHTML(this.video.description) 82 const html = await this.markdownService.textMarkdownToHTML(this.video.description)
83 this.videoHTMLDescription = this.markdownService.processVideoTimestamps(html) 83
84 this.videoHTMLDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
84 } 85 }
85} 86}
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts
index d078844c3..ccb9c5e71 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.component.ts
@@ -21,8 +21,16 @@ import { isXPercentInViewport, scrollToTop } from '@app/helpers'
21import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' 21import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main'
22import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' 22import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
23import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' 23import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
24import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 24import { timeToInt } from '@shared/core-utils'
25import { HTMLServerConfig, PeerTubeProblemDocument, ServerErrorCode, VideoCaption, VideoPrivacy, VideoState } from '@shared/models' 25import {
26 HTMLServerConfig,
27 HttpStatusCode,
28 PeerTubeProblemDocument,
29 ServerErrorCode,
30 VideoCaption,
31 VideoPrivacy,
32 VideoState
33} from '@shared/models'
26import { cleanupVideoWatch, getStoredTheater, getStoredVideoWatchHistory } from '../../../assets/player/peertube-player-local-storage' 34import { cleanupVideoWatch, getStoredTheater, getStoredVideoWatchHistory } from '../../../assets/player/peertube-player-local-storage'
27import { 35import {
28 CustomizationOptions, 36 CustomizationOptions,
@@ -32,7 +40,6 @@ import {
32 PlayerMode, 40 PlayerMode,
33 videojs 41 videojs
34} from '../../../assets/player/peertube-player-manager' 42} from '../../../assets/player/peertube-player-manager'
35import { timeToInt } from '../../../assets/player/utils'
36import { environment } from '../../../environments/environment' 43import { environment } from '../../../environments/environment'
37import { VideoWatchPlaylistComponent } from './shared' 44import { VideoWatchPlaylistComponent } from './shared'
38 45
@@ -575,6 +582,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
575 582
576 videoCaptions: playerCaptions, 583 videoCaptions: playerCaptions,
577 584
585 videoShortUUID: video.shortUUID,
578 videoUUID: video.uuid 586 videoUUID: video.uuid
579 }, 587 },
580 588
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts
index cdf13186b..60bd72c60 100644
--- a/client/src/app/core/auth/auth.service.ts
+++ b/client/src/app/core/auth/auth.service.ts
@@ -6,12 +6,11 @@ import { Injectable } from '@angular/core'
6import { Router } from '@angular/router' 6import { Router } from '@angular/router'
7import { Notifier } from '@app/core/notification/notifier.service' 7import { Notifier } from '@app/core/notification/notifier.service'
8import { objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index' 8import { objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index'
9import { MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models' 9import { HttpStatusCode, MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models'
10import { environment } from '../../../environments/environment' 10import { environment } from '../../../environments/environment'
11import { RestExtractor } from '../rest/rest-extractor.service' 11import { RestExtractor } from '../rest/rest-extractor.service'
12import { AuthStatus } from './auth-status.model' 12import { AuthStatus } from './auth-status.model'
13import { AuthUser } from './auth-user.model' 13import { AuthUser } from './auth-user.model'
14import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
15 14
16interface UserLoginWithUsername extends UserLogin { 15interface UserLoginWithUsername extends UserLogin {
17 access_token: string 16 access_token: string
diff --git a/client/src/app/core/menu/menu.service.ts b/client/src/app/core/menu/menu.service.ts
index 60130382f..0b8d0191e 100644
--- a/client/src/app/core/menu/menu.service.ts
+++ b/client/src/app/core/menu/menu.service.ts
@@ -101,7 +101,7 @@ export class MenuService {
101 101
102 return { 102 return {
103 key: 'in-my-library', 103 key: 'in-my-library',
104 title: 'In my library', 104 title: $localize`In my library`,
105 links 105 links
106 } 106 }
107 } 107 }
diff --git a/client/src/app/core/renderer/markdown.service.ts b/client/src/app/core/renderer/markdown.service.ts
index ca1bf4eb9..36258ca98 100644
--- a/client/src/app/core/renderer/markdown.service.ts
+++ b/client/src/app/core/renderer/markdown.service.ts
@@ -1,6 +1,6 @@
1import * as MarkdownIt from 'markdown-it' 1import * as MarkdownIt from 'markdown-it'
2import { buildVideoLink } from 'src/assets/player/utils'
3import { Injectable } from '@angular/core' 2import { Injectable } from '@angular/core'
3import { buildVideoLink, decorateVideoLink } from '@shared/core-utils'
4import { 4import {
5 COMPLETE_RULES, 5 COMPLETE_RULES,
6 ENHANCED_RULES, 6 ENHANCED_RULES,
@@ -82,10 +82,14 @@ export class MarkdownService {
82 return this.render({ name: 'customPageMarkdownIt', markdown, withEmoji: true, additionalAllowedTags }) 82 return this.render({ name: 'customPageMarkdownIt', markdown, withEmoji: true, additionalAllowedTags })
83 } 83 }
84 84
85 processVideoTimestamps (html: string) { 85 processVideoTimestamps (videoShortUUID: string, html: string) {
86 return html.replace(/((\d{1,2}):)?(\d{1,2}):(\d{1,2})/g, function (str, _, h, m, s) { 86 return html.replace(/((\d{1,2}):)?(\d{1,2}):(\d{1,2})/g, function (str, _, h, m, s) {
87 const t = (3600 * +(h || 0)) + (60 * +(m || 0)) + (+(s || 0)) 87 const t = (3600 * +(h || 0)) + (60 * +(m || 0)) + (+(s || 0))
88 const url = buildVideoLink({ startTime: t }) 88
89 const url = decorateVideoLink({
90 url: buildVideoLink({ shortUUID: videoShortUUID }),
91 startTime: t
92 })
89 return `<a class="video-timestamp" href="${url}">${str}</a>` 93 return `<a class="video-timestamp" href="${url}">${str}</a>`
90 }) 94 })
91 } 95 }
diff --git a/client/src/app/core/rest/rest-extractor.service.ts b/client/src/app/core/rest/rest-extractor.service.ts
index 08ab49512..2a926e68f 100644
--- a/client/src/app/core/rest/rest-extractor.service.ts
+++ b/client/src/app/core/rest/rest-extractor.service.ts
@@ -2,8 +2,7 @@ import { throwError as observableThrowError } from 'rxjs'
2import { Injectable } from '@angular/core' 2import { Injectable } from '@angular/core'
3import { Router } from '@angular/router' 3import { Router } from '@angular/router'
4import { dateToHuman } from '@app/helpers' 4import { dateToHuman } from '@app/helpers'
5import { ResultList } from '@shared/models' 5import { HttpStatusCode, ResultList } from '@shared/models'
6import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
7 6
8@Injectable() 7@Injectable()
9export class RestExtractor { 8export class RestExtractor {
diff --git a/client/src/app/helpers/utils.ts b/client/src/app/helpers/utils.ts
index 94f6def26..edcaf50e0 100644
--- a/client/src/app/helpers/utils.ts
+++ b/client/src/app/helpers/utils.ts
@@ -3,7 +3,7 @@ import { SelectChannelItem } from 'src/types/select-options-item.model'
3import { DatePipe } from '@angular/common' 3import { DatePipe } from '@angular/common'
4import { HttpErrorResponse } from '@angular/common/http' 4import { HttpErrorResponse } from '@angular/common/http'
5import { Notifier } from '@app/core' 5import { Notifier } from '@app/core'
6import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '@shared/models'
7import { environment } from '../../environments/environment' 7import { environment } from '../../environments/environment'
8import { AuthService } from '../core/auth' 8import { AuthService } from '../core/auth'
9 9
diff --git a/client/src/app/shared/form-validators/batch-domains-validators.ts b/client/src/app/shared/form-validators/batch-domains-validators.ts
deleted file mode 100644
index 423d1337f..000000000
--- a/client/src/app/shared/form-validators/batch-domains-validators.ts
+++ /dev/null
@@ -1,60 +0,0 @@
1import { AbstractControl, FormControl, ValidatorFn, Validators } from '@angular/forms'
2import { BuildFormValidator } from './form-validator.model'
3import { validateHost } from './host'
4
5export function getNotEmptyHosts (hosts: string) {
6 return hosts
7 .split('\n')
8 .filter((host: string) => host && host.length !== 0) // Eject empty hosts
9}
10
11const validDomains: ValidatorFn = (control: FormControl) => {
12 if (!control.value) return null
13
14 const newHostsErrors = []
15 const hosts = getNotEmptyHosts(control.value)
16
17 for (const host of hosts) {
18 if (validateHost(host) === false) {
19 newHostsErrors.push($localize`${host} is not valid`)
20 }
21 }
22
23 /* Is not valid. */
24 if (newHostsErrors.length !== 0) {
25 return {
26 'validDomains': {
27 reason: 'invalid',
28 value: newHostsErrors.join('. ') + '.'
29 }
30 }
31 }
32
33 /* Is valid. */
34 return null
35}
36
37const isHostsUnique: ValidatorFn = (control: AbstractControl) => {
38 if (!control.value) return null
39
40 const hosts = getNotEmptyHosts(control.value)
41
42 if (hosts.every((host: string) => hosts.indexOf(host) === hosts.lastIndexOf(host))) {
43 return null
44 } else {
45 return {
46 'uniqueDomains': {
47 reason: 'invalid'
48 }
49 }
50 }
51}
52
53export const DOMAINS_VALIDATOR: BuildFormValidator = {
54 VALIDATORS: [Validators.required, validDomains, isHostsUnique],
55 MESSAGES: {
56 'required': $localize`Domain is required.`,
57 'validDomains': $localize`Domains entered are invalid.`,
58 'uniqueDomains': $localize`Domains entered contain duplicates.`
59 }
60}
diff --git a/client/src/app/shared/form-validators/host-validators.ts b/client/src/app/shared/form-validators/host-validators.ts
new file mode 100644
index 000000000..6f410a50a
--- /dev/null
+++ b/client/src/app/shared/form-validators/host-validators.ts
@@ -0,0 +1,105 @@
1import { AbstractControl, ValidatorFn, Validators } from '@angular/forms'
2import { BuildFormValidator } from './form-validator.model'
3
4export function validateHost (value: string) {
5 // Thanks to http://stackoverflow.com/a/106223
6 const HOST_REGEXP = new RegExp(
7 '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$'
8 )
9
10 return HOST_REGEXP.test(value)
11}
12
13export function validateHandle (value: string) {
14 if (!value) return false
15
16 return value.includes('@')
17}
18
19const validHosts: ValidatorFn = (control: AbstractControl) => {
20 if (!control.value) return null
21
22 const errors = []
23 const hosts = splitAndGetNotEmpty(control.value)
24
25 for (const host of hosts) {
26 if (validateHost(host) === false) {
27 errors.push($localize`${host} is not valid`)
28 }
29 }
30
31 // valid
32 if (errors.length === 0) return null
33
34 return {
35 'validHosts': {
36 reason: 'invalid',
37 value: errors.join('. ') + '.'
38 }
39 }
40}
41
42const validHostsOrHandles: ValidatorFn = (control: AbstractControl) => {
43 if (!control.value) return null
44
45 const errors = []
46 const lines = splitAndGetNotEmpty(control.value)
47
48 for (const line of lines) {
49 if (validateHost(line) === false && validateHandle(line) === false) {
50 errors.push($localize`${line} is not valid`)
51 }
52 }
53
54 // valid
55 if (errors.length === 0) return null
56
57 return {
58 'validHostsOrHandles': {
59 reason: 'invalid',
60 value: errors.join('. ') + '.'
61 }
62 }
63}
64
65// ---------------------------------------------------------------------------
66
67export function splitAndGetNotEmpty (value: string) {
68 return value
69 .split('\n')
70 .filter(line => line && line.length !== 0) // Eject empty hosts
71}
72
73export const unique: ValidatorFn = (control: AbstractControl) => {
74 if (!control.value) return null
75
76 const hosts = splitAndGetNotEmpty(control.value)
77
78 if (hosts.every((host: string) => hosts.indexOf(host) === hosts.lastIndexOf(host))) {
79 return null
80 }
81
82 return {
83 'unique': {
84 reason: 'invalid'
85 }
86 }
87}
88
89export const UNIQUE_HOSTS_VALIDATOR: BuildFormValidator = {
90 VALIDATORS: [ Validators.required, validHosts, unique ],
91 MESSAGES: {
92 'required': $localize`Domain is required.`,
93 'validHosts': $localize`Hosts entered are invalid.`,
94 'unique': $localize`Hosts entered contain duplicates.`
95 }
96}
97
98export const UNIQUE_HOSTS_OR_HANDLE_VALIDATOR: BuildFormValidator = {
99 VALIDATORS: [ Validators.required, validHostsOrHandles, unique ],
100 MESSAGES: {
101 'required': $localize`Domain is required.`,
102 'validHostsOrHandles': $localize`Hosts or handles are invalid.`,
103 'unique': $localize`Hosts or handles contain duplicates.`
104 }
105}
diff --git a/client/src/app/shared/form-validators/host.ts b/client/src/app/shared/form-validators/host.ts
deleted file mode 100644
index c18a35f9b..000000000
--- a/client/src/app/shared/form-validators/host.ts
+++ /dev/null
@@ -1,8 +0,0 @@
1export function validateHost (value: string) {
2 // Thanks to http://stackoverflow.com/a/106223
3 const HOST_REGEXP = new RegExp(
4 '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$'
5 )
6
7 return HOST_REGEXP.test(value)
8}
diff --git a/client/src/app/shared/form-validators/index.ts b/client/src/app/shared/form-validators/index.ts
index f621f03a4..0b605719c 100644
--- a/client/src/app/shared/form-validators/index.ts
+++ b/client/src/app/shared/form-validators/index.ts
@@ -1,7 +1,6 @@
1export * from './form-validator.model' 1export * from './form-validator.model'
2export * from './host'
3 2
4// Don't re export const variables because webpack 4 cannot do tree shaking with them 3// Don't re export const variables because webpack cannot do tree shaking with them
5// export * from './abuse-validators' 4// export * from './abuse-validators'
6// export * from './batch-domains-validators' 5// export * from './batch-domains-validators'
7// export * from './custom-config-validators' 6// export * from './custom-config-validators'
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
index 67aa0e399..a7932ebab 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
+++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
@@ -1,7 +1,7 @@
1import * as debug from 'debug' 1import * as debug from 'debug'
2import truncate from 'lodash-es/truncate' 2import truncate from 'lodash-es/truncate'
3import { SortMeta } from 'primeng/api' 3import { SortMeta } from 'primeng/api'
4import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' 4import { buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
5import { environment } from 'src/environments/environment' 5import { environment } from 'src/environments/environment'
6import { Component, Input, OnInit, ViewChild } from '@angular/core' 6import { Component, Input, OnInit, ViewChild } from '@angular/core'
7import { DomSanitizer } from '@angular/platform-browser' 7import { DomSanitizer } from '@angular/platform-browser'
@@ -10,6 +10,7 @@ import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable }
10import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main' 10import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main'
11import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation' 11import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
12import { VideoCommentService } from '@app/shared/shared-video-comment' 12import { VideoCommentService } from '@app/shared/shared-video-comment'
13import { buildVideoEmbedLink, decorateVideoLink } from '@shared/core-utils'
13import { AbuseState, AdminAbuse } from '@shared/models' 14import { AbuseState, AdminAbuse } from '@shared/models'
14import { AdvancedInputFilter } from '../shared-forms' 15import { AdvancedInputFilter } from '../shared-forms'
15import { AbuseMessageModalComponent } from './abuse-message-modal.component' 16import { AbuseMessageModalComponent } from './abuse-message-modal.component'
@@ -129,8 +130,8 @@ export class AbuseListTableComponent extends RestTable implements OnInit {
129 130
130 getVideoEmbed (abuse: AdminAbuse) { 131 getVideoEmbed (abuse: AdminAbuse) {
131 return buildVideoOrPlaylistEmbed( 132 return buildVideoOrPlaylistEmbed(
132 buildVideoLink({ 133 decorateVideoLink({
133 baseUrl: `${environment.originServerUrl}/videos/embed/${abuse.video.uuid}`, 134 url: buildVideoEmbedLink(abuse.video, environment.originServerUrl),
134 title: false, 135 title: false,
135 warningTitle: false, 136 warningTitle: false,
136 startTime: abuse.video.startAt, 137 startTime: abuse.video.startAt,
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts
index 4462903db..53b70cc47 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts
@@ -1,6 +1,7 @@
1import { buildPlaylistLink, buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' 1import { buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
2import { environment } from 'src/environments/environment' 2import { environment } from 'src/environments/environment'
3import { Component, ElementRef, Input, OnInit } from '@angular/core' 3import { Component, ElementRef, Input, OnInit } from '@angular/core'
4import { buildPlaylistEmbedLink, buildVideoEmbedLink } from '@shared/core-utils'
4import { CustomMarkupComponent } from './shared' 5import { CustomMarkupComponent } from './shared'
5 6
6@Component({ 7@Component({
@@ -17,8 +18,8 @@ export class EmbedMarkupComponent implements CustomMarkupComponent, OnInit {
17 18
18 ngOnInit () { 19 ngOnInit () {
19 const link = this.type === 'video' 20 const link = this.type === 'video'
20 ? buildVideoLink({ baseUrl: `${environment.originServerUrl}/videos/embed/${this.uuid}` }) 21 ? buildVideoEmbedLink({ uuid: this.uuid }, environment.originServerUrl)
21 : buildPlaylistLink({ baseUrl: `${environment.originServerUrl}/video-playlists/embed/${this.uuid}` }) 22 : buildPlaylistEmbedLink({ uuid: this.uuid }, environment.originServerUrl)
22 23
23 this.el.nativeElement.innerHTML = buildVideoOrPlaylistEmbed(link, this.uuid) 24 this.el.nativeElement.innerHTML = buildVideoOrPlaylistEmbed(link, this.uuid)
24 } 25 }
diff --git a/client/src/app/shared/shared-forms/markdown-textarea.component.ts b/client/src/app/shared/shared-forms/markdown-textarea.component.ts
index a233a4205..8f51d47df 100644
--- a/client/src/app/shared/shared-forms/markdown-textarea.component.ts
+++ b/client/src/app/shared/shared-forms/markdown-textarea.component.ts
@@ -6,6 +6,7 @@ import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@an
6import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' 6import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
7import { SafeHtml } from '@angular/platform-browser' 7import { SafeHtml } from '@angular/platform-browser'
8import { MarkdownService, ScreenService } from '@app/core' 8import { MarkdownService, ScreenService } from '@app/core'
9import { Video } from '@shared/models'
9 10
10@Component({ 11@Component({
11 selector: 'my-markdown-textarea', 12 selector: 'my-markdown-textarea',
@@ -33,7 +34,7 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
33 @Input() markdownType: 'text' | 'enhanced' = 'text' 34 @Input() markdownType: 'text' | 'enhanced' = 'text'
34 @Input() customMarkdownRenderer?: (text: string) => Promise<string | HTMLElement> 35 @Input() customMarkdownRenderer?: (text: string) => Promise<string | HTMLElement>
35 36
36 @Input() markdownVideo = false 37 @Input() markdownVideo: Video
37 38
38 @Input() name = 'description' 39 @Input() name = 'description'
39 40
@@ -147,7 +148,7 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
147 } 148 }
148 149
149 if (this.markdownVideo) { 150 if (this.markdownVideo) {
150 html = this.markdownService.processVideoTimestamps(html) 151 html = this.markdownService.processVideoTimestamps(this.markdownVideo.shortUUID, html)
151 } 152 }
152 153
153 return html 154 return html
diff --git a/client/src/app/shared/shared-forms/timestamp-input.component.ts b/client/src/app/shared/shared-forms/timestamp-input.component.ts
index 0ffd03d02..3fc705905 100644
--- a/client/src/app/shared/shared-forms/timestamp-input.component.ts
+++ b/client/src/app/shared/shared-forms/timestamp-input.component.ts
@@ -1,6 +1,6 @@
1import { ChangeDetectorRef, Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core' 1import { ChangeDetectorRef, Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'
2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' 2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
3import { secondsToTime, timeToInt } from '../../../assets/player/utils' 3import { secondsToTime, timeToInt } from '@shared/core-utils'
4 4
5@Component({ 5@Component({
6 selector: 'my-timestamp-input', 6 selector: 'my-timestamp-input',
diff --git a/client/src/app/shared/shared-instance/instance-follow.service.ts b/client/src/app/shared/shared-instance/instance-follow.service.ts
index e52660140..af44020cf 100644
--- a/client/src/app/shared/shared-instance/instance-follow.service.ts
+++ b/client/src/app/shared/shared-instance/instance-follow.service.ts
@@ -4,7 +4,7 @@ import { catchError, map } from 'rxjs/operators'
4import { HttpClient, HttpParams } from '@angular/common/http' 4import { HttpClient, HttpParams } from '@angular/common/http'
5import { Injectable } from '@angular/core' 5import { Injectable } from '@angular/core'
6import { RestExtractor, RestPagination, RestService } from '@app/core' 6import { RestExtractor, RestPagination, RestService } from '@app/core'
7import { ActivityPubActorType, ActorFollow, FollowState, ResultList } from '@shared/models' 7import { ActivityPubActorType, ActorFollow, FollowState, ResultList, ServerFollowCreate } from '@shared/models'
8import { environment } from '../../../environments/environment' 8import { environment } from '../../../environments/environment'
9 9
10@Injectable() 10@Injectable()
@@ -64,9 +64,10 @@ export class InstanceFollowService {
64 ) 64 )
65 } 65 }
66 66
67 follow (notEmptyHosts: string[]) { 67 follow (hostsOrHandles: string[]) {
68 const body = { 68 const body: ServerFollowCreate = {
69 hosts: notEmptyHosts 69 handles: hostsOrHandles.filter(v => v.includes('@')),
70 hosts: hostsOrHandles.filter(v => !v.includes('@'))
70 } 71 }
71 72
72 return this.authHttp.post(InstanceFollowService.BASE_APPLICATION_URL + '/following', body) 73 return this.authHttp.post(InstanceFollowService.BASE_APPLICATION_URL + '/following', body)
@@ -77,7 +78,9 @@ export class InstanceFollowService {
77 } 78 }
78 79
79 unfollow (follow: ActorFollow) { 80 unfollow (follow: ActorFollow) {
80 return this.authHttp.delete(InstanceFollowService.BASE_APPLICATION_URL + '/following/' + follow.following.host) 81 const handle = follow.following.name + '@' + follow.following.host
82
83 return this.authHttp.delete(InstanceFollowService.BASE_APPLICATION_URL + '/following/' + handle)
81 .pipe( 84 .pipe(
82 map(this.restExtractor.extractDataBool), 85 map(this.restExtractor.extractDataBool),
83 catchError(res => this.restExtractor.handleError(res)) 86 catchError(res => this.restExtractor.handleError(res))
diff --git a/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts b/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts
index 5bcad36d0..a75c8a25c 100644
--- a/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts
+++ b/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts
@@ -1,11 +1,11 @@
1import { Observable, of, throwError as observableThrowError } from 'rxjs' 1import { Observable, of, throwError as observableThrowError } from 'rxjs'
2import { catchError, switchMap } from 'rxjs/operators' 2import { catchError, switchMap } from 'rxjs/operators'
3import { HTTP_INTERCEPTORS, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpErrorResponse } from '@angular/common/http' 3import { HTTP_INTERCEPTORS, HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'
4import { Injectable, Injector } from '@angular/core' 4import { Injectable, Injector } from '@angular/core'
5import { AuthService } from '@app/core/auth/auth.service'
6import { Router } from '@angular/router' 5import { Router } from '@angular/router'
7import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 6import { AuthService } from '@app/core/auth/auth.service'
8import { OAuth2ErrorCode, PeerTubeProblemDocument, ServerErrorCode } from '@shared/models/server' 7import { HttpStatusCode } from '@shared/models'
8import { OAuth2ErrorCode, PeerTubeProblemDocument } from '@shared/models/server'
9 9
10@Injectable() 10@Injectable()
11export class AuthInterceptor implements HttpInterceptor { 11export class AuthInterceptor implements HttpInterceptor {
diff --git a/client/src/app/shared/shared-main/users/user-notification.model.ts b/client/src/app/shared/shared-main/users/user-notification.model.ts
index 4c15eb981..439547102 100644
--- a/client/src/app/shared/shared-main/users/user-notification.model.ts
+++ b/client/src/app/shared/shared-main/users/user-notification.model.ts
@@ -47,11 +47,7 @@ export class UserNotification implements UserNotificationServer {
47 comment?: { 47 comment?: {
48 threadId: number 48 threadId: number
49 49
50 video: { 50 video: VideoInfo
51 id: number
52 uuid: string
53 name: string
54 }
55 } 51 }
56 52
57 account?: ActorInfo 53 account?: ActorInfo
diff --git a/client/src/app/shared/shared-main/users/user-notifications.component.ts b/client/src/app/shared/shared-main/users/user-notifications.component.ts
index d7c722355..96b141543 100644
--- a/client/src/app/shared/shared-main/users/user-notifications.component.ts
+++ b/client/src/app/shared/shared-main/users/user-notifications.component.ts
@@ -1,7 +1,7 @@
1import { Subject } from 'rxjs' 1import { Subject } from 'rxjs'
2import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' 2import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
3import { ComponentPagination, hasMoreItems, Notifier } from '@app/core' 3import { ComponentPagination, hasMoreItems, Notifier } from '@app/core'
4import { UserNotificationType, AbuseState } from '@shared/models' 4import { AbuseState } from '@shared/models'
5import { UserNotification } from './user-notification.model' 5import { UserNotification } from './user-notification.model'
6import { UserNotificationService } from './user-notification.service' 6import { UserNotificationService } from './user-notification.service'
7 7
diff --git a/client/src/app/shared/shared-main/video/video.model.ts b/client/src/app/shared/shared-main/video/video.model.ts
index f0a4a3f37..b7720c8d2 100644
--- a/client/src/app/shared/shared-main/video/video.model.ts
+++ b/client/src/app/shared/shared-main/video/video.model.ts
@@ -2,6 +2,7 @@ import { AuthUser } from '@app/core'
2import { User } from '@app/core/users/user.model' 2import { User } from '@app/core/users/user.model'
3import { durationToString, getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers' 3import { durationToString, getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
4import { Actor } from '@app/shared/shared-main/account/actor.model' 4import { Actor } from '@app/shared/shared-main/account/actor.model'
5import { buildVideoWatchPath } from '@shared/core-utils'
5import { peertubeTranslate } from '@shared/core-utils/i18n' 6import { peertubeTranslate } from '@shared/core-utils/i18n'
6import { 7import {
7 ActorImage, 8 ActorImage,
@@ -92,7 +93,7 @@ export class Video implements VideoServerModel {
92 pluginData?: any 93 pluginData?: any
93 94
94 static buildWatchUrl (video: Partial<Pick<Video, 'uuid' | 'shortUUID'>>) { 95 static buildWatchUrl (video: Partial<Pick<Video, 'uuid' | 'shortUUID'>>) {
95 return '/w/' + (video.shortUUID || video.uuid) 96 return buildVideoWatchPath({ shortUUID: video.shortUUID || video.uuid })
96 } 97 }
97 98
98 static buildUpdateUrl (video: Pick<Video, 'uuid'>) { 99 static buildUpdateUrl (video: Pick<Video, 'uuid'>) {
diff --git a/client/src/app/shared/shared-moderation/batch-domains-modal.component.html b/client/src/app/shared/shared-moderation/batch-domains-modal.component.html
index 6a3c65721..8306a96bc 100644
--- a/client/src/app/shared/shared-moderation/batch-domains-modal.component.html
+++ b/client/src/app/shared/shared-moderation/batch-domains-modal.component.html
@@ -1,6 +1,6 @@
1<ng-template #modal> 1<ng-template #modal>
2 <div class="modal-header"> 2 <div class="modal-header">
3 <h4 i18n class="modal-title">{{ action }}</h4> 3 <h4 class="modal-title">{{ action }}</h4>
4 4
5 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon> 5 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
6 </div> 6 </div>
@@ -11,15 +11,15 @@
11 <label i18n for="hosts">1 host (without "http://") per line</label> 11 <label i18n for="hosts">1 host (without "http://") per line</label>
12 12
13 <textarea 13 <textarea
14 [placeholder]="placeholder" formControlName="domains" type="text" id="hosts" name="hosts" 14 [placeholder]="placeholder" formControlName="hosts" type="text" id="hosts" name="hosts"
15 class="form-control" [ngClass]="{ 'input-error': formErrors['domains'] }" ngbAutofocus 15 class="form-control" [ngClass]="{ 'input-error': formErrors['hosts'] }" ngbAutofocus
16 ></textarea> 16 ></textarea>
17 17
18 <div *ngIf="formErrors.domains" class="form-error"> 18 <div *ngIf="formErrors.hosts" class="form-error">
19 {{ formErrors.domains }} 19 {{ formErrors.hosts }}
20 20
21 <div *ngIf="form.controls['domains'].errors.validDomains"> 21 <div *ngIf="form.controls['hosts'].errors.validHosts">
22 {{ form.controls['domains'].errors.validDomains.value }} 22 {{ form.controls['hosts'].errors.validHosts.value }}
23 </div> 23 </div>
24 </div> 24 </div>
25 </div> 25 </div>
diff --git a/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts b/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts
index 6edbb6023..20be728f6 100644
--- a/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts
+++ b/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts
@@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angu
2import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 2import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
3import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 3import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
4import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 4import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
5import { DOMAINS_VALIDATOR, getNotEmptyHosts } from '../form-validators/batch-domains-validators' 5import { splitAndGetNotEmpty, UNIQUE_HOSTS_VALIDATOR } from '../form-validators/host-validators'
6 6
7@Component({ 7@Component({
8 selector: 'my-batch-domains-modal', 8 selector: 'my-batch-domains-modal',
@@ -28,7 +28,7 @@ export class BatchDomainsModalComponent extends FormReactive implements OnInit {
28 if (!this.action) this.action = $localize`Process domains` 28 if (!this.action) this.action = $localize`Process domains`
29 29
30 this.buildForm({ 30 this.buildForm({
31 domains: DOMAINS_VALIDATOR 31 hosts: UNIQUE_HOSTS_VALIDATOR
32 }) 32 })
33 } 33 }
34 34
@@ -41,9 +41,7 @@ export class BatchDomainsModalComponent extends FormReactive implements OnInit {
41 } 41 }
42 42
43 submit () { 43 submit () {
44 this.domains.emit( 44 this.domains.emit(splitAndGetNotEmpty(this.form.controls['hosts'].value))
45 getNotEmptyHosts(this.form.controls['domains'].value)
46 )
47 this.form.reset() 45 this.form.reset()
48 this.hide() 46 this.hide()
49 } 47 }
diff --git a/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts b/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts
index 4ca6f52ad..e509ac88f 100644
--- a/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts
+++ b/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts
@@ -1,5 +1,5 @@
1import { mapValues, pickBy } from 'lodash-es' 1import { mapValues, pickBy } from 'lodash-es'
2import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' 2import { buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
3import { Component, Input, OnInit, ViewChild } from '@angular/core' 3import { Component, Input, OnInit, ViewChild } from '@angular/core'
4import { DomSanitizer, SafeHtml } from '@angular/platform-browser' 4import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
5import { Notifier } from '@app/core' 5import { Notifier } from '@app/core'
@@ -7,6 +7,7 @@ import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-valida
7import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 7import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
8import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 8import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
9import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 9import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
10import { decorateVideoLink } from '@shared/core-utils'
10import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' 11import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
11import { AbusePredefinedReasonsString } from '@shared/models' 12import { AbusePredefinedReasonsString } from '@shared/models'
12import { Video } from '../../shared-main' 13import { Video } from '../../shared-main'
@@ -57,11 +58,12 @@ export class VideoReportComponent extends FormReactive implements OnInit {
57 getVideoEmbed () { 58 getVideoEmbed () {
58 return this.sanitizer.bypassSecurityTrustHtml( 59 return this.sanitizer.bypassSecurityTrustHtml(
59 buildVideoOrPlaylistEmbed( 60 buildVideoOrPlaylistEmbed(
60 buildVideoLink({ 61 decorateVideoLink({
61 baseUrl: this.video.embedUrl, 62 url: this.video.embedUrl,
62 title: false, 63 title: false,
63 warningTitle: false 64 warningTitle: false
64 }), 65 }),
66
65 this.video.name 67 this.video.name
66 ) 68 )
67 ) 69 )
diff --git a/client/src/app/shared/shared-search/advanced-search.model.ts b/client/src/app/shared/shared-search/advanced-search.model.ts
index 2c83f53b6..9c55f6cd8 100644
--- a/client/src/app/shared/shared-search/advanced-search.model.ts
+++ b/client/src/app/shared/shared-search/advanced-search.model.ts
@@ -1,4 +1,11 @@
1import { BooleanBothQuery, BooleanQuery, SearchTargetType, VideosSearchQuery } from '@shared/models' 1import {
2 BooleanBothQuery,
3 BooleanQuery,
4 SearchTargetType,
5 VideoChannelsSearchQuery,
6 VideoPlaylistsSearchQuery,
7 VideosSearchQuery
8} from '@shared/models'
2 9
3export class AdvancedSearch { 10export class AdvancedSearch {
4 startDate: string // ISO 8601 11 startDate: string // ISO 8601
@@ -23,6 +30,8 @@ export class AdvancedSearch {
23 30
24 isLive: BooleanQuery 31 isLive: BooleanQuery
25 32
33 host: string
34
26 sort: string 35 sort: string
27 36
28 searchTarget: SearchTargetType 37 searchTarget: SearchTargetType
@@ -45,6 +54,8 @@ export class AdvancedSearch {
45 54
46 isLive?: BooleanQuery 55 isLive?: BooleanQuery
47 56
57 host?: string
58
48 durationMin?: string 59 durationMin?: string
49 durationMax?: string 60 durationMax?: string
50 sort?: string 61 sort?: string
@@ -68,6 +79,8 @@ export class AdvancedSearch {
68 this.durationMin = parseInt(options.durationMin, 10) 79 this.durationMin = parseInt(options.durationMin, 10)
69 this.durationMax = parseInt(options.durationMax, 10) 80 this.durationMax = parseInt(options.durationMax, 10)
70 81
82 this.host = options.host || undefined
83
71 this.searchTarget = options.searchTarget || undefined 84 this.searchTarget = options.searchTarget || undefined
72 85
73 if (isNaN(this.durationMin)) this.durationMin = undefined 86 if (isNaN(this.durationMin)) this.durationMin = undefined
@@ -101,6 +114,7 @@ export class AdvancedSearch {
101 this.durationMin = undefined 114 this.durationMin = undefined
102 this.durationMax = undefined 115 this.durationMax = undefined
103 this.isLive = undefined 116 this.isLive = undefined
117 this.host = undefined
104 118
105 this.sort = '-match' 119 this.sort = '-match'
106 } 120 }
@@ -120,12 +134,13 @@ export class AdvancedSearch {
120 durationMin: this.durationMin, 134 durationMin: this.durationMin,
121 durationMax: this.durationMax, 135 durationMax: this.durationMax,
122 isLive: this.isLive, 136 isLive: this.isLive,
137 host: this.host,
123 sort: this.sort, 138 sort: this.sort,
124 searchTarget: this.searchTarget 139 searchTarget: this.searchTarget
125 } 140 }
126 } 141 }
127 142
128 toAPIObject (): VideosSearchQuery { 143 toVideosAPIObject (): VideosSearchQuery {
129 let isLive: boolean 144 let isLive: boolean
130 if (this.isLive) isLive = this.isLive === 'true' 145 if (this.isLive) isLive = this.isLive === 'true'
131 146
@@ -142,12 +157,27 @@ export class AdvancedSearch {
142 tagsAllOf: this.tagsAllOf, 157 tagsAllOf: this.tagsAllOf,
143 durationMin: this.durationMin, 158 durationMin: this.durationMin,
144 durationMax: this.durationMax, 159 durationMax: this.durationMax,
160 host: this.host,
145 isLive, 161 isLive,
146 sort: this.sort, 162 sort: this.sort,
147 searchTarget: this.searchTarget 163 searchTarget: this.searchTarget
148 } 164 }
149 } 165 }
150 166
167 toPlaylistAPIObject (): VideoPlaylistsSearchQuery {
168 return {
169 host: this.host,
170 searchTarget: this.searchTarget
171 }
172 }
173
174 toChannelAPIObject (): VideoChannelsSearchQuery {
175 return {
176 host: this.host,
177 searchTarget: this.searchTarget
178 }
179 }
180
151 size () { 181 size () {
152 let acc = 0 182 let acc = 0
153 183
diff --git a/client/src/app/shared/shared-search/search.service.ts b/client/src/app/shared/shared-search/search.service.ts
index ad258f5e5..a1603da98 100644
--- a/client/src/app/shared/shared-search/search.service.ts
+++ b/client/src/app/shared/shared-search/search.service.ts
@@ -7,7 +7,6 @@ import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/sha
7import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 7import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
8import { 8import {
9 ResultList, 9 ResultList,
10 SearchTargetType,
11 Video as VideoServerModel, 10 Video as VideoServerModel,
12 VideoChannel as VideoChannelServerModel, 11 VideoChannel as VideoChannelServerModel,
13 VideoPlaylist as VideoPlaylistServerModel 12 VideoPlaylist as VideoPlaylistServerModel
@@ -33,8 +32,8 @@ export class SearchService {
33 } 32 }
34 33
35 searchVideos (parameters: { 34 searchVideos (parameters: {
36 search: string, 35 search: string
37 componentPagination?: ComponentPaginationLight, 36 componentPagination?: ComponentPaginationLight
38 advancedSearch?: AdvancedSearch 37 advancedSearch?: AdvancedSearch
39 }): Observable<ResultList<Video>> { 38 }): Observable<ResultList<Video>> {
40 const { search, componentPagination, advancedSearch } = parameters 39 const { search, componentPagination, advancedSearch } = parameters
@@ -52,7 +51,7 @@ export class SearchService {
52 if (search) params = params.append('search', search) 51 if (search) params = params.append('search', search)
53 52
54 if (advancedSearch) { 53 if (advancedSearch) {
55 const advancedSearchObject = advancedSearch.toAPIObject() 54 const advancedSearchObject = advancedSearch.toVideosAPIObject()
56 params = this.restService.addObjectParams(params, advancedSearchObject) 55 params = this.restService.addObjectParams(params, advancedSearchObject)
57 } 56 }
58 57
@@ -65,11 +64,11 @@ export class SearchService {
65 } 64 }
66 65
67 searchVideoChannels (parameters: { 66 searchVideoChannels (parameters: {
68 search: string, 67 search: string
69 searchTarget?: SearchTargetType, 68 advancedSearch?: AdvancedSearch
70 componentPagination?: ComponentPaginationLight 69 componentPagination?: ComponentPaginationLight
71 }): Observable<ResultList<VideoChannel>> { 70 }): Observable<ResultList<VideoChannel>> {
72 const { search, componentPagination, searchTarget } = parameters 71 const { search, advancedSearch, componentPagination } = parameters
73 72
74 const url = SearchService.BASE_SEARCH_URL + 'video-channels' 73 const url = SearchService.BASE_SEARCH_URL + 'video-channels'
75 74
@@ -80,10 +79,12 @@ export class SearchService {
80 79
81 let params = new HttpParams() 80 let params = new HttpParams()
82 params = this.restService.addRestGetParams(params, pagination) 81 params = this.restService.addRestGetParams(params, pagination)
83 params = params.append('search', search)
84 82
85 if (searchTarget) { 83 if (search) params = params.append('search', search)
86 params = params.append('searchTarget', searchTarget as string) 84
85 if (advancedSearch) {
86 const advancedSearchObject = advancedSearch.toChannelAPIObject()
87 params = this.restService.addObjectParams(params, advancedSearchObject)
87 } 88 }
88 89
89 return this.authHttp 90 return this.authHttp
@@ -95,11 +96,11 @@ export class SearchService {
95 } 96 }
96 97
97 searchVideoPlaylists (parameters: { 98 searchVideoPlaylists (parameters: {
98 search: string, 99 search: string
99 searchTarget?: SearchTargetType, 100 advancedSearch?: AdvancedSearch
100 componentPagination?: ComponentPaginationLight 101 componentPagination?: ComponentPaginationLight
101 }): Observable<ResultList<VideoPlaylist>> { 102 }): Observable<ResultList<VideoPlaylist>> {
102 const { search, componentPagination, searchTarget } = parameters 103 const { search, advancedSearch, componentPagination } = parameters
103 104
104 const url = SearchService.BASE_SEARCH_URL + 'video-playlists' 105 const url = SearchService.BASE_SEARCH_URL + 'video-playlists'
105 106
@@ -110,10 +111,12 @@ export class SearchService {
110 111
111 let params = new HttpParams() 112 let params = new HttpParams()
112 params = this.restService.addRestGetParams(params, pagination) 113 params = this.restService.addRestGetParams(params, pagination)
113 params = params.append('search', search)
114 114
115 if (searchTarget) { 115 if (search) params = params.append('search', search)
116 params = params.append('searchTarget', searchTarget as string) 116
117 if (advancedSearch) {
118 const advancedSearchObject = advancedSearch.toPlaylistAPIObject()
119 params = this.restService.addObjectParams(params, advancedSearchObject)
117 } 120 }
118 121
119 return this.authHttp 122 return this.authHttp
diff --git a/client/src/app/shared/shared-share-modal/video-share.component.ts b/client/src/app/shared/shared-share-modal/video-share.component.ts
index a41ff248b..341abdc2b 100644
--- a/client/src/app/shared/shared-share-modal/video-share.component.ts
+++ b/client/src/app/shared/shared-share-modal/video-share.component.ts
@@ -1,9 +1,10 @@
1import { Component, ElementRef, Input, ViewChild } from '@angular/core' 1import { Component, ElementRef, Input, ViewChild } from '@angular/core'
2import { Video, VideoDetails } from '@app/shared/shared-main' 2import { VideoDetails } from '@app/shared/shared-main'
3import { VideoPlaylist } from '@app/shared/shared-video-playlist' 3import { VideoPlaylist } from '@app/shared/shared-video-playlist'
4import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 4import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
5import { buildPlaylistLink, buildVideoLink, decoratePlaylistLink, decorateVideoLink } from '@shared/core-utils'
5import { VideoCaption } from '@shared/models' 6import { VideoCaption } from '@shared/models'
6import { buildPlaylistLink, buildVideoLink, buildVideoOrPlaylistEmbed } from '../../../assets/player/utils' 7import { buildVideoOrPlaylistEmbed } from '../../../assets/player/utils'
7 8
8type Customizations = { 9type Customizations = {
9 startAtCheckbox: boolean 10 startAtCheckbox: boolean
@@ -83,34 +84,34 @@ export class VideoShareComponent {
83 } 84 }
84 85
85 getVideoIframeCode () { 86 getVideoIframeCode () {
86 const options = this.getVideoOptions(this.video.embedUrl) 87 const embedUrl = decorateVideoLink({ url: this.video.embedUrl, ...this.getVideoOptions() })
87 88
88 const embedUrl = buildVideoLink(options)
89 return buildVideoOrPlaylistEmbed(embedUrl, this.video.name) 89 return buildVideoOrPlaylistEmbed(embedUrl, this.video.name)
90 } 90 }
91 91
92 getPlaylistIframeCode () { 92 getPlaylistIframeCode () {
93 const options = this.getPlaylistOptions(this.playlist.embedUrl) 93 const embedUrl = decoratePlaylistLink({ url: this.playlist.embedUrl, ...this.getPlaylistOptions() })
94 94
95 const embedUrl = buildPlaylistLink(options)
96 return buildVideoOrPlaylistEmbed(embedUrl, this.playlist.displayName) 95 return buildVideoOrPlaylistEmbed(embedUrl, this.playlist.displayName)
97 } 96 }
98 97
99 getVideoUrl () { 98 getVideoUrl () {
100 let baseUrl = this.customizations.originUrl ? this.video.originInstanceUrl : window.location.origin 99 const baseUrl = this.customizations.originUrl
101 baseUrl += Video.buildWatchUrl(this.video) 100 ? this.video.originInstanceUrl
101 : window.location.origin
102 102
103 const options = this.getVideoOptions(baseUrl) 103 return decorateVideoLink({
104 url: buildVideoLink(this.video, baseUrl),
104 105
105 return buildVideoLink(options) 106 ...this.getVideoOptions()
107 })
106 } 108 }
107 109
108 getPlaylistUrl () { 110 getPlaylistUrl () {
109 const base = window.location.origin + VideoPlaylist.buildWatchUrl(this.playlist) 111 const url = buildPlaylistLink(this.playlist)
112 if (!this.includeVideoInPlaylist) return url
110 113
111 if (!this.includeVideoInPlaylist) return base 114 return decoratePlaylistLink({ url, playlistPosition: this.playlistPosition })
112
113 return base + '?playlistPosition=' + this.playlistPosition
114 } 115 }
115 116
116 notSecure () { 117 notSecure () {
@@ -133,10 +134,8 @@ export class VideoShareComponent {
133 } 134 }
134 } 135 }
135 136
136 private getVideoOptions (baseUrl?: string) { 137 private getVideoOptions () {
137 return { 138 return {
138 baseUrl,
139
140 startTime: this.customizations.startAtCheckbox ? this.customizations.startAt : undefined, 139 startTime: this.customizations.startAtCheckbox ? this.customizations.startAt : undefined,
141 stopTime: this.customizations.stopAtCheckbox ? this.customizations.stopAt : undefined, 140 stopTime: this.customizations.stopAtCheckbox ? this.customizations.stopAt : undefined,
142 141
diff --git a/client/src/app/shared/shared-video-miniature/abstract-video-list.ts b/client/src/app/shared/shared-video-miniature/abstract-video-list.ts
index 52e72d35b..33061a837 100644
--- a/client/src/app/shared/shared-video-miniature/abstract-video-list.ts
+++ b/client/src/app/shared/shared-video-miniature/abstract-video-list.ts
@@ -24,7 +24,7 @@ import {
24} from '@app/core' 24} from '@app/core'
25import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' 25import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
26import { GlobalIconName } from '@app/shared/shared-icons' 26import { GlobalIconName } from '@app/shared/shared-icons'
27import { isLastMonth, isLastWeek, isThisMonth, isToday, isYesterday } from '@shared/core-utils/miscs/date' 27import { isLastMonth, isLastWeek, isThisMonth, isToday, isYesterday } from '@shared/core-utils'
28import { HTMLServerConfig, UserRight, VideoFilter, VideoSortField } from '@shared/models' 28import { HTMLServerConfig, UserRight, VideoFilter, VideoSortField } from '@shared/models'
29import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type' 29import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type'
30import { Syndication, Video } from '../shared-main' 30import { Syndication, Video } from '../shared-main'
diff --git a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
index 681e5becd..8b019103c 100644
--- a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
+++ b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
@@ -4,6 +4,7 @@ import { debounceTime, filter } from 'rxjs/operators'
4import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core' 4import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'
5import { AuthService, DisableForReuseHook, Notifier } from '@app/core' 5import { AuthService, DisableForReuseHook, Notifier } from '@app/core'
6import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 6import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
7import { secondsToTime } from '@shared/core-utils'
7import { 8import {
8 Video, 9 Video,
9 VideoExistInPlaylist, 10 VideoExistInPlaylist,
@@ -12,7 +13,6 @@ import {
12 VideoPlaylistElementUpdate, 13 VideoPlaylistElementUpdate,
13 VideoPlaylistPrivacy 14 VideoPlaylistPrivacy
14} from '@shared/models' 15} from '@shared/models'
15import { secondsToTime } from '../../../assets/player/utils'
16import { VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR } from '../form-validators/video-playlist-validators' 16import { VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR } from '../form-validators/video-playlist-validators'
17import { CachedPlaylist, VideoPlaylistService } from './video-playlist.service' 17import { CachedPlaylist, VideoPlaylistService } from './video-playlist.service'
18 18
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts
index d99170e4e..2e495ec26 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts
+++ b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts
@@ -2,8 +2,8 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, In
2import { AuthService, Notifier, ServerService } from '@app/core' 2import { AuthService, Notifier, ServerService } from '@app/core'
3import { Video } from '@app/shared/shared-main' 3import { Video } from '@app/shared/shared-main'
4import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap' 4import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
5import { secondsToTime } from '@shared/core-utils'
5import { HTMLServerConfig, VideoPlaylistElementType, VideoPlaylistElementUpdate } from '@shared/models' 6import { HTMLServerConfig, VideoPlaylistElementType, VideoPlaylistElementUpdate } from '@shared/models'
6import { secondsToTime } from '../../../assets/player/utils'
7import { VideoPlaylistElement } from './video-playlist-element.model' 7import { VideoPlaylistElement } from './video-playlist-element.model'
8import { VideoPlaylist } from './video-playlist.model' 8import { VideoPlaylist } from './video-playlist.model'
9import { VideoPlaylistService } from './video-playlist.service' 9import { VideoPlaylistService } from './video-playlist.service'
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist.model.ts b/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
index 55013e4c5..fcc2ce705 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
+++ b/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
@@ -1,5 +1,6 @@
1import { getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers' 1import { getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
2import { Actor } from '@app/shared/shared-main' 2import { Actor } from '@app/shared/shared-main'
3import { buildPlaylistWatchPath } from '@shared/core-utils'
3import { peertubeTranslate } from '@shared/core-utils/i18n' 4import { peertubeTranslate } from '@shared/core-utils/i18n'
4import { 5import {
5 AccountSummary, 6 AccountSummary,
@@ -44,7 +45,7 @@ export class VideoPlaylist implements ServerVideoPlaylist {
44 videoChannelBy?: string 45 videoChannelBy?: string
45 46
46 static buildWatchUrl (playlist: Pick<VideoPlaylist, 'uuid' | 'shortUUID'>) { 47 static buildWatchUrl (playlist: Pick<VideoPlaylist, 'uuid' | 'shortUUID'>) {
47 return '/w/p/' + (playlist.shortUUID || playlist.uuid) 48 return buildPlaylistWatchPath({ shortUUID: playlist.shortUUID || playlist.uuid })
48 } 49 }
49 50
50 constructor (hash: ServerVideoPlaylist, translations: {}) { 51 constructor (hash: ServerVideoPlaylist, translations: {}) {
diff --git a/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts b/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts
index f1bd9f0c4..2eb849d2b 100644
--- a/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts
+++ b/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts
@@ -2,8 +2,8 @@ import * as Hlsjs from 'hls.js/dist/hls.light.js'
2import { Events, Segment } from 'p2p-media-loader-core' 2import { Events, Segment } from 'p2p-media-loader-core'
3import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs' 3import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs'
4import videojs from 'video.js' 4import videojs from 'video.js'
5import { timeToInt } from '@shared/core-utils'
5import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../peertube-videojs-typings' 6import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../peertube-videojs-typings'
6import { timeToInt } from '../utils'
7import { registerConfigPlugin, registerSourceHandler } from './hls-plugin' 7import { registerConfigPlugin, registerSourceHandler } from './hls-plugin'
8 8
9registerConfigPlugin(videojs) 9registerConfigPlugin(videojs)
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts
index b071a0938..766ad203e 100644
--- a/client/src/assets/player/peertube-player-manager.ts
+++ b/client/src/assets/player/peertube-player-manager.ts
@@ -23,6 +23,7 @@ import './videojs-components/theater-button'
23import './playlist/playlist-plugin' 23import './playlist/playlist-plugin'
24import videojs from 'video.js' 24import videojs from 'video.js'
25import { PluginsManager } from '@root-helpers/plugins-manager' 25import { PluginsManager } from '@root-helpers/plugins-manager'
26import { buildVideoLink, decorateVideoLink } from '@shared/core-utils'
26import { isDefaultLocale } from '@shared/core-utils/i18n' 27import { isDefaultLocale } from '@shared/core-utils/i18n'
27import { VideoFile } from '@shared/models' 28import { VideoFile } from '@shared/models'
28import { copyToClipboard } from '../../root-helpers/utils' 29import { copyToClipboard } from '../../root-helpers/utils'
@@ -33,13 +34,14 @@ import { getStoredP2PEnabled } from './peertube-player-local-storage'
33import { 34import {
34 NextPreviousVideoButtonOptions, 35 NextPreviousVideoButtonOptions,
35 P2PMediaLoaderPluginOptions, 36 P2PMediaLoaderPluginOptions,
37 PeerTubeLinkButtonOptions,
36 PlaylistPluginOptions, 38 PlaylistPluginOptions,
37 UserWatching, 39 UserWatching,
38 VideoJSCaption, 40 VideoJSCaption,
39 VideoJSPluginOptions 41 VideoJSPluginOptions
40} from './peertube-videojs-typings' 42} from './peertube-videojs-typings'
41import { TranslationsManager } from './translations-manager' 43import { TranslationsManager } from './translations-manager'
42import { buildVideoLink, buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isSafari } from './utils' 44import { buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isSafari } from './utils'
43 45
44// Change 'Playback Rate' to 'Speed' (smaller for our settings menu) 46// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
45(videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed' 47(videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed'
@@ -110,6 +112,7 @@ export interface CommonOptions extends CustomizationOptions {
110 videoCaptions: VideoJSCaption[] 112 videoCaptions: VideoJSCaption[]
111 113
112 videoUUID: string 114 videoUUID: string
115 videoShortUUID: string
113 116
114 userWatching?: UserWatching 117 userWatching?: UserWatching
115 118
@@ -175,7 +178,13 @@ export class PeertubePlayerManager {
175 PeertubePlayerManager.alreadyPlayed = true 178 PeertubePlayerManager.alreadyPlayed = true
176 }) 179 })
177 180
178 self.addContextMenu(mode, player, options.common.embedUrl, options.common.embedTitle) 181 self.addContextMenu({
182 mode,
183 player,
184 videoShortUUID: options.common.videoShortUUID,
185 videoEmbedUrl: options.common.embedUrl,
186 videoEmbedTitle: options.common.embedTitle
187 })
179 188
180 player.bezels() 189 player.bezels()
181 player.stats({ 190 player.stats({
@@ -218,7 +227,13 @@ export class PeertubePlayerManager {
218 videojs(newVideoElement, videojsOptions, function (this: videojs.Player) { 227 videojs(newVideoElement, videojsOptions, function (this: videojs.Player) {
219 const player = this 228 const player = this
220 229
221 self.addContextMenu(mode, player, options.common.embedUrl, options.common.embedTitle) 230 self.addContextMenu({
231 mode,
232 player,
233 videoShortUUID: options.common.videoShortUUID,
234 videoEmbedUrl: options.common.embedUrl,
235 videoEmbedTitle: options.common.embedTitle
236 })
222 237
223 PeertubePlayerManager.onPlayerChange(player) 238 PeertubePlayerManager.onPlayerChange(player)
224 }) 239 })
@@ -295,6 +310,8 @@ export class PeertubePlayerManager {
295 310
296 controlBar: { 311 controlBar: {
297 children: this.getControlBarChildren(mode, { 312 children: this.getControlBarChildren(mode, {
313 videoShortUUID: commonOptions.videoShortUUID,
314
298 captions: commonOptions.captions, 315 captions: commonOptions.captions,
299 peertubeLink: commonOptions.peertubeLink, 316 peertubeLink: commonOptions.peertubeLink,
300 theaterButton: commonOptions.theaterButton, 317 theaterButton: commonOptions.theaterButton,
@@ -409,6 +426,8 @@ export class PeertubePlayerManager {
409 } 426 }
410 427
411 private static getControlBarChildren (mode: PlayerMode, options: { 428 private static getControlBarChildren (mode: PlayerMode, options: {
429 videoShortUUID: string
430
412 peertubeLink: boolean 431 peertubeLink: boolean
413 theaterButton: boolean 432 theaterButton: boolean
414 captions: boolean 433 captions: boolean
@@ -497,7 +516,7 @@ export class PeertubePlayerManager {
497 516
498 if (options.peertubeLink === true) { 517 if (options.peertubeLink === true) {
499 Object.assign(children, { 518 Object.assign(children, {
500 'peerTubeLinkButton': {} 519 'peerTubeLinkButton': { shortUUID: options.videoShortUUID } as PeerTubeLinkButtonOptions
501 }) 520 })
502 } 521 }
503 522
@@ -514,7 +533,15 @@ export class PeertubePlayerManager {
514 return children 533 return children
515 } 534 }
516 535
517 private static addContextMenu (mode: PlayerMode, player: videojs.Player, videoEmbedUrl: string, videoEmbedTitle: string) { 536 private static addContextMenu (options: {
537 mode: PlayerMode
538 player: videojs.Player
539 videoShortUUID: string
540 videoEmbedUrl: string
541 videoEmbedTitle: string
542 }) {
543 const { mode, player, videoEmbedTitle, videoEmbedUrl, videoShortUUID } = options
544
518 const content = () => { 545 const content = () => {
519 const isLoopEnabled = player.options_['loop'] 546 const isLoopEnabled = player.options_['loop']
520 const items = [ 547 const items = [
@@ -528,13 +555,15 @@ export class PeertubePlayerManager {
528 { 555 {
529 label: player.localize('Copy the video URL'), 556 label: player.localize('Copy the video URL'),
530 listener: function () { 557 listener: function () {
531 copyToClipboard(buildVideoLink()) 558 copyToClipboard(buildVideoLink({ shortUUID: videoShortUUID }))
532 } 559 }
533 }, 560 },
534 { 561 {
535 label: player.localize('Copy the video URL at the current time'), 562 label: player.localize('Copy the video URL at the current time'),
536 listener: function (this: videojs.Player) { 563 listener: function (this: videojs.Player) {
537 copyToClipboard(buildVideoLink({ startTime: this.currentTime() })) 564 const url = buildVideoLink({ shortUUID: videoShortUUID })
565
566 copyToClipboard(decorateVideoLink({ url, startTime: this.currentTime() }))
538 } 567 }
539 }, 568 },
540 { 569 {
diff --git a/client/src/assets/player/peertube-plugin.ts b/client/src/assets/player/peertube-plugin.ts
index 07c7e33f6..919b7c239 100644
--- a/client/src/assets/player/peertube-plugin.ts
+++ b/client/src/assets/player/peertube-plugin.ts
@@ -1,12 +1,6 @@
1import videojs from 'video.js'
2import './videojs-components/settings-menu-button' 1import './videojs-components/settings-menu-button'
3import { 2import videojs from 'video.js'
4 PeerTubePluginOptions, 3import { timeToInt } from '@shared/core-utils'
5 ResolutionUpdateData,
6 UserWatching,
7 VideoJSCaption
8} from './peertube-videojs-typings'
9import { isMobile, timeToInt } from './utils'
10import { 4import {
11 getStoredLastSubtitle, 5 getStoredLastSubtitle,
12 getStoredMute, 6 getStoredMute,
@@ -16,6 +10,8 @@ import {
16 saveVideoWatchHistory, 10 saveVideoWatchHistory,
17 saveVolumeInStore 11 saveVolumeInStore
18} from './peertube-player-local-storage' 12} from './peertube-player-local-storage'
13import { PeerTubePluginOptions, ResolutionUpdateData, UserWatching, VideoJSCaption } from './peertube-videojs-typings'
14import { isMobile } from './utils'
19 15
20const Plugin = videojs.getPlugin('plugin') 16const Plugin = videojs.getPlugin('plugin')
21 17
diff --git a/client/src/assets/player/peertube-videojs-typings.ts b/client/src/assets/player/peertube-videojs-typings.ts
index 8afb424a7..d3c75990b 100644
--- a/client/src/assets/player/peertube-videojs-typings.ts
+++ b/client/src/assets/player/peertube-videojs-typings.ts
@@ -132,6 +132,10 @@ type NextPreviousVideoButtonOptions = {
132 isDisabled: () => boolean 132 isDisabled: () => boolean
133} 133}
134 134
135type PeerTubeLinkButtonOptions = {
136 shortUUID: string
137}
138
135type WebtorrentPluginOptions = { 139type WebtorrentPluginOptions = {
136 playerElement: HTMLVideoElement 140 playerElement: HTMLVideoElement
137 141
@@ -225,5 +229,6 @@ export {
225 VideoJSPluginOptions, 229 VideoJSPluginOptions,
226 LoadedQualityData, 230 LoadedQualityData,
227 QualityLevelRepresentation, 231 QualityLevelRepresentation,
232 PeerTubeLinkButtonOptions,
228 QualityLevels 233 QualityLevels
229} 234}
diff --git a/client/src/assets/player/playlist/playlist-menu-item.ts b/client/src/assets/player/playlist/playlist-menu-item.ts
index 87a72b6a3..2519a34c7 100644
--- a/client/src/assets/player/playlist/playlist-menu-item.ts
+++ b/client/src/assets/player/playlist/playlist-menu-item.ts
@@ -1,7 +1,7 @@
1import videojs from 'video.js' 1import videojs from 'video.js'
2import { secondsToTime } from '@shared/core-utils'
2import { VideoPlaylistElement } from '@shared/models' 3import { VideoPlaylistElement } from '@shared/models'
3import { PlaylistItemOptions } from '../peertube-videojs-typings' 4import { PlaylistItemOptions } from '../peertube-videojs-typings'
4import { secondsToTime } from '../utils'
5 5
6const Component = videojs.getComponent('Component') 6const Component = videojs.getComponent('Component')
7 7
diff --git a/client/src/assets/player/stats/stats-card.ts b/client/src/assets/player/stats/stats-card.ts
index a93f59506..b271d0526 100644
--- a/client/src/assets/player/stats/stats-card.ts
+++ b/client/src/assets/player/stats/stats-card.ts
@@ -1,6 +1,7 @@
1import videojs from 'video.js' 1import videojs from 'video.js'
2import { secondsToTime } from '@shared/core-utils'
2import { PlayerNetworkInfo as EventPlayerNetworkInfo } from '../peertube-videojs-typings' 3import { PlayerNetworkInfo as EventPlayerNetworkInfo } from '../peertube-videojs-typings'
3import { bytes, secondsToTime } from '../utils' 4import { bytes } from '../utils'
4 5
5interface StatsCardOptions extends videojs.ComponentOptions { 6interface StatsCardOptions extends videojs.ComponentOptions {
6 videoUUID: string 7 videoUUID: string
diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts
index f26176acc..f0a1b1aee 100644
--- a/client/src/assets/player/utils.ts
+++ b/client/src/assets/player/utils.ts
@@ -1,5 +1,5 @@
1import { VideoFile } from '@shared/models'
2import { escapeHTML } from '@shared/core-utils/renderer' 1import { escapeHTML } from '@shared/core-utils/renderer'
2import { VideoFile } from '@shared/models'
3 3
4function toTitleCase (str: string) { 4function toTitleCase (str: string) {
5 return str.charAt(0).toUpperCase() + str.slice(1) 5 return str.charAt(0).toUpperCase() + str.slice(1)
@@ -43,136 +43,9 @@ function isMobile () {
43 return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) 43 return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
44} 44}
45 45
46function buildVideoLink (options: {
47 baseUrl?: string
48
49 startTime?: number
50 stopTime?: number
51
52 subtitle?: string
53
54 loop?: boolean
55 autoplay?: boolean
56 muted?: boolean
57
58 // Embed options
59 title?: boolean
60 warningTitle?: boolean
61 controls?: boolean
62 peertubeLink?: boolean
63} = {}) {
64 const { baseUrl } = options
65
66 const url = baseUrl
67 ? baseUrl
68 : window.location.origin + window.location.pathname.replace('/videos/embed/', '/w/')
69
70 const params = generateParams(window.location.search)
71
72 if (options.startTime !== undefined && options.startTime !== null) {
73 const startTimeInt = Math.floor(options.startTime)
74 params.set('start', secondsToTime(startTimeInt))
75 }
76
77 if (options.stopTime) {
78 const stopTimeInt = Math.floor(options.stopTime)
79 params.set('stop', secondsToTime(stopTimeInt))
80 }
81
82 if (options.subtitle) params.set('subtitle', options.subtitle)
83
84 if (options.loop === true) params.set('loop', '1')
85 if (options.autoplay === true) params.set('autoplay', '1')
86 if (options.muted === true) params.set('muted', '1')
87 if (options.title === false) params.set('title', '0')
88 if (options.warningTitle === false) params.set('warningTitle', '0')
89 if (options.controls === false) params.set('controls', '0')
90 if (options.peertubeLink === false) params.set('peertubeLink', '0')
91
92 return buildUrl(url, params)
93}
94
95function buildPlaylistLink (options: {
96 baseUrl?: string
97
98 playlistPosition?: number
99}) {
100 const { baseUrl } = options
101
102 const url = baseUrl
103 ? baseUrl
104 : window.location.origin + window.location.pathname.replace('/video-playlists/embed/', '/w/p/')
105
106 const params = generateParams(window.location.search)
107
108 if (options.playlistPosition) params.set('playlistPosition', '' + options.playlistPosition)
109
110 return buildUrl(url, params)
111}
112
113function buildUrl (url: string, params: URLSearchParams) {
114 let hasParams = false
115 params.forEach(() => hasParams = true)
116
117 if (hasParams) return url + '?' + params.toString()
118
119 return url
120}
121
122function generateParams (url: string) {
123 const params = new URLSearchParams(window.location.search)
124 // Unused parameters in embed
125 params.delete('videoId')
126 params.delete('resume')
127
128 return params
129}
130
131function timeToInt (time: number | string) {
132 if (!time) return 0
133 if (typeof time === 'number') return time
134
135 const reg = /^((\d+)[h:])?((\d+)[m:])?((\d+)s?)?$/
136 const matches = time.match(reg)
137
138 if (!matches) return 0
139
140 const hours = parseInt(matches[2] || '0', 10)
141 const minutes = parseInt(matches[4] || '0', 10)
142 const seconds = parseInt(matches[6] || '0', 10)
143
144 return hours * 3600 + minutes * 60 + seconds
145}
146
147function secondsToTime (seconds: number, full = false, symbol?: string) {
148 let time = ''
149
150 if (seconds === 0 && !full) return '0s'
151
152 const hourSymbol = (symbol || 'h')
153 const minuteSymbol = (symbol || 'm')
154 const secondsSymbol = full ? '' : 's'
155
156 const hours = Math.floor(seconds / 3600)
157 if (hours >= 1) time = hours + hourSymbol
158 else if (full) time = '0' + hourSymbol
159
160 seconds %= 3600
161 const minutes = Math.floor(seconds / 60)
162 if (minutes >= 1 && minutes < 10 && full) time += '0' + minutes + minuteSymbol
163 else if (minutes >= 1) time += minutes + minuteSymbol
164 else if (full) time += '00' + minuteSymbol
165
166 seconds %= 60
167 if (seconds >= 1 && seconds < 10 && full) time += '0' + seconds + secondsSymbol
168 else if (seconds >= 1) time += seconds + secondsSymbol
169 else if (full) time += '00'
170
171 return time
172}
173
174function buildVideoOrPlaylistEmbed (embedUrl: string, embedTitle: string) { 46function buildVideoOrPlaylistEmbed (embedUrl: string, embedTitle: string) {
175 const title = escapeHTML(embedTitle) 47 const title = escapeHTML(embedTitle)
48
176 return '<iframe width="560" height="315" ' + 49 return '<iframe width="560" height="315" ' +
177 'sandbox="allow-same-origin allow-scripts allow-popups" ' + 50 'sandbox="allow-same-origin allow-scripts allow-popups" ' +
178 'title="' + title + '" ' + 51 'title="' + title + '" ' +
@@ -221,11 +94,8 @@ function getRtcConfig () {
221export { 94export {
222 getRtcConfig, 95 getRtcConfig,
223 toTitleCase, 96 toTitleCase,
224 timeToInt,
225 secondsToTime,
226 isWebRTCDisabled, 97 isWebRTCDisabled,
227 buildPlaylistLink, 98
228 buildVideoLink,
229 buildVideoOrPlaylistEmbed, 99 buildVideoOrPlaylistEmbed,
230 videoFileMaxByResolution, 100 videoFileMaxByResolution,
231 videoFileMinByResolution, 101 videoFileMinByResolution,
diff --git a/client/src/assets/player/videojs-components/peertube-link-button.ts b/client/src/assets/player/videojs-components/peertube-link-button.ts
index e73c95900..98434898c 100644
--- a/client/src/assets/player/videojs-components/peertube-link-button.ts
+++ b/client/src/assets/player/videojs-components/peertube-link-button.ts
@@ -1,11 +1,15 @@
1import { buildVideoLink } from '../utils'
2import videojs from 'video.js' 1import videojs from 'video.js'
2import { buildVideoLink, decorateVideoLink } from '@shared/core-utils'
3import { PeerTubeLinkButtonOptions } from '../peertube-videojs-typings'
3 4
4const Button = videojs.getComponent('Button') 5const Button = videojs.getComponent('Button')
5class PeerTubeLinkButton extends Button { 6class PeerTubeLinkButton extends Button {
7 private shortUUID: string
6 8
7 constructor (player: videojs.Player, options?: videojs.ComponentOptions) { 9 constructor (player: videojs.Player, options?: PeerTubeLinkButtonOptions) {
8 super(player, options) 10 super(player, options as any)
11
12 this.shortUUID = options.shortUUID
9 } 13 }
10 14
11 createEl () { 15 createEl () {
@@ -13,7 +17,7 @@ class PeerTubeLinkButton extends Button {
13 } 17 }
14 18
15 updateHref () { 19 updateHref () {
16 this.el().setAttribute('href', buildVideoLink({ startTime: this.player().currentTime() })) 20 this.el().setAttribute('href', this.buildLink())
17 } 21 }
18 22
19 handleClick () { 23 handleClick () {
@@ -22,7 +26,7 @@ class PeerTubeLinkButton extends Button {
22 26
23 private buildElement () { 27 private buildElement () {
24 const el = videojs.dom.createEl('a', { 28 const el = videojs.dom.createEl('a', {
25 href: buildVideoLink(), 29 href: this.buildLink(),
26 innerHTML: 'PeerTube', 30 innerHTML: 'PeerTube',
27 title: this.player().localize('Video page (new window)'), 31 title: this.player().localize('Video page (new window)'),
28 className: 'vjs-peertube-link', 32 className: 'vjs-peertube-link',
@@ -33,6 +37,12 @@ class PeerTubeLinkButton extends Button {
33 37
34 return el as HTMLButtonElement 38 return el as HTMLButtonElement
35 } 39 }
40
41 private buildLink () {
42 const url = buildVideoLink({ shortUUID: this.shortUUID })
43
44 return decorateVideoLink({ url, startTime: this.player().currentTime() })
45 }
36} 46}
37 47
38videojs.registerComponent('PeerTubeLinkButton', PeerTubeLinkButton) 48videojs.registerComponent('PeerTubeLinkButton', PeerTubeLinkButton)
diff --git a/client/src/assets/player/webtorrent/webtorrent-plugin.ts b/client/src/assets/player/webtorrent/webtorrent-plugin.ts
index b648b29e8..17d369c10 100644
--- a/client/src/assets/player/webtorrent/webtorrent-plugin.ts
+++ b/client/src/assets/player/webtorrent/webtorrent-plugin.ts
@@ -1,9 +1,7 @@
1import videojs from 'video.js' 1import videojs from 'video.js'
2import * as WebTorrent from 'webtorrent' 2import * as WebTorrent from 'webtorrent'
3import { renderVideo } from './video-renderer' 3import { timeToInt } from '@shared/core-utils'
4import { LoadedQualityData, PlayerNetworkInfo, WebtorrentPluginOptions } from '../peertube-videojs-typings' 4import { VideoFile } from '@shared/models'
5import { getRtcConfig, timeToInt, videoFileMaxByResolution, videoFileMinByResolution, isIOS, isSafari } from '../utils'
6import { PeertubeChunkStore } from './peertube-chunk-store'
7import { 5import {
8 getAverageBandwidthInStore, 6 getAverageBandwidthInStore,
9 getStoredMute, 7 getStoredMute,
@@ -11,7 +9,10 @@ import {
11 getStoredVolume, 9 getStoredVolume,
12 saveAverageBandwidth 10 saveAverageBandwidth
13} from '../peertube-player-local-storage' 11} from '../peertube-player-local-storage'
14import { VideoFile } from '@shared/models' 12import { LoadedQualityData, PlayerNetworkInfo, WebtorrentPluginOptions } from '../peertube-videojs-typings'
13import { getRtcConfig, isIOS, videoFileMaxByResolution, videoFileMinByResolution } from '../utils'
14import { PeertubeChunkStore } from './peertube-chunk-store'
15import { renderVideo } from './video-renderer'
15 16
16const CacheChunkStore = require('cache-chunk-store') 17const CacheChunkStore = require('cache-chunk-store')
17 18
diff --git a/client/src/locale/angular.ar.xlf b/client/src/locale/angular.ar.xlf
index f7102b68d..48f9a2c7c 100644
--- a/client/src/locale/angular.ar.xlf
+++ b/client/src/locale/angular.ar.xlf
@@ -29,7 +29,7 @@
29 29
30 30
31 31
32 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 32 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
33 <trans-unit id="1486537403020619891" datatype="html"> 33 <trans-unit id="1486537403020619891" datatype="html">
34 <source>My watch history</source> 34 <source>My watch history</source>
35 <target state="translated">سجل مشاهداتي</target> 35 <target state="translated">سجل مشاهداتي</target>
@@ -458,10 +458,10 @@
458 <trans-unit id="2602586221576511475" datatype="html"> 458 <trans-unit id="2602586221576511475" datatype="html">
459 <source>Video quota</source> 459 <source>Video quota</source>
460 <target>حصة الفيديو</target> 460 <target>حصة الفيديو</target>
461 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 461
462 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 462
463 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 463
464 </trans-unit> 464 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
465 <trans-unit id="1502595455339510144" datatype="html"> 465 <trans-unit id="1502595455339510144" datatype="html">
466 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 466 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
467 <target>غير محدود <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> في اليوم الواحد)<x id="CLOSE_TAG_NG_CONTAINER"/></target> 467 <target>غير محدود <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> في اليوم الواحد)<x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -557,6 +557,30 @@
557 <target state="translated">الفديرالية</target> 557 <target state="translated">الفديرالية</target>
558 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 558 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
559 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 559 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
560 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
561 <source>Following</source><target state="new">Following</target>
562 <context-group purpose="location">
563 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
564 <context context-type="linenumber">29</context>
565 </context-group>
566 <context-group purpose="location">
567 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
568 <context context-type="linenumber">31</context>
569 </context-group>
570 <context-group purpose="location">
571 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
572 <context context-type="linenumber">28</context>
573 </context-group>
574 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
575 <source>Followers</source><target state="new">Followers</target>
576 <context-group purpose="location">
577 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
578 <context context-type="linenumber">34</context>
579 </context-group>
580 <context-group purpose="location">
581 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
582 <context context-type="linenumber">37</context>
583 </context-group>
560 </trans-unit> 584 </trans-unit>
561 <trans-unit id="3541687134897970106" datatype="html"> 585 <trans-unit id="3541687134897970106" datatype="html">
562 <source>followers</source> 586 <source>followers</source>
@@ -859,16 +883,16 @@
859 <trans-unit id="2454050363478003966" datatype="html"> 883 <trans-unit id="2454050363478003966" datatype="html">
860 <source>Login</source> 884 <source>Login</source>
861 <target>لِج</target> 885 <target>لِج</target>
862 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 886
863 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 887
864 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 888
865 </trans-unit> 889 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
866 <trans-unit id="2308975396733519902" datatype="html"> 890 <trans-unit id="2308975396733519902" datatype="html">
867 <source>Create an account</source> 891 <source>Create an account</source>
868 <target>أنشئ حسابًا</target> 892 <target>أنشئ حسابًا</target>
869 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 893
870 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 894
871 </trans-unit> 895 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
872 <trans-unit id="8936704404804793618" datatype="html"> 896 <trans-unit id="8936704404804793618" datatype="html">
873 <source>Videos</source> 897 <source>Videos</source>
874 <target>الفيديوهات</target> 898 <target>الفيديوهات</target>
@@ -903,10 +927,10 @@
903 <trans-unit id="1504521795586863905" datatype="html"> 927 <trans-unit id="1504521795586863905" datatype="html">
904 <source>VIDEOS</source> 928 <source>VIDEOS</source>
905 <target>الفيديوهات</target> 929 <target>الفيديوهات</target>
906 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 930
907 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 931
908 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 932
909 </trans-unit> 933 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
910 <trans-unit id="667372110624203230" datatype="html"> 934 <trans-unit id="667372110624203230" datatype="html">
911 <source>Import jobs concurrency</source> 935 <source>Import jobs concurrency</source>
912 <target state="new">Import jobs concurrency</target> 936 <target state="new">Import jobs concurrency</target>
@@ -1025,8 +1049,8 @@
1025 <trans-unit id="4424964105331349857" datatype="html"> 1049 <trans-unit id="4424964105331349857" datatype="html">
1026 <source>I'm a teapot</source> 1050 <source>I'm a teapot</source>
1027 <target state="new">I'm a teapot</target> 1051 <target state="new">I'm a teapot</target>
1028 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1052
1029 </trans-unit> 1053 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1030 <trans-unit id="1597262876035959248" datatype="html"> 1054 <trans-unit id="1597262876035959248" datatype="html">
1031 <source>That's an error.</source> 1055 <source>That's an error.</source>
1032 <target state="translated">هذا خطأ.</target> 1056 <target state="translated">هذا خطأ.</target>
@@ -1119,8 +1143,8 @@
1119 <trans-unit id="2971365540217107489" datatype="html"> 1143 <trans-unit id="2971365540217107489" datatype="html">
1120 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1144 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1121 <target state="translated">هذا الملف كبير. اتصل بالمدير حتى يزيد حد الرفع.</target> 1145 <target state="translated">هذا الملف كبير. اتصل بالمدير حتى يزيد حد الرفع.</target>
1122 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1146
1123 </trans-unit> 1147 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1124 <trans-unit id="2468689683507870964" datatype="html"> 1148 <trans-unit id="2468689683507870964" datatype="html">
1125 <source>In this instance's network</source> 1149 <source>In this instance's network</source>
1126 <target state="translated">في شبكة هذ المثيل</target> 1150 <target state="translated">في شبكة هذ المثيل</target>
@@ -1206,7 +1230,7 @@
1206 1230
1207 1231
1208 1232
1209 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 1233 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
1210 <trans-unit id="4209525355702493436" datatype="html"> 1234 <trans-unit id="4209525355702493436" datatype="html">
1211 <source>Ban</source> 1235 <source>Ban</source>
1212 <target>احظر</target> 1236 <target>احظر</target>
@@ -1429,11 +1453,7 @@
1429 <target state="translated">فيديو\تعليق\حساب</target> 1453 <target state="translated">فيديو\تعليق\حساب</target>
1430 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">22</context></context-group> 1454 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">22</context></context-group>
1431 </trans-unit> 1455 </trans-unit>
1432 <trans-unit id="2265605798180116441" datatype="html"> 1456
1433 <source>Follower handle</source>
1434 <target>مقبض تابع</target>
1435 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
1436 </trans-unit>
1437 <trans-unit id="3301856295120048857" datatype="html"> 1457 <trans-unit id="3301856295120048857" datatype="html">
1438 <source>State <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 1458 <source>State <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
1439 <target>حالة <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 1459 <target>حالة <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -1510,17 +1530,8 @@
1510 <target>يعرض <x id="INTERPOLATION"/> ل <x id="INTERPOLATION_1"/> من <x id="INTERPOLATION_2"/> متابع</target> 1530 <target>يعرض <x id="INTERPOLATION"/> ل <x id="INTERPOLATION_1"/> من <x id="INTERPOLATION_2"/> متابع</target>
1511 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">11</context></context-group> 1531 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">11</context></context-group>
1512 </trans-unit> 1532 </trans-unit>
1513 <trans-unit id="4682675125751819107" datatype="html"> 1533
1514 <source>Instances you follow</source> 1534
1515 <target state="translated">المثلاء المتابَعون</target>
1516 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
1517 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
1518 </trans-unit>
1519 <trans-unit id="6641024648411549335" datatype="html">
1520 <source>Host</source>
1521 <target>المضيف</target>
1522 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
1523 </trans-unit>
1524 <trans-unit id="6571718060636962350" datatype="html"> 1535 <trans-unit id="6571718060636962350" datatype="html">
1525 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 1536 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
1526 <target state="translated">Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 1537 <target state="translated">Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -1529,9 +1540,9 @@
1529 <trans-unit id="9160510009013134726" datatype="html"> 1540 <trans-unit id="9160510009013134726" datatype="html">
1530 <source>Unfollow</source> 1541 <source>Unfollow</source>
1531 <target state="translated">ألغ المتابعة</target> 1542 <target state="translated">ألغ المتابعة</target>
1532 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 1543
1533 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 1544
1534 </trans-unit> 1545 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
1535 <trans-unit id="8246779176913476983" datatype="html"> 1546 <trans-unit id="8246779176913476983" datatype="html">
1536 <source>Open instance in a new tab</source> 1547 <source>Open instance in a new tab</source>
1537 <target>افتح مثيل الخادم في لسان جديد</target> 1548 <target>افتح مثيل الخادم في لسان جديد</target>
@@ -1542,13 +1553,13 @@
1542 <trans-unit id="9132918641931433659" datatype="html"> 1553 <trans-unit id="9132918641931433659" datatype="html">
1543 <source>No host found matching current filters.</source> 1554 <source>No host found matching current filters.</source>
1544 <target>لم يُعثر على مضيف مطابق للمرشحات الحالية.</target> 1555 <target>لم يُعثر على مضيف مطابق للمرشحات الحالية.</target>
1545 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 1556
1546 </trans-unit> 1557 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
1547 <trans-unit id="7274241885665071790" datatype="html"> 1558 <trans-unit id="7274241885665071790" datatype="html">
1548 <source>Your instance is not following anyone.</source> 1559 <source>Your instance is not following anyone.</source>
1549 <target>مثيل الخادم الخاص بك لا يتابع أي شخص.</target> 1560 <target>مثيل الخادم الخاص بك لا يتابع أي شخص.</target>
1550 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 1561
1551 </trans-unit> 1562 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
1552 <trans-unit id="4774348799569692380" datatype="html"> 1563 <trans-unit id="4774348799569692380" datatype="html">
1553 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 1564 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
1554 <target>يعرض <x id="INTERPOLATION"/> ل <x id="INTERPOLATION_1"/> من <x id="INTERPOLATION_2"/> مضيفا</target> 1565 <target>يعرض <x id="INTERPOLATION"/> ل <x id="INTERPOLATION_1"/> من <x id="INTERPOLATION_2"/> مضيفا</target>
@@ -1557,18 +1568,10 @@
1557 <trans-unit id="4917252294930256268" datatype="html"> 1568 <trans-unit id="4917252294930256268" datatype="html">
1558 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 1569 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
1559 <target>يبدو أنك لست على خادم HTTPS. يحتاج خادم الويب الخاص بك إلى تنشيط TLS لمتابعة الخوادم.</target> 1570 <target>يبدو أنك لست على خادم HTTPS. يحتاج خادم الويب الخاص بك إلى تنشيط TLS لمتابعة الخوادم.</target>
1560 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 1571
1561 </trans-unit> 1572 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
1562 <trans-unit id="6275803119759621687" datatype="html"> 1573
1563 <source>Follow domains</source> 1574
1564 <target>تابع النطاق</target>
1565 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
1566 </trans-unit>
1567 <trans-unit id="1268699198448750610" datatype="html">
1568 <source>Follow instances</source>
1569 <target state="new">Follow instances</target>
1570 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
1571 </trans-unit>
1572 <trans-unit id="9216117865911519658" datatype="html"> 1575 <trans-unit id="9216117865911519658" datatype="html">
1573 <source>Action</source> 1576 <source>Action</source>
1574 <target state="new">Action</target> 1577 <target state="new">Action</target>
@@ -1650,16 +1653,16 @@
1650 <trans-unit id="5674286808255988565" datatype="html"> 1653 <trans-unit id="5674286808255988565" datatype="html">
1651 <source>Create</source> 1654 <source>Create</source>
1652 <target>إنشاء</target> 1655 <target>إنشاء</target>
1653 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 1656
1654 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 1657
1655 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 1658
1656 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 1659
1657 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 1660
1658 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 1661
1659 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 1662
1660 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 1663
1661 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 1664
1662 </trans-unit> 1665 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
1663 <trans-unit id="8147229944654164397" datatype="html"> 1666 <trans-unit id="8147229944654164397" datatype="html">
1664 <source>{VAR_PLURAL, plural, =1 {Video} other {Videos} }</source> 1667 <source>{VAR_PLURAL, plural, =1 {Video} other {Videos} }</source>
1665 <target>{VAR_PLURAL, plural, =1 {فيديو} other {مقاطع فيديو} }</target> 1668 <target>{VAR_PLURAL, plural, =1 {فيديو} other {مقاطع فيديو} }</target>
@@ -1705,11 +1708,11 @@
1705 <trans-unit id="5248717555542428023" datatype="html"> 1708 <trans-unit id="5248717555542428023" datatype="html">
1706 <source>Username</source> 1709 <source>Username</source>
1707 <target>اسم المستخدم</target> 1710 <target>اسم المستخدم</target>
1708 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 1711
1709 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 1712
1710 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 1713
1711 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 1714
1712 </trans-unit> 1715 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
1713 <trans-unit id="5428411040014095392" datatype="html"> 1716 <trans-unit id="5428411040014095392" datatype="html">
1714 <source>e.g. jane_doe</source> 1717 <source>e.g. jane_doe</source>
1715 <target state="translated">مثل jane_doe</target> 1718 <target state="translated">مثل jane_doe</target>
@@ -1725,14 +1728,14 @@
1725 <trans-unit id="4768749765465246664" datatype="html"> 1728 <trans-unit id="4768749765465246664" datatype="html">
1726 <source>Email</source> 1729 <source>Email</source>
1727 <target>البريد الإلكتروني</target> 1730 <target>البريد الإلكتروني</target>
1728 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1731
1729 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1732
1730 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1733
1731 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1734
1732 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1735
1733 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1736
1734 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1737
1735 </trans-unit> 1738 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1736 <trans-unit id="6475711663580561164" datatype="html"> 1739 <trans-unit id="6475711663580561164" datatype="html">
1737 <source>mail@example.com</source> 1740 <source>mail@example.com</source>
1738 <target>mail@example.com</target> 1741 <target>mail@example.com</target>
@@ -1742,15 +1745,15 @@
1742 <trans-unit id="1431416938026210429" datatype="html"> 1745 <trans-unit id="1431416938026210429" datatype="html">
1743 <source>Password</source> 1746 <source>Password</source>
1744 <target>كلمة المرور</target> 1747 <target>كلمة المرور</target>
1745 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1748
1746 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1749
1747 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1750
1748 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1751
1749 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1752
1750 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1753
1751 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1754
1752 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1755
1753 </trans-unit> 1756 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1754 <trans-unit id="8371296837649897723" datatype="html"> 1757 <trans-unit id="8371296837649897723" datatype="html">
1755 <source>If you leave the password empty, an email will be sent to the user.</source> 1758 <source>If you leave the password empty, an email will be sent to the user.</source>
1756 <target>إذا تركت كلمة المرور فارغة ، فسيتم إرسال بريد إلكتروني إلى المستخدم.</target> 1759 <target>إذا تركت كلمة المرور فارغة ، فسيتم إرسال بريد إلكتروني إلى المستخدم.</target>
@@ -1760,9 +1763,9 @@
1760 <trans-unit id="4145496584631696119" datatype="html"> 1763 <trans-unit id="4145496584631696119" datatype="html">
1761 <source>Role</source> 1764 <source>Role</source>
1762 <target state="translated">الدور</target> 1765 <target state="translated">الدور</target>
1763 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 1766
1764 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 1767
1765 </trans-unit> 1768 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
1766 <trans-unit id="7046347992315328430" datatype="html"> 1769 <trans-unit id="7046347992315328430" datatype="html">
1767 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 1770 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
1768 <target state="translated">تحويل الترميز مفعل. سيقتطع الحجم <x id="START_TAG_STRONG"/>الأصلي<x id="CLOSE_TAG_STRONG"/> للفيديو من حصة الفيديو.<x id="LINE_BREAK"/>يمكن لهذا المستخدم رفع ~ <x id="INTERPOLATION"/> كحد أقصى. </target> 1771 <target state="translated">تحويل الترميز مفعل. سيقتطع الحجم <x id="START_TAG_STRONG"/>الأصلي<x id="CLOSE_TAG_STRONG"/> للفيديو من حصة الفيديو.<x id="LINE_BREAK"/>يمكن لهذا المستخدم رفع ~ <x id="INTERPOLATION"/> كحد أقصى. </target>
@@ -1779,15 +1782,9 @@
1779 <trans-unit id="2622255144026150901" datatype="html"> 1782 <trans-unit id="2622255144026150901" datatype="html">
1780 <source>Auth plugin</source> 1783 <source>Auth plugin</source>
1781 <target state="new">Auth plugin</target> 1784 <target state="new">Auth plugin</target>
1782 <context-group purpose="location"> 1785
1783 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 1786
1784 <context context-type="linenumber">188</context> 1787 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
1785 </context-group>
1786 <context-group purpose="location">
1787 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
1788 <context context-type="linenumber">188</context>
1789 </context-group>
1790 </trans-unit>
1791 <trans-unit id="588099657508661970" datatype="html"> 1788 <trans-unit id="588099657508661970" datatype="html">
1792 <source>None (local authentication)</source> 1789 <source>None (local authentication)</source>
1793 <target state="translated">بدون (استيثاق محلي)</target> 1790 <target state="translated">بدون (استيثاق محلي)</target>
@@ -2047,6 +2044,12 @@
2047 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 2044 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
2048 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 2045 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
2049 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 2046 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
2047 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
2048 <source>Follower</source><target state="new">Follower</target>
2049 <context-group purpose="location">
2050 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
2051 <context context-type="linenumber">24</context>
2052 </context-group>
2050 </trans-unit> 2053 </trans-unit>
2051 <trans-unit id="4691552465058437520" datatype="html"> 2054 <trans-unit id="4691552465058437520" datatype="html">
2052 <source>Commented video</source> 2055 <source>Commented video</source>
@@ -2256,12 +2259,7 @@
2256 <target>يعرض <x id="INTERPOLATION"/> ل <x id="INTERPOLATION_1"/> من <x id="INTERPOLATION_2"/> إبلغا</target> 2259 <target>يعرض <x id="INTERPOLATION"/> ل <x id="INTERPOLATION_1"/> من <x id="INTERPOLATION_2"/> إبلغا</target>
2257 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">6</context></context-group> 2260 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">6</context></context-group>
2258 </trans-unit> 2261 </trans-unit>
2259 <trans-unit id="8899833753704589712" datatype="html"> 2262
2260 <source>Instances following you</source>
2261 <target state="translated">منصات تتابعك</target>
2262 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
2263 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
2264 </trans-unit>
2265 <trans-unit id="3109314382334906782" datatype="html"> 2263 <trans-unit id="3109314382334906782" datatype="html">
2266 <source>Reportee</source> 2264 <source>Reportee</source>
2267 <target>مراسل</target> 2265 <target>مراسل</target>
@@ -3643,9 +3641,9 @@ color: red;
3643 <trans-unit id="1006562256968398209" datatype="html"> 3641 <trans-unit id="1006562256968398209" datatype="html">
3644 <source>video</source> 3642 <source>video</source>
3645 <target>فيديو</target> 3643 <target>فيديو</target>
3646 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group> 3644
3647 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group> 3645
3648 </trans-unit> 3646 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
3649 <trans-unit id="6438815964972582865" datatype="html"> 3647 <trans-unit id="6438815964972582865" datatype="html">
3650 <source>The following link contains a private token and should not be shared with anyone.</source> 3648 <source>The following link contains a private token and should not be shared with anyone.</source>
3651 <target state="new"> The following link contains a private token and should not be shared with anyone. </target> 3649 <target state="new"> The following link contains a private token and should not be shared with anyone. </target>
@@ -3712,13 +3710,13 @@ color: red;
3712 <trans-unit id="6995024616159044376" datatype="html"> 3710 <trans-unit id="6995024616159044376" datatype="html">
3713 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 3711 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
3714 <target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 3712 <target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
3715 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 3713
3716 </trans-unit> 3714 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
3717 <trans-unit id="7873395933409147217" datatype="html"> 3715 <trans-unit id="7873395933409147217" datatype="html">
3718 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 3716 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
3719 <target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 3717 <target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
3720 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 3718
3721 </trans-unit> 3719 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
3722 <trans-unit id="5235042777215655908" datatype="html"> 3720 <trans-unit id="5235042777215655908" datatype="html">
3723 <source>subtitles</source> 3721 <source>subtitles</source>
3724 <target state="translated">ترجمات</target> 3722 <target state="translated">ترجمات</target>
@@ -4370,6 +4368,12 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
4370channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 4368channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
4371 <target state="translated">أتريد حذف <x id="PH"/>؟ ستحذف جميع فيديوهات قناة <x id="PH_1"/>، ولن تتمكن من إنشاء قناة بنفس الاسم (<x id="PH_2"/>)!</target> 4369 <target state="translated">أتريد حذف <x id="PH"/>؟ ستحذف جميع فيديوهات قناة <x id="PH_1"/>، ولن تتمكن من إنشاء قناة بنفس الاسم (<x id="PH_2"/>)!</target>
4372 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 4370 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
4371 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
4372 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
4373 <context-group purpose="location">
4374 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
4375 <context context-type="linenumber">48</context>
4376 </context-group>
4373 </trans-unit> 4377 </trans-unit>
4374 <trans-unit id="5387007581996837469" datatype="html"> 4378 <trans-unit id="5387007581996837469" datatype="html">
4375 <source>My Channels</source> 4379 <source>My Channels</source>
@@ -4488,19 +4492,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
4488 <trans-unit id="7252854992688790751" datatype="html"> 4492 <trans-unit id="7252854992688790751" datatype="html">
4489 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 4493 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
4490 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 4494 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
4491 <context-group purpose="location"> 4495
4492 <context context-type="sourcefile">src/app/+login/login.component.html</context> 4496 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
4493 <context context-type="linenumber">60,62</context>
4494 </context-group>
4495 </trans-unit>
4496 <trans-unit id="7215649348148521605" datatype="html"> 4497 <trans-unit id="7215649348148521605" datatype="html">
4497 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 4498 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
4498 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 4499 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
4499 <context-group purpose="location"> 4500
4500 <context context-type="sourcefile">src/app/+login/login.component.html</context> 4501 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
4501 <context context-type="linenumber">65,67</context>
4502 </context-group>
4503 </trans-unit>
4504 <trans-unit id="4302331889176439801" datatype="html"> 4502 <trans-unit id="4302331889176439801" datatype="html">
4505 <source>Request email for account verification</source> 4503 <source>Request email for account verification</source>
4506 <target>طلب البريد الإلكتروني للتحقق من الحساب</target> 4504 <target>طلب البريد الإلكتروني للتحقق من الحساب</target>
@@ -4509,15 +4507,15 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
4509 <trans-unit id="3967269098753656610" datatype="html"> 4507 <trans-unit id="3967269098753656610" datatype="html">
4510 <source>Email address</source> 4508 <source>Email address</source>
4511 <target>عنوان البريد الإلكتروني</target> 4509 <target>عنوان البريد الإلكتروني</target>
4512 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 4510
4513 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 4511
4514 </trans-unit> 4512 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
4515 <trans-unit id="7808756054397155068" datatype="html"> 4513 <trans-unit id="7808756054397155068" datatype="html">
4516 <source>Reset</source> 4514 <source>Reset</source>
4517 <target state="new">Reset</target> 4515 <target state="new">Reset</target>
4518 <note priority="1" from="description">Password reset button</note> 4516 <note priority="1" from="description">Password reset button</note>
4519 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 4517
4520 </trans-unit> 4518 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
4521 <trans-unit id="4027779086550572813" datatype="html"> 4519 <trans-unit id="4027779086550572813" datatype="html">
4522 <source>Send verification email</source> 4520 <source>Send verification email</source>
4523 <target>إرسال رسالة التأكيد</target> 4521 <target>إرسال رسالة التأكيد</target>
@@ -4621,11 +4619,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
4621 <trans-unit id="5512878593724620692" datatype="html"> 4619 <trans-unit id="5512878593724620692" datatype="html">
4622 <source>CHANNELS</source> 4620 <source>CHANNELS</source>
4623 <target state="new">CHANNELS</target> 4621 <target state="new">CHANNELS</target>
4624 <context-group purpose="location"> 4622
4625 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 4623 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
4626 <context context-type="linenumber">82</context>
4627 </context-group>
4628 </trans-unit>
4629 <trans-unit id="6479885129995567639" datatype="html"> 4624 <trans-unit id="6479885129995567639" datatype="html">
4630 <source>Video channels</source> 4625 <source>Video channels</source>
4631 <target>قنوات الفيديو</target> 4626 <target>قنوات الفيديو</target>
@@ -5185,48 +5180,48 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
5185 <source>Username or email address</source> 5180 <source>Username or email address</source>
5186 <target>اسم المستخدم أو عنوان البريد الإلكتروني</target> 5181 <target>اسم المستخدم أو عنوان البريد الإلكتروني</target>
5187 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 5182 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
5183 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
5184 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
5185 <context-group purpose="location">
5186 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5187 <context context-type="linenumber">33,34</context>
5188 </context-group>
5188 </trans-unit> 5189 </trans-unit>
5189 <trans-unit id="8715156686857791956" datatype="html"> 5190 <trans-unit id="8715156686857791956" datatype="html">
5190 <source>Click here to reset your password</source> 5191 <source>Click here to reset your password</source>
5191 <target>انقر هنا لإعادة تعيين كلمة المرور الخاصة بك</target> 5192 <target>انقر هنا لإعادة تعيين كلمة المرور الخاصة بك</target>
5192 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 5193
5193 </trans-unit> 5194 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
5194 <trans-unit id="892063502898494584" datatype="html"> 5195 <trans-unit id="892063502898494584" datatype="html">
5195 <source>I forgot my password</source> 5196 <source>I forgot my password</source>
5196 <target state="new">I forgot my password</target> 5197 <target state="new">I forgot my password</target>
5197 <context-group purpose="location"> 5198
5198 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5199 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
5199 <context context-type="linenumber">47</context>
5200 </context-group>
5201 </trans-unit>
5202 <trans-unit id="2101170466365500913" datatype="html"> 5200 <trans-unit id="2101170466365500913" datatype="html">
5203 <source>Logging into an account lets you publish content</source> 5201 <source>Logging into an account lets you publish content</source>
5204 <target state="new"> Logging into an account lets you publish content </target> 5202 <target state="new"> Logging into an account lets you publish content </target>
5205 <context-group purpose="location"> 5203
5206 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5204 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
5207 <context context-type="linenumber">56,57</context>
5208 </context-group>
5209 </trans-unit>
5210 <trans-unit id="3183213940445113677" datatype="html"> 5205 <trans-unit id="3183213940445113677" datatype="html">
5211 <source>Or sign in with</source> 5206 <source>Or sign in with</source>
5212 <target>أو قم بتسجيل الدخول باستخدام</target> 5207 <target>أو قم بتسجيل الدخول باستخدام</target>
5213 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 5208
5214 </trans-unit> 5209 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
5215 <trans-unit id="3238209155172574367" datatype="html"> 5210 <trans-unit id="3238209155172574367" datatype="html">
5216 <source>Forgot your password</source> 5211 <source>Forgot your password</source>
5217 <target>نسيتَ كلمة المرور</target> 5212 <target>نسيتَ كلمة المرور</target>
5218 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 5213
5219 </trans-unit> 5214 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
5220 <trans-unit id="87327320394367488" datatype="html"> 5215 <trans-unit id="87327320394367488" datatype="html">
5221 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 5216 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
5222 <target>عذرًا ، لا يمكنك استرداد كلمة المرور الخاصة بك لأن مدير مثيل الخادم الخاص بك لم يقم بتكوين نظام البريد الإلكتروني لبيرتيوب.</target> 5217 <target>عذرًا ، لا يمكنك استرداد كلمة المرور الخاصة بك لأن مدير مثيل الخادم الخاص بك لم يقم بتكوين نظام البريد الإلكتروني لبيرتيوب.</target>
5223 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 5218
5224 </trans-unit> 5219 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
5225 <trans-unit id="3188014010833256853" datatype="html"> 5220 <trans-unit id="3188014010833256853" datatype="html">
5226 <source>Enter your email address and we will send you a link to reset your password.</source> 5221 <source>Enter your email address and we will send you a link to reset your password.</source>
5227 <target state="new"> Enter your email address and we will send you a link to reset your password. </target> 5222 <target state="new"> Enter your email address and we will send you a link to reset your password. </target>
5228 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 5223
5229 </trans-unit> 5224 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
5230 <trans-unit id="1190256911880544559" datatype="html"> 5225 <trans-unit id="1190256911880544559" datatype="html">
5231 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 5226 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
5232The link will expire within 1 hour.</source> 5227The link will expire within 1 hour.</source>
@@ -5921,8 +5916,8 @@ The link will expire within 1 hour.</source>
5921 <trans-unit id="6161604372916832458" datatype="html"> 5916 <trans-unit id="6161604372916832458" datatype="html">
5922 <source>Upload on hold</source> 5917 <source>Upload on hold</source>
5923 <target state="new">Upload on hold</target> 5918 <target state="new">Upload on hold</target>
5924 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 5919
5925 </trans-unit> 5920 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
5926 <trans-unit id="285180972645018518" datatype="html"> 5921 <trans-unit id="285180972645018518" datatype="html">
5927 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 5922 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
5928 <target state="translated">عذرا، عُطلت خاصية الرفع في حسابك، اتصل بمدير المنصة ليقوم بفك قَفل حصتك.</target> 5923 <target state="translated">عذرا، عُطلت خاصية الرفع في حسابك، اتصل بمدير المنصة ليقوم بفك قَفل حصتك.</target>
@@ -6399,13 +6394,13 @@ The link will expire within 1 hour.</source>
6399 <trans-unit id="6979021199788941693" datatype="html"> 6394 <trans-unit id="6979021199788941693" datatype="html">
6400 <source>Your message has been sent.</source> 6395 <source>Your message has been sent.</source>
6401 <target>تم ارسال رسالتك.</target> 6396 <target>تم ارسال رسالتك.</target>
6402 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group> 6397
6403 </trans-unit> 6398 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6404 <trans-unit id="2072135752262464360" datatype="html"> 6399 <trans-unit id="2072135752262464360" datatype="html">
6405 <source>You already sent this form recently</source> 6400 <source>You already sent this form recently</source>
6406 <target>لقد أرسلت هذا النموذج بالفعل مؤخرًا</target> 6401 <target>لقد أرسلت هذا النموذج بالفعل مؤخرًا</target>
6407 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group> 6402
6408 </trans-unit> 6403 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6409 <trans-unit id="819067926858619041" datatype="html"> 6404 <trans-unit id="819067926858619041" datatype="html">
6410 <source>Account videos</source> 6405 <source>Account videos</source>
6411 <target state="translated">فيديوهات الحساب</target> 6406 <target state="translated">فيديوهات الحساب</target>
@@ -6448,13 +6443,13 @@ The link will expire within 1 hour.</source>
6448 <trans-unit id="4856575356061361269" datatype="html"> 6443 <trans-unit id="4856575356061361269" datatype="html">
6449 <source><x id="PH"/> direct account followers </source> 6444 <source><x id="PH"/> direct account followers </source>
6450 <target><x id="PH"/> متابعًا مباشرًا للحساب </target> 6445 <target><x id="PH"/> متابعًا مباشرًا للحساب </target>
6451 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6446
6452 </trans-unit> 6447 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6453 <trans-unit id="6250999352462648289" datatype="html"> 6448 <trans-unit id="6250999352462648289" datatype="html">
6454 <source>Report this account</source> 6449 <source>Report this account</source>
6455 <target state="translated">بلِّغ عن هذا الحساب</target> 6450 <target state="translated">بلِّغ عن هذا الحساب</target>
6456 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6451
6457 </trans-unit> 6452 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6458 <trans-unit id="1504521795586863905" datatype="html"> 6453 <trans-unit id="1504521795586863905" datatype="html">
6459 <source>VIDEOS</source> 6454 <source>VIDEOS</source>
6460 <target state="translated">فيديوهات</target> 6455 <target state="translated">فيديوهات</target>
@@ -6464,19 +6459,19 @@ The link will expire within 1 hour.</source>
6464 <trans-unit id="25349740244798533" datatype="html"> 6459 <trans-unit id="25349740244798533" datatype="html">
6465 <source>Username copied</source> 6460 <source>Username copied</source>
6466 <target>تم نسخ اسم المستخدم</target> 6461 <target>تم نسخ اسم المستخدم</target>
6467 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6462
6468 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6463
6469 </trans-unit> 6464 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6470 <trans-unit id="9221735175659318025" datatype="html"> 6465 <trans-unit id="9221735175659318025" datatype="html">
6471 <source>1 subscriber</source> 6466 <source>1 subscriber</source>
6472 <target state="translated">مشترك</target> 6467 <target state="translated">مشترك</target>
6473 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6468
6474 </trans-unit> 6469 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6475 <trans-unit id="4097331874769079975" datatype="html"> 6470 <trans-unit id="4097331874769079975" datatype="html">
6476 <source><x id="PH"/> subscribers</source> 6471 <source><x id="PH"/> subscribers</source>
6477 <target state="translated"><x id="PH"/> مشتركا</target> 6472 <target state="translated"><x id="PH"/> مشتركا</target>
6478 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6473
6479 </trans-unit> 6474 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6480 <trans-unit id="1035838766454786107" datatype="html"> 6475 <trans-unit id="1035838766454786107" datatype="html">
6481 <source>Audio-only</source> 6476 <source>Audio-only</source>
6482 <target>صوت فقط</target> 6477 <target>صوت فقط</target>
@@ -6526,6 +6521,12 @@ The link will expire within 1 hour.</source>
6526 <source>Auto (via ffmpeg)</source> 6521 <source>Auto (via ffmpeg)</source>
6527 <target>تلقائي (عبر ffmpeg)</target> 6522 <target>تلقائي (عبر ffmpeg)</target>
6528 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6523 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6524 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6525 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6526 <context-group purpose="location">
6527 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6528 <context context-type="linenumber">3</context>
6529 </context-group>
6529 </trans-unit> 6530 </trans-unit>
6530 <trans-unit id="931255636742351800" datatype="html"> 6531 <trans-unit id="931255636742351800" datatype="html">
6531 <source>No limit</source> 6532 <source>No limit</source>
@@ -6804,17 +6805,43 @@ The link will expire within 1 hour.</source>
6804 <source><x id="PH"/> removed from instance followers </source> 6805 <source><x id="PH"/> removed from instance followers </source>
6805 <target>أُزيل <x id="PH"/> من متابعي المثيل </target> 6806 <target>أُزيل <x id="PH"/> من متابعي المثيل </target>
6806 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6807 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6808 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6809 <source>Follow</source><target state="new">Follow</target>
6810 <context-group purpose="location">
6811 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6812 <context context-type="linenumber">3</context>
6813 </context-group>
6814 <context-group purpose="location">
6815 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6816 <context context-type="linenumber">37</context>
6817 </context-group>
6818 <context-group purpose="location">
6819 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6820 <context context-type="linenumber">18</context>
6821 </context-group>
6822 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6823 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6824 <context-group purpose="location">
6825 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6826 <context context-type="linenumber">11</context>
6827 </context-group>
6807 </trans-unit> 6828 </trans-unit>
6808 <trans-unit id="2355066641781598196" datatype="html"> 6829 <trans-unit id="2355066641781598196" datatype="html">
6809 <source>Follow request(s) sent!</source> 6830 <source>Follow request(s) sent!</source>
6810 <target>تم إرسال طلب المتابعة!</target> 6831 <target>تم إرسال طلب المتابعة!</target>
6811 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6832
6833 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6834 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6835 <context-group purpose="location">
6836 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6837 <context context-type="linenumber">3</context>
6838 </context-group>
6812 </trans-unit> 6839 </trans-unit>
6813 <trans-unit id="4245720728052819482" datatype="html"> 6840 <trans-unit id="4245720728052819482" datatype="html">
6814 <source>Do you really want to unfollow <x id="PH"/>?</source> 6841 <source>Do you really want to unfollow <x id="PH"/>?</source>
6815 <target>هل تريد الغاء متابعة <x id="PH"/>؟</target> 6842 <target>هل تريد الغاء متابعة <x id="PH"/>؟</target>
6816 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6843
6817 </trans-unit> 6844 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6818 <trans-unit id="9160510009013134726" datatype="html"> 6845 <trans-unit id="9160510009013134726" datatype="html">
6819 <source>Unfollow</source> 6846 <source>Unfollow</source>
6820 <target>إلغاء المتابَعة</target> 6847 <target>إلغاء المتابَعة</target>
@@ -6823,8 +6850,8 @@ The link will expire within 1 hour.</source>
6823 <trans-unit id="3935234189109112926" datatype="html"> 6850 <trans-unit id="3935234189109112926" datatype="html">
6824 <source>You are not following <x id="PH"/> anymore.</source> 6851 <source>You are not following <x id="PH"/> anymore.</source>
6825 <target>انت لا تتابع <x id="PH"/> بعد الآن.</target> 6852 <target>انت لا تتابع <x id="PH"/> بعد الآن.</target>
6826 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6853
6827 </trans-unit> 6854 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6828 <trans-unit id="2593763089859685916" datatype="html"> 6855 <trans-unit id="2593763089859685916" datatype="html">
6829 <source>enabled</source> 6856 <source>enabled</source>
6830 <target>مفعّل</target> 6857 <target>مفعّل</target>
@@ -7355,9 +7382,9 @@ The link will expire within 1 hour.</source>
7355 <trans-unit id="1519954996184640001" datatype="html"> 7382 <trans-unit id="1519954996184640001" datatype="html">
7356 <source>Error</source> 7383 <source>Error</source>
7357 <target>خطأ</target> 7384 <target>خطأ</target>
7358 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7385
7359 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7386
7360 </trans-unit> 7387 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7361 <trans-unit id="5076187961693950167" datatype="html"> 7388 <trans-unit id="5076187961693950167" datatype="html">
7362 <source>Standard logs</source> 7389 <source>Standard logs</source>
7363 <target>السجلات القياسية</target> 7390 <target>السجلات القياسية</target>
@@ -7398,16 +7425,8 @@ The link will expire within 1 hour.</source>
7398 <target>حدّث كلمة مرور المستخدم</target> 7425 <target>حدّث كلمة مرور المستخدم</target>
7399 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7426 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7400 </trans-unit> 7427 </trans-unit>
7401 <trans-unit id="177544274549739411" datatype="html"> 7428
7402 <source>Following list</source> 7429
7403 <target state="translated">قائمة المتابَعين</target>
7404 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7405 </trans-unit>
7406 <trans-unit id="8092429110007204784" datatype="html">
7407 <source>Followers list</source>
7408 <target state="translated">قائمة المتابِعين</target>
7409 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7410 </trans-unit>
7411 <trans-unit id="780323526182667308" datatype="html"> 7430 <trans-unit id="780323526182667308" datatype="html">
7412 <source>User <x id="PH"/> updated.</source> 7431 <source>User <x id="PH"/> updated.</source>
7413 <target>حُدّث حساب المستخدم <x id="PH"/>.</target> 7432 <target>حُدّث حساب المستخدم <x id="PH"/>.</target>
@@ -7443,16 +7462,8 @@ The link will expire within 1 hour.</source>
7443 <target state="translated">الاتحادية</target> 7462 <target state="translated">الاتحادية</target>
7444 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7463 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7445 </trans-unit> 7464 </trans-unit>
7446 <trans-unit id="4682675125751819107" datatype="html"> 7465
7447 <source>Instances you follow</source> 7466
7448 <target state="translated">المثيلات المتابَعة</target>
7449 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7450 </trans-unit>
7451 <trans-unit id="8899833753704589712" datatype="html">
7452 <source>Instances following you</source>
7453 <target state="translated">المثيلات المتابِعة</target>
7454 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7455 </trans-unit>
7456 <trans-unit id="3767259920053407667" datatype="html"> 7467 <trans-unit id="3767259920053407667" datatype="html">
7457 <source>Videos will be deleted, comments will be tombstoned.</source> 7468 <source>Videos will be deleted, comments will be tombstoned.</source>
7458 <target state="translated">سيحذف مقاطع الفيديو ، ستحذف التعليقات.</target> 7469 <target state="translated">سيحذف مقاطع الفيديو ، ستحذف التعليقات.</target>
@@ -7483,6 +7494,24 @@ The link will expire within 1 hour.</source>
7483 <target>تعيين البريد الإلكتروني كمتحقق منه</target> 7494 <target>تعيين البريد الإلكتروني كمتحقق منه</target>
7484 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7495 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7485 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7496 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7497 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7498 <source>Created</source><target state="new">Created</target>
7499 <context-group purpose="location">
7500 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7501 <context context-type="linenumber">115</context>
7502 </context-group>
7503 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7504 <source>Daily quota</source><target state="new">Daily quota</target>
7505 <context-group purpose="location">
7506 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7507 <context context-type="linenumber">120</context>
7508 </context-group>
7509 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7510 <source>Last login</source><target state="new">Last login</target>
7511 <context-group purpose="location">
7512 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7513 <context context-type="linenumber">122</context>
7514 </context-group>
7486 </trans-unit> 7515 </trans-unit>
7487 <trans-unit id="3403978719736970622" datatype="html"> 7516 <trans-unit id="3403978719736970622" datatype="html">
7488 <source>You cannot ban root.</source> 7517 <source>You cannot ban root.</source>
@@ -7524,23 +7553,23 @@ The link will expire within 1 hour.</source>
7524 <trans-unit id="1266887509445371246" datatype="html"> 7553 <trans-unit id="1266887509445371246" datatype="html">
7525 <source>Incorrect username or password.</source> 7554 <source>Incorrect username or password.</source>
7526 <target>اسم المستخدم أو كلمة المرور خاطئة.</target> 7555 <target>اسم المستخدم أو كلمة المرور خاطئة.</target>
7527 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 7556
7528 </trans-unit> 7557 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
7529 <trans-unit id="6974874606619467663" datatype="html"> 7558 <trans-unit id="6974874606619467663" datatype="html">
7530 <source>Your account is blocked.</source> 7559 <source>Your account is blocked.</source>
7531 <target state="translated">حسابك محجوب.</target> 7560 <target state="translated">حسابك محجوب.</target>
7532 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 7561
7533 </trans-unit> 7562 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
7534 <trans-unit id="1137937154872046253" datatype="html"> 7563 <trans-unit id="1137937154872046253" datatype="html">
7535 <source>Video channel <x id="PH"/> created.</source> 7564 <source>Video channel <x id="PH"/> created.</source>
7536 <target>أُنشئت قناة الفيديو <x id="PH"/>.</target> 7565 <target>أُنشئت قناة الفيديو <x id="PH"/>.</target>
7537 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 7566
7538 </trans-unit> 7567 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7539 <trans-unit id="8723777130353305761" datatype="html"> 7568 <trans-unit id="8723777130353305761" datatype="html">
7540 <source>This name already exists on this instance.</source> 7569 <source>This name already exists on this instance.</source>
7541 <target>هذا الإسم موجود على هذا المثيل.</target> 7570 <target>هذا الإسم موجود على هذا المثيل.</target>
7542 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 7571
7543 </trans-unit> 7572 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7544 <trans-unit id="7589345916094713536" datatype="html"> 7573 <trans-unit id="7589345916094713536" datatype="html">
7545 <source>Video channel <x id="PH"/> updated.</source> 7574 <source>Video channel <x id="PH"/> updated.</source>
7546 <target>حُدثت قناة فيديو <x id="PH"/>.</target> 7575 <target>حُدثت قناة فيديو <x id="PH"/>.</target>
@@ -7579,11 +7608,7 @@ The link will expire within 1 hour.</source>
7579 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-settings.component.ts</context><context context-type="linenumber">61</context></context-group> 7608 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-settings.component.ts</context><context context-type="linenumber">61</context></context-group>
7580 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">122</context></context-group> 7609 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">122</context></context-group>
7581 </trans-unit> 7610 </trans-unit>
7582 <trans-unit id="2575302837003821736" datatype="html"> 7611
7583 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7584 <target>لتأكيد اكتب الاسم العلني لقناة الفيديو <x id="PH"/></target>
7585 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
7586 </trans-unit>
7587 <trans-unit id="624066830180032195" datatype="html"> 7612 <trans-unit id="624066830180032195" datatype="html">
7588 <source>Video channel <x id="PH"/> deleted.</source> 7613 <source>Video channel <x id="PH"/> deleted.</source>
7589 <target>حُذفت قناة فيديو <x id="PH"/>.</target> 7614 <target>حُذفت قناة فيديو <x id="PH"/>.</target>
@@ -7935,6 +7960,12 @@ The link will expire within 1 hour.</source>
7935 <source>Ownership change request sent.</source> 7960 <source>Ownership change request sent.</source>
7936 <target>تم إرسال طلب تغيير الملكية.</target> 7961 <target>تم إرسال طلب تغيير الملكية.</target>
7937 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 7962 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
7963 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
7964 <source>Sort by</source><target state="new">Sort by</target>
7965 <context-group purpose="location">
7966 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7967 <context context-type="linenumber">26</context>
7968 </context-group>
7938 </trans-unit> 7969 </trans-unit>
7939 <trans-unit id="3058024914967508975" datatype="html"> 7970 <trans-unit id="3058024914967508975" datatype="html">
7940 <source>My videos</source> 7971 <source>My videos</source>
@@ -8152,7 +8183,7 @@ The link will expire within 1 hour.</source>
8152 <target>الاشتراك في الحساب</target> 8183 <target>الاشتراك في الحساب</target>
8153 8184
8154 8185
8155 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8186 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8156 <trans-unit id="3131904093925601441" datatype="html"> 8187 <trans-unit id="3131904093925601441" datatype="html">
8157 <source>PLAYLISTS</source> 8188 <source>PLAYLISTS</source>
8158 <target state="new">PLAYLISTS</target> 8189 <target state="new">PLAYLISTS</target>
@@ -8438,33 +8469,33 @@ The link will expire within 1 hour.</source>
8438 <trans-unit id="3284171506518522275" datatype="html"> 8469 <trans-unit id="3284171506518522275" datatype="html">
8439 <source>Your video was uploaded to your account and is private.</source> 8470 <source>Your video was uploaded to your account and is private.</source>
8440 <target>تم رفع الفيديو الخاص بك إلى حسابك وهو خاص.</target> 8471 <target>تم رفع الفيديو الخاص بك إلى حسابك وهو خاص.</target>
8441 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 8472
8442 </trans-unit> 8473 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
8443 <trans-unit id="5699822024600815733" datatype="html"> 8474 <trans-unit id="5699822024600815733" datatype="html">
8444 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 8475 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
8445 <target>ولكن ستفقد البيانات المرتبطة (العلامات ،الوصف...) ، هل تريد بالتأكيد مغادرة هذه الصفحة؟</target> 8476 <target>ولكن ستفقد البيانات المرتبطة (العلامات ،الوصف...) ، هل تريد بالتأكيد مغادرة هذه الصفحة؟</target>
8446 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 8477
8447 </trans-unit> 8478 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
8448 <trans-unit id="1219739004043110649" datatype="html"> 8479 <trans-unit id="1219739004043110649" datatype="html">
8449 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 8480 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
8450 <target>لم يرفع الفيديو الخاص بك حتى الآن ، هل تريد بالتأكيد مغادرة هذه الصفحة؟</target> 8481 <target>لم يرفع الفيديو الخاص بك حتى الآن ، هل تريد بالتأكيد مغادرة هذه الصفحة؟</target>
8451 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 8482
8452 </trans-unit> 8483 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8453 <trans-unit id="6932865105766151309" datatype="html"> 8484 <trans-unit id="6932865105766151309" datatype="html">
8454 <source>Upload</source> 8485 <source>Upload</source>
8455 <target state="translated">رفع</target> 8486 <target state="translated">رفع</target>
8456 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 8487
8457 </trans-unit> 8488 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
8458 <trans-unit id="8278735427925094503" datatype="html"> 8489 <trans-unit id="8278735427925094503" datatype="html">
8459 <source>Upload <x id="PH"/> </source> 8490 <source>Upload <x id="PH"/> </source>
8460 <target>ارفع <x id="PH"/> </target> 8491 <target>ارفع <x id="PH"/> </target>
8461 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 8492
8462 </trans-unit> 8493 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
8463 <trans-unit id="5981816353437801748" datatype="html"> 8494 <trans-unit id="5981816353437801748" datatype="html">
8464 <source>Video published.</source> 8495 <source>Video published.</source>
8465 <target>نُشر الفيديو.</target> 8496 <target>نُشر الفيديو.</target>
8466 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 8497
8467 </trans-unit> 8498 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
8468 <trans-unit id="764164089183618119" datatype="html"> 8499 <trans-unit id="764164089183618119" datatype="html">
8469 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 8500 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
8470 <target>لم تحفظ التغييرات! إذا غادرت ، ستفقد التغييرات.</target> 8501 <target>لم تحفظ التغييرات! إذا غادرت ، ستفقد التغييرات.</target>
@@ -8558,27 +8589,27 @@ The link will expire within 1 hour.</source>
8558 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 8589 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
8559 <target state="translated">هذا الفيديو ليس متوفرا على هذا المثيل. هل تريد التوجه المثيل الأصلي: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a> ؟</target> 8590 <target state="translated">هذا الفيديو ليس متوفرا على هذا المثيل. هل تريد التوجه المثيل الأصلي: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a> ؟</target>
8560 8591
8561 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 8592 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
8562 <trans-unit id="5761611056224181752" datatype="html"> 8593 <trans-unit id="5761611056224181752" datatype="html">
8563 <source>Redirection</source> 8594 <source>Redirection</source>
8564 <target state="translated">اعادة توجيه</target> 8595 <target state="translated">اعادة توجيه</target>
8565 8596
8566 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 8597 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
8567 <trans-unit id="8858527736400081688" datatype="html"> 8598 <trans-unit id="8858527736400081688" datatype="html">
8568 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 8599 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
8569 <target>يحتوي هذا الفيديو على محتوى للبالغين أو محتوى صريح. أمتأكد من مشاهدته؟</target> 8600 <target>يحتوي هذا الفيديو على محتوى للبالغين أو محتوى صريح. أمتأكد من مشاهدته؟</target>
8570 8601
8571 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 8602 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
8572 <trans-unit id="3937119019020041049" datatype="html"> 8603 <trans-unit id="3937119019020041049" datatype="html">
8573 <source>Mature or explicit content</source> 8604 <source>Mature or explicit content</source>
8574 <target>محتوى للبالغين أو محتوى صريح</target> 8605 <target>محتوى للبالغين أو محتوى صريح</target>
8575 8606
8576 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 8607 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
8577 <trans-unit id="1755474755114288376" datatype="html"> 8608 <trans-unit id="1755474755114288376" datatype="html">
8578 <source>Up Next</source> 8609 <source>Up Next</source>
8579 <target>التالي</target> 8610 <target>التالي</target>
8580 8611
8581 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 8612 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
8582 <trans-unit id="2159130950882492111" datatype="html"> 8613 <trans-unit id="2159130950882492111" datatype="html">
8583 <source>Cancel</source> 8614 <source>Cancel</source>
8584 <target state="translated">ألغ</target> 8615 <target state="translated">ألغ</target>
@@ -8588,62 +8619,62 @@ The link will expire within 1 hour.</source>
8588 <source>Autoplay is suspended</source> 8619 <source>Autoplay is suspended</source>
8589 <target>أُوقف التشغيل التلقائي</target> 8620 <target>أُوقف التشغيل التلقائي</target>
8590 8621
8591 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 8622 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
8592 <trans-unit id="7895294730547405228" datatype="html"> 8623 <trans-unit id="7895294730547405228" datatype="html">
8593 <source>Enter/exit fullscreen (requires player focus)</source> 8624 <source>Enter/exit fullscreen (requires player focus)</source>
8594 <target>ادخل / اخرج من وضع ملء الشاشة (يتطلب تركيز المشغل)</target> 8625 <target>ادخل / اخرج من وضع ملء الشاشة (يتطلب تركيز المشغل)</target>
8595 8626
8596 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 8627 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
8597 <trans-unit id="7618388257165864759" datatype="html"> 8628 <trans-unit id="7618388257165864759" datatype="html">
8598 <source>Play/Pause the video (requires player focus)</source> 8629 <source>Play/Pause the video (requires player focus)</source>
8599 <target>شغل / أوقف مؤقتًا الفيديو (يتطلب تركيز المشغل)</target> 8630 <target>شغل / أوقف مؤقتًا الفيديو (يتطلب تركيز المشغل)</target>
8600 8631
8601 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 8632 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
8602 <trans-unit id="7761890399634216630" datatype="html"> 8633 <trans-unit id="7761890399634216630" datatype="html">
8603 <source>Mute/unmute the video (requires player focus)</source> 8634 <source>Mute/unmute the video (requires player focus)</source>
8604 <target>أكتم / ألغ كتم الفيديو (يتطلب تركيز المشغل)</target> 8635 <target>أكتم / ألغ كتم الفيديو (يتطلب تركيز المشغل)</target>
8605 8636
8606 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 8637 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
8607 <trans-unit id="5996585232248234904" datatype="html"> 8638 <trans-unit id="5996585232248234904" datatype="html">
8608 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 8639 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
8609 <target>التخطي إلى نسبة مئوية من الفيديو: 0 هو 0٪ و 9 هو 90٪ (يتطلب تركيز المشغل)</target> 8640 <target>التخطي إلى نسبة مئوية من الفيديو: 0 هو 0٪ و 9 هو 90٪ (يتطلب تركيز المشغل)</target>
8610 8641
8611 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 8642 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
8612 <trans-unit id="3748765405903319998" datatype="html"> 8643 <trans-unit id="3748765405903319998" datatype="html">
8613 <source>Increase the volume (requires player focus)</source> 8644 <source>Increase the volume (requires player focus)</source>
8614 <target>زد مستوى الصوت (يتطلب تركيز اللاعب)</target> 8645 <target>زد مستوى الصوت (يتطلب تركيز اللاعب)</target>
8615 8646
8616 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 8647 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
8617 <trans-unit id="5810704036407159982" datatype="html"> 8648 <trans-unit id="5810704036407159982" datatype="html">
8618 <source>Decrease the volume (requires player focus)</source> 8649 <source>Decrease the volume (requires player focus)</source>
8619 <target>خفّض مستوى الصوت (يتطلب تركيز المشغل)</target> 8650 <target>خفّض مستوى الصوت (يتطلب تركيز المشغل)</target>
8620 8651
8621 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 8652 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
8622 <trans-unit id="2622048822548065691" datatype="html"> 8653 <trans-unit id="2622048822548065691" datatype="html">
8623 <source>Seek the video forward (requires player focus)</source> 8654 <source>Seek the video forward (requires player focus)</source>
8624 <target state="translated">البحث عن الفيديو (يتطلب تركيز المشغل)</target> 8655 <target state="translated">البحث عن الفيديو (يتطلب تركيز المشغل)</target>
8625 8656
8626 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 8657 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
8627 <trans-unit id="6540078205109221153" datatype="html"> 8658 <trans-unit id="6540078205109221153" datatype="html">
8628 <source>Seek the video backward (requires player focus)</source> 8659 <source>Seek the video backward (requires player focus)</source>
8629 <target state="translated">طلب الفيديو للخلف (يتطلب تركيز المشغل)</target> 8660 <target state="translated">طلب الفيديو للخلف (يتطلب تركيز المشغل)</target>
8630 8661
8631 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 8662 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
8632 <trans-unit id="1956491957766210808" datatype="html"> 8663 <trans-unit id="1956491957766210808" datatype="html">
8633 <source>Increase playback rate (requires player focus)</source> 8664 <source>Increase playback rate (requires player focus)</source>
8634 <target>زيادة معدل التشغيل (يتطلب تركيز المشغل)</target> 8665 <target>زيادة معدل التشغيل (يتطلب تركيز المشغل)</target>
8635 8666
8636 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 8667 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
8637 <trans-unit id="5495529997674803186" datatype="html"> 8668 <trans-unit id="5495529997674803186" datatype="html">
8638 <source>Decrease playback rate (requires player focus)</source> 8669 <source>Decrease playback rate (requires player focus)</source>
8639 <target>تقليل معدل التشغيل (يتطلب تركيز المشغل)</target> 8670 <target>تقليل معدل التشغيل (يتطلب تركيز المشغل)</target>
8640 8671
8641 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 8672 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
8642 <trans-unit id="3178343147230721210" datatype="html"> 8673 <trans-unit id="3178343147230721210" datatype="html">
8643 <source>Navigate in the video frame by frame (requires player focus)</source> 8674 <source>Navigate in the video frame by frame (requires player focus)</source>
8644 <target>تصفح الفيديو إطار فإطار (يتطلب تركيز المشغل)</target> 8675 <target>تصفح الفيديو إطار فإطار (يتطلب تركيز المشغل)</target>
8645 8676
8646 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 8677 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
8647 <trans-unit id="8025996572234182184" datatype="html"> 8678 <trans-unit id="8025996572234182184" datatype="html">
8648 <source>Like the video</source> 8679 <source>Like the video</source>
8649 <target>أعجبني الفيديو</target> 8680 <target>أعجبني الفيديو</target>
@@ -8769,35 +8800,35 @@ The link will expire within 1 hour.</source>
8769 <trans-unit id="3779524668013120370" datatype="html"> 8800 <trans-unit id="3779524668013120370" datatype="html">
8770 <source>Go to my subscriptions</source> 8801 <source>Go to my subscriptions</source>
8771 <target>انتقل إلى اشتراكاتي</target> 8802 <target>انتقل إلى اشتراكاتي</target>
8772 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8803
8773 </trans-unit> 8804 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8774 <trans-unit id="1136469849928650779" datatype="html"> 8805 <trans-unit id="1136469849928650779" datatype="html">
8775 <source>Go to my videos</source> 8806 <source>Go to my videos</source>
8776 <target>انتقل إلى فيديوهاتي</target> 8807 <target>انتقل إلى فيديوهاتي</target>
8777 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8808
8778 </trans-unit> 8809 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8779 <trans-unit id="7836683738999600376" datatype="html"> 8810 <trans-unit id="7836683738999600376" datatype="html">
8780 <source>Go to my imports</source> 8811 <source>Go to my imports</source>
8781 <target>انتقل إلى مستورداتي</target> 8812 <target>انتقل إلى مستورداتي</target>
8782 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8813
8783 </trans-unit> 8814 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8784 <trans-unit id="7511292153332773503" datatype="html"> 8815 <trans-unit id="7511292153332773503" datatype="html">
8785 <source>Go to my channels</source> 8816 <source>Go to my channels</source>
8786 <target>انتقل إلى قنواتي</target> 8817 <target>انتقل إلى قنواتي</target>
8787 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8818
8788 </trans-unit> 8819 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8789 <trans-unit id="2013324644839511073" datatype="html"> 8820 <trans-unit id="2013324644839511073" datatype="html">
8790 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8821 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8791Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8822Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8792 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8823 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8793Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8824Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8794 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8825
8795 </trans-unit> 8826 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8796 <trans-unit id="375263728166936544" datatype="html"> 8827 <trans-unit id="375263728166936544" datatype="html">
8797 <source>You need to reconnect.</source> 8828 <source>You need to reconnect.</source>
8798 <target>تحتاج لإعادة الإتصال.</target> 8829 <target>تحتاج لإعادة الإتصال.</target>
8799 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8830
8800 </trans-unit> 8831 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8801 <trans-unit id="2206638022166154361" datatype="html"> 8832 <trans-unit id="2206638022166154361" datatype="html">
8802 <source>Keyboard Shortcuts:</source> 8833 <source>Keyboard Shortcuts:</source>
8803 <target>اختصارات لوحة المفاتيح:</target> 8834 <target>اختصارات لوحة المفاتيح:</target>
@@ -8810,6 +8841,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8810 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8841 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8811 <context context-type="linenumber">98</context> 8842 <context context-type="linenumber">98</context>
8812 </context-group> 8843 </context-group>
8844 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8845 <source>In my library</source><target state="new">In my library</target>
8846 <context-group purpose="location">
8847 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8848 <context context-type="linenumber">104</context>
8849 </context-group>
8813 </trans-unit> 8850 </trans-unit>
8814 <trans-unit id="232050922346936574" datatype="html"> 8851 <trans-unit id="232050922346936574" datatype="html">
8815 <source>Trending</source> 8852 <source>Trending</source>
@@ -8838,18 +8875,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8838 <trans-unit id="968295009933361070" datatype="html"> 8875 <trans-unit id="968295009933361070" datatype="html">
8839 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 8876 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
8840 <target>أكثرت المحاولات، حاول لاحقا بعد <x id="PH"/> دقيقة.</target> 8877 <target>أكثرت المحاولات، حاول لاحقا بعد <x id="PH"/> دقيقة.</target>
8841 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 8878
8842 </trans-unit> 8879 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
8843 <trans-unit id="4965472196059235310" datatype="html"> 8880 <trans-unit id="4965472196059235310" datatype="html">
8844 <source>Too many attempts, please try again later.</source> 8881 <source>Too many attempts, please try again later.</source>
8845 <target>أكثرت المحاولات، حاول لاحقا.</target> 8882 <target>أكثرت المحاولات، حاول لاحقا.</target>
8846 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 8883
8847 </trans-unit> 8884 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
8848 <trans-unit id="1693549688987384699" datatype="html"> 8885 <trans-unit id="1693549688987384699" datatype="html">
8849 <source>Server error. Please retry later.</source> 8886 <source>Server error. Please retry later.</source>
8850 <target>خطأ في السيرفر. يرجى إعادة المحاولة لاحقا.</target> 8887 <target>خطأ في السيرفر. يرجى إعادة المحاولة لاحقا.</target>
8851 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 8888
8852 </trans-unit> 8889 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8853 <trans-unit id="4670312387769733978" datatype="html"> 8890 <trans-unit id="4670312387769733978" datatype="html">
8854 <source>All unsaved data will be lost, are you sure you want to leave this page?</source> 8891 <source>All unsaved data will be lost, are you sure you want to leave this page?</source>
8855 <target>ستفقد جميع البيانات غير المحفوظة ، هل تريد مغادرة هذه الصفحة؟</target> 8892 <target>ستفقد جميع البيانات غير المحفوظة ، هل تريد مغادرة هذه الصفحة؟</target>
@@ -8876,28 +8913,28 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8876 <trans-unit id="5633144232269377096" datatype="html"> 8913 <trans-unit id="5633144232269377096" datatype="html">
8877 <source>hide</source> 8914 <source>hide</source>
8878 <target>أخف</target> 8915 <target>أخف</target>
8879 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8916
8880 </trans-unit> 8917 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8881 <trans-unit id="8603861867909474404" datatype="html"> 8918 <trans-unit id="8603861867909474404" datatype="html">
8882 <source>blur</source> 8919 <source>blur</source>
8883 <target>طمس</target> 8920 <target>طمس</target>
8884 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8921
8885 </trans-unit> 8922 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8886 <trans-unit id="4534458451100881847" datatype="html"> 8923 <trans-unit id="4534458451100881847" datatype="html">
8887 <source>display</source> 8924 <source>display</source>
8888 <target>اعرض</target> 8925 <target>اعرض</target>
8889 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8926
8890 </trans-unit> 8927 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8891 <trans-unit id="4467323362722952678" datatype="html"> 8928 <trans-unit id="4467323362722952678" datatype="html">
8892 <source>Unknown</source> 8929 <source>Unknown</source>
8893 <target>مجهول</target> 8930 <target>مجهول</target>
8894 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8931
8895 </trans-unit> 8932 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8896 <trans-unit id="7939914198003891823" datatype="html"> 8933 <trans-unit id="7939914198003891823" datatype="html">
8897 <source>any language</source> 8934 <source>any language</source>
8898 <target>أي لغة</target> 8935 <target>أي لغة</target>
8899 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8936
8900 </trans-unit> 8937 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8901 <trans-unit id="9178182467454450952" datatype="html"> 8938 <trans-unit id="9178182467454450952" datatype="html">
8902 <source>Confirm</source> 8939 <source>Confirm</source>
8903 <target>أكد</target> 8940 <target>أكد</target>
@@ -8906,23 +8943,39 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8906 <trans-unit id="2127446333083057097" datatype="html"> 8943 <trans-unit id="2127446333083057097" datatype="html">
8907 <source>Domain is required.</source> 8944 <source>Domain is required.</source>
8908 <target>اسم النطاق مطلوب.</target> 8945 <target>اسم النطاق مطلوب.</target>
8909 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 8946
8910 </trans-unit> 8947 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
8911 <trans-unit id="6780793142903080663" datatype="html"> 8948 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
8912 <source>Domains entered are invalid.</source> 8949 <context-group purpose="location">
8913 <target>أسماء النطاقات المدخلة غير صالحة.</target> 8950 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
8914 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 8951 <context context-type="linenumber">93</context>
8915 </trans-unit> 8952 </context-group>
8916 <trans-unit id="5886492514458202177" datatype="html"> 8953 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
8917 <source>Domains entered contain duplicates.</source> 8954 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
8918 <target>تحتوي المجالات المدخلة على تكرارات.</target> 8955 <context-group purpose="location">
8919 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 8956 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
8957 <context context-type="linenumber">94</context>
8958 </context-group>
8959 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
8960 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
8961 <context-group purpose="location">
8962 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
8963 <context context-type="linenumber">102</context>
8964 </context-group>
8965 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
8966 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
8967 <context-group purpose="location">
8968 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
8969 <context context-type="linenumber">103</context>
8970 </context-group>
8920 </trans-unit> 8971 </trans-unit>
8972
8973
8921 <trans-unit id="2740793005745065895" datatype="html"> 8974 <trans-unit id="2740793005745065895" datatype="html">
8922 <source><x id="PH"/> is not valid </source> 8975 <source><x id="PH"/> is not valid </source>
8923 <target><x id="PH"/> غير صالح </target> 8976 <target><x id="PH"/> غير صالح </target>
8924 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 8977
8925 </trans-unit> 8978 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
8926 <trans-unit id="7784486624424057376" datatype="html"> 8979 <trans-unit id="7784486624424057376" datatype="html">
8927 <source>Instance name is required.</source> 8980 <source>Instance name is required.</source>
8928 <target>اسم مثيل الخادم مطلوب.</target> 8981 <target>اسم مثيل الخادم مطلوب.</target>
diff --git a/client/src/locale/angular.ca-ES.xlf b/client/src/locale/angular.ca-ES.xlf
index 38f39ab48..9b5195313 100644
--- a/client/src/locale/angular.ca-ES.xlf
+++ b/client/src/locale/angular.ca-ES.xlf
@@ -215,19 +215,19 @@
215 <trans-unit id="187187500641108332" datatype="html"> 215 <trans-unit id="187187500641108332" datatype="html">
216 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source> 216 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source>
217 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target> 217 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target>
218 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group> 218
219 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group> 219
220 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group> 220
221 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group> 221
222 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group> 222
223 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 223
224 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group> 224
225 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group> 225
226 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group> 226
227 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group> 227
228 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group> 228
229 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group> 229
230 </trans-unit> 230 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
231 <trans-unit id="1486537403020619891" datatype="html"> 231 <trans-unit id="1486537403020619891" datatype="html">
232 <source>My watch history</source> 232 <source>My watch history</source>
233 <target state="new">My watch history</target> 233 <target state="new">My watch history</target>
@@ -296,22 +296,22 @@
296 <trans-unit id="5674286808255988565"> 296 <trans-unit id="5674286808255988565">
297 <source>Create</source> 297 <source>Create</source>
298 <target>Crea</target> 298 <target>Crea</target>
299 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 299
300 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 300
301 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 301
302 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 302
303 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 303
304 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 304
305 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 305
306 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 306
307 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 307
308 </trans-unit> 308 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
309 <trans-unit id="1006562256968398209" datatype="html"> 309 <trans-unit id="1006562256968398209" datatype="html">
310 <source>video</source> 310 <source>video</source>
311 <target state="translated">vídeo</target> 311 <target state="translated">vídeo</target>
312 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group> 312
313 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group> 313
314 </trans-unit> 314 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
315 <trans-unit id="6438815964972582865" datatype="html"> 315 <trans-unit id="6438815964972582865" datatype="html">
316 <source>The following link contains a private token and should not be shared with anyone.</source> 316 <source>The following link contains a private token and should not be shared with anyone.</source>
317 <target state="translated">El següent enllaç conté un token privat i no es deuria compartir amb cap persona.</target> 317 <target state="translated">El següent enllaç conté un token privat i no es deuria compartir amb cap persona.</target>
@@ -381,13 +381,13 @@
381 <trans-unit id="6995024616159044376" datatype="html"> 381 <trans-unit id="6995024616159044376" datatype="html">
382 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 382 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
383 <target state="translated">La seua cuota de vídeo s'excedeix amb aquest vídeo (tamany del vídeo: <x id="PH" equiv-text="videoSizeBytes"/>, utilitzat: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 383 <target state="translated">La seua cuota de vídeo s'excedeix amb aquest vídeo (tamany del vídeo: <x id="PH" equiv-text="videoSizeBytes"/>, utilitzat: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
384 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 384
385 </trans-unit> 385 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
386 <trans-unit id="7873395933409147217" datatype="html"> 386 <trans-unit id="7873395933409147217" datatype="html">
387 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 387 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
388 <target state="translated">La seua quota diària de vídeo s'excedeix amb aquest vídeo (tamany del vídeo: <x id="PH" equiv-text="videoSizeBytes"/>, utilitzat: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 388 <target state="translated">La seua quota diària de vídeo s'excedeix amb aquest vídeo (tamany del vídeo: <x id="PH" equiv-text="videoSizeBytes"/>, utilitzat: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
389 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 389
390 </trans-unit> 390 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
391 <trans-unit id="5235042777215655908" datatype="html"> 391 <trans-unit id="5235042777215655908" datatype="html">
392 <source>subtitles</source> 392 <source>subtitles</source>
393 <target state="translated">subtítols</target> 393 <target state="translated">subtítols</target>
@@ -837,10 +837,10 @@
837 <trans-unit id="2602586221576511475"> 837 <trans-unit id="2602586221576511475">
838 <source>Video quota</source> 838 <source>Video quota</source>
839 <target>Quota de vídeo</target> 839 <target>Quota de vídeo</target>
840 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 840
841 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 841
842 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 842
843 </trans-unit> 843 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
844 <trans-unit id="1502595455339510144" datatype="html"> 844 <trans-unit id="1502595455339510144" datatype="html">
845 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 845 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
846 <target state="translated">Sense limit 846 <target state="translated">Sense limit
@@ -924,6 +924,30 @@
924 <target state="translated">Federació</target> 924 <target state="translated">Federació</target>
925 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 925 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
926 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 926 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
927 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
928 <source>Following</source><target state="new">Following</target>
929 <context-group purpose="location">
930 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
931 <context context-type="linenumber">29</context>
932 </context-group>
933 <context-group purpose="location">
934 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
935 <context context-type="linenumber">31</context>
936 </context-group>
937 <context-group purpose="location">
938 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
939 <context context-type="linenumber">28</context>
940 </context-group>
941 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
942 <source>Followers</source><target state="new">Followers</target>
943 <context-group purpose="location">
944 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
945 <context context-type="linenumber">34</context>
946 </context-group>
947 <context-group purpose="location">
948 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
949 <context context-type="linenumber">37</context>
950 </context-group>
927 </trans-unit> 951 </trans-unit>
928 <trans-unit id="3541687134897970106" datatype="html"> 952 <trans-unit id="3541687134897970106" datatype="html">
929 <source>followers</source> 953 <source>followers</source>
@@ -985,25 +1009,25 @@
985 <trans-unit id="2159130950882492111" datatype="html"> 1009 <trans-unit id="2159130950882492111" datatype="html">
986 <source>Cancel</source> 1010 <source>Cancel</source>
987 <target state="translated">Cancel·la</target> 1011 <target state="translated">Cancel·la</target>
988 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group> 1012
989 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group> 1013
990 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group> 1014
991 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group> 1015
992 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group> 1016
993 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group> 1017
994 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group> 1018
995 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group> 1019
996 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group> 1020
997 <context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group> 1021
998 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group> 1022
999 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group> 1023
1000 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 1024
1001 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 1025
1002 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group> 1026
1003 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group> 1027
1004 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group> 1028
1005 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group> 1029
1006 </trans-unit> 1030 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
1007 <trans-unit id="3616223838716839702" datatype="html"> 1031 <trans-unit id="3616223838716839702" datatype="html">
1008 <source>Ban this user</source> 1032 <source>Ban this user</source>
1009 <target state="translated">Expulsa aquesta usuaria</target> 1033 <target state="translated">Expulsa aquesta usuaria</target>
@@ -1068,19 +1092,13 @@
1068 <trans-unit id="7252854992688790751" datatype="html"> 1092 <trans-unit id="7252854992688790751" datatype="html">
1069 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1093 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1070 <target state="translated">Aquesta instància permet el registre. No obstant aixó , tinga cura de comprovar les <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Condicions <x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> <x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Condicions <x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> abans de crear un compte. També pot buscar una altra instància que coincideixi amb les vostres necessitats: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/> <x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1094 <target state="translated">Aquesta instància permet el registre. No obstant aixó , tinga cura de comprovar les <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Condicions <x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> <x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Condicions <x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> abans de crear un compte. També pot buscar una altra instància que coincideixi amb les vostres necessitats: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/> <x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1071 <context-group purpose="location"> 1095
1072 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1096 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1073 <context context-type="linenumber">60,62</context>
1074 </context-group>
1075 </trans-unit>
1076 <trans-unit id="7215649348148521605" datatype="html"> 1097 <trans-unit id="7215649348148521605" datatype="html">
1077 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1098 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1078 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1099 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1079 <context-group purpose="location"> 1100
1080 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1101 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1081 <context context-type="linenumber">65,67</context>
1082 </context-group>
1083 </trans-unit>
1084 <trans-unit id="2392488717875840729"> 1102 <trans-unit id="2392488717875840729">
1085 <source>User</source> 1103 <source>User</source>
1086 <target>Usuari</target> 1104 <target>Usuari</target>
@@ -1091,67 +1109,67 @@
1091 <source>Username or email address</source> 1109 <source>Username or email address</source>
1092 <target>Nom d'usuari o adreça de correu electrònic</target> 1110 <target>Nom d'usuari o adreça de correu electrònic</target>
1093 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1111 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1112 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1113 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1114 <context-group purpose="location">
1115 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1116 <context context-type="linenumber">33,34</context>
1117 </context-group>
1094 </trans-unit> 1118 </trans-unit>
1095 <trans-unit id="1431416938026210429"> 1119 <trans-unit id="1431416938026210429">
1096 <source>Password</source> 1120 <source>Password</source>
1097 <target>Contrasenya</target> 1121 <target>Contrasenya</target>
1098 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1122
1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1123
1100 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1124
1101 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1125
1102 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1126
1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1127
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1128
1105 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1129
1106 </trans-unit> 1130 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1107 <trans-unit id="8715156686857791956" datatype="html"> 1131 <trans-unit id="8715156686857791956" datatype="html">
1108 <source>Click here to reset your password</source> 1132 <source>Click here to reset your password</source>
1109 <target state="translated">Premeu aquí per restablir la contrasenya</target> 1133 <target state="translated">Premeu aquí per restablir la contrasenya</target>
1110 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1134
1111 </trans-unit> 1135 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1112 <trans-unit id="892063502898494584" datatype="html"> 1136 <trans-unit id="892063502898494584" datatype="html">
1113 <source>I forgot my password</source> 1137 <source>I forgot my password</source>
1114 <target state="translated">He oblidat la meua contrasenya</target> 1138 <target state="translated">He oblidat la meua contrasenya</target>
1115 <context-group purpose="location"> 1139
1116 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1140 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1117 <context context-type="linenumber">47</context>
1118 </context-group>
1119 </trans-unit>
1120 <trans-unit id="2101170466365500913" datatype="html"> 1141 <trans-unit id="2101170466365500913" datatype="html">
1121 <source>Logging into an account lets you publish content</source> 1142 <source>Logging into an account lets you publish content</source>
1122 <target state="translated">Iniciar sessió amb un compte et permet publicar contingut</target> 1143 <target state="translated">Iniciar sessió amb un compte et permet publicar contingut</target>
1123 <context-group purpose="location"> 1144
1124 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1145 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1125 <context context-type="linenumber">56,57</context>
1126 </context-group>
1127 </trans-unit>
1128 <trans-unit id="2454050363478003966"> 1146 <trans-unit id="2454050363478003966">
1129 <source>Login</source> 1147 <source>Login</source>
1130 <target>Inicia sessió</target> 1148 <target>Inicia sessió</target>
1131 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1149
1132 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1150
1133 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1151
1134 </trans-unit> 1152 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1135 <trans-unit id="3183213940445113677" datatype="html"> 1153 <trans-unit id="3183213940445113677" datatype="html">
1136 <source>Or sign in with</source> 1154 <source>Or sign in with</source>
1137 <target state="translated">O identifiqueu-vos amb</target> 1155 <target state="translated">O identifiqueu-vos amb</target>
1138 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1156
1139 </trans-unit> 1157 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1140 <trans-unit id="3238209155172574367"> 1158 <trans-unit id="3238209155172574367">
1141 <source>Forgot your password</source> 1159 <source>Forgot your password</source>
1142 <target>Has oblidat la teva contrasenya</target> 1160 <target>Has oblidat la teva contrasenya</target>
1143 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1161
1144 </trans-unit> 1162 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1145 <trans-unit id="87327320394367488" datatype="html"> 1163 <trans-unit id="87327320394367488" datatype="html">
1146 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1164 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1147 <target state="translated">No podem recuperar la vostra contrasenya perquè l'administració de la vostra instància no ha configurat cap sistema de correus de PeerTube.</target> 1165 <target state="translated">No podem recuperar la vostra contrasenya perquè l'administració de la vostra instància no ha configurat cap sistema de correus de PeerTube.</target>
1148 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1166
1149 </trans-unit> 1167 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1150 <trans-unit id="3188014010833256853" datatype="html"> 1168 <trans-unit id="3188014010833256853" datatype="html">
1151 <source>Enter your email address and we will send you a link to reset your password.</source> 1169 <source>Enter your email address and we will send you a link to reset your password.</source>
1152 <target state="translated">Inseriu la vostra adreça de correu electrònic i us enviarem un enllaç per a restablir la contrasenya.</target> 1170 <target state="translated">Inseriu la vostra adreça de correu electrònic i us enviarem un enllaç per a restablir la contrasenya.</target>
1153 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1171
1154 </trans-unit> 1172 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1155 <trans-unit id="1190256911880544559" datatype="html"> 1173 <trans-unit id="1190256911880544559" datatype="html">
1156 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1174 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1157The link will expire within 1 hour.</source> 1175The link will expire within 1 hour.</source>
@@ -1161,26 +1179,26 @@ The link will expire within 1 hour.</source>
1161 <trans-unit id="4768749765465246664"> 1179 <trans-unit id="4768749765465246664">
1162 <source>Email</source> 1180 <source>Email</source>
1163 <target>Correu</target> 1181 <target>Correu</target>
1164 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1182
1165 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1183
1166 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1184
1167 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1185
1168 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1186
1169 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1187
1170 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1188
1171 </trans-unit> 1189 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1172 <trans-unit id="3967269098753656610"> 1190 <trans-unit id="3967269098753656610">
1173 <source>Email address</source> 1191 <source>Email address</source>
1174 <target>Adreça de correu</target> 1192 <target>Adreça de correu</target>
1175 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1193
1176 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1194
1177 </trans-unit> 1195 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1178 <trans-unit id="7808756054397155068" datatype="html"> 1196 <trans-unit id="7808756054397155068" datatype="html">
1179 <source>Reset</source> 1197 <source>Reset</source>
1180 <target state="translated">Restableix</target> 1198 <target state="translated">Restableix</target>
1181 <note priority="1" from="description">Password reset button</note> 1199 <note priority="1" from="description">Password reset button</note>
1182 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1200
1183 </trans-unit> 1201 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1184 <trans-unit id="4319634264526091601" datatype="html"> 1202 <trans-unit id="4319634264526091601" datatype="html">
1185 <source>on this instance</source> 1203 <source>on this instance</source>
1186 <target state="translated">en aquesta instància</target> 1204 <target state="translated">en aquesta instància</target>
@@ -1531,9 +1549,9 @@ The link will expire within 1 hour.</source>
1531 <trans-unit id="2308975396733519902"> 1549 <trans-unit id="2308975396733519902">
1532 <source>Create an account</source> 1550 <source>Create an account</source>
1533 <target>Registrar un compte</target> 1551 <target>Registrar un compte</target>
1534 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1552
1535 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1553
1536 </trans-unit> 1554 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1537 <trans-unit id="3058024914967508975" datatype="html"> 1555 <trans-unit id="3058024914967508975" datatype="html">
1538 <source>My videos</source> 1556 <source>My videos</source>
1539 <target state="new">My videos</target> 1557 <target state="new">My videos</target>
@@ -1600,10 +1618,10 @@ The link will expire within 1 hour.</source>
1600 <trans-unit id="1504521795586863905" datatype="html"> 1618 <trans-unit id="1504521795586863905" datatype="html">
1601 <source>VIDEOS</source> 1619 <source>VIDEOS</source>
1602 <target state="new">VIDEOS</target> 1620 <target state="new">VIDEOS</target>
1603 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1621
1604 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1622
1605 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1623
1606 </trans-unit> 1624 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1607 <trans-unit id="667372110624203230" datatype="html"> 1625 <trans-unit id="667372110624203230" datatype="html">
1608 <source>Import jobs concurrency</source> 1626 <source>Import jobs concurrency</source>
1609 <target state="new">Import jobs concurrency</target> 1627 <target state="new">Import jobs concurrency</target>
@@ -1682,8 +1700,8 @@ The link will expire within 1 hour.</source>
1682 <trans-unit id="4424964105331349857" datatype="html"> 1700 <trans-unit id="4424964105331349857" datatype="html">
1683 <source>I'm a teapot</source> 1701 <source>I'm a teapot</source>
1684 <target state="translated">Sóc una tetera</target> 1702 <target state="translated">Sóc una tetera</target>
1685 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1703
1686 </trans-unit> 1704 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1687 <trans-unit id="1597262876035959248" datatype="html"> 1705 <trans-unit id="1597262876035959248" datatype="html">
1688 <source>That's an error.</source> 1706 <source>That's an error.</source>
1689 <target state="translated">Això és un error.</target> 1707 <target state="translated">Això és un error.</target>
@@ -1776,8 +1794,8 @@ The link will expire within 1 hour.</source>
1776 <trans-unit id="2971365540217107489" datatype="html"> 1794 <trans-unit id="2971365540217107489" datatype="html">
1777 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1795 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1778 <target state="translated">El mitjà es massa gran per al servidor. Contacta amb l'administrador si desitja augmentar la grandària límit.</target> 1796 <target state="translated">El mitjà es massa gran per al servidor. Contacta amb l'administrador si desitja augmentar la grandària límit.</target>
1779 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1797
1780 </trans-unit> 1798 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1781 <trans-unit id="5131854469652959713" datatype="html"> 1799 <trans-unit id="5131854469652959713" datatype="html">
1782 <source>GLOBAL SEARCH</source> 1800 <source>GLOBAL SEARCH</source>
1783 <target state="translated">CERCA GLOBAL</target> 1801 <target state="translated">CERCA GLOBAL</target>
@@ -2482,13 +2500,13 @@ The link will expire within 1 hour.</source>
2482 <trans-unit id="9172233176401579786" datatype="html"> 2500 <trans-unit id="9172233176401579786" datatype="html">
2483 <source>Scheduled</source> 2501 <source>Scheduled</source>
2484 <target state="translated">Programat</target> 2502 <target state="translated">Programat</target>
2485 2503 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-edit.component.ts</context><context context-type="linenumber">192</context></context-group>
2486 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-edit.component.ts</context><context context-type="linenumber">192</context></context-group></trans-unit> 2504 </trans-unit>
2487 <trans-unit id="1435317307066082710" datatype="html"> 2505 <trans-unit id="1435317307066082710" datatype="html">
2488 <source>Hide the video until a specific date</source> 2506 <source>Hide the video until a specific date</source>
2489 <target state="new">Hide the video until a specific date</target> 2507 <target state="new">Hide the video until a specific date</target>
2490 2508 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-edit.component.ts</context><context context-type="linenumber">193</context></context-group>
2491 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-edit.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 2509 </trans-unit>
2492 <trans-unit id="6148369758871787018" datatype="html"> 2510 <trans-unit id="6148369758871787018" datatype="html">
2493 <source>Video background image</source> 2511 <source>Video background image</source>
2494 <target state="translated">Imatge de fons del vídeo</target> 2512 <target state="translated">Imatge de fons del vídeo</target>
@@ -2542,8 +2560,8 @@ The link will expire within 1 hour.</source>
2542 <trans-unit id="6161604372916832458" datatype="html"> 2560 <trans-unit id="6161604372916832458" datatype="html">
2543 <source>Upload on hold</source> 2561 <source>Upload on hold</source>
2544 <target state="new">Upload on hold</target> 2562 <target state="new">Upload on hold</target>
2545 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2563
2546 </trans-unit> 2564 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2547 <trans-unit id="285180972645018518" datatype="html"> 2565 <trans-unit id="285180972645018518" datatype="html">
2548 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2566 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2549 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2567 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3276,11 +3294,7 @@ The link will expire within 1 hour.</source>
3276 <target>ID</target> 3294 <target>ID</target>
3277 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3295 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3278 </trans-unit> 3296 </trans-unit>
3279 <trans-unit id="2265605798180116441" datatype="html"> 3297
3280 <source>Follower handle</source>
3281 <target state="new">Follower handle</target>
3282 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3283 </trans-unit>
3284 <trans-unit id="5911214550882917183"> 3298 <trans-unit id="5911214550882917183">
3285 <source>State</source> 3299 <source>State</source>
3286 <target>Estat</target> 3300 <target>Estat</target>
@@ -3355,11 +3369,7 @@ The link will expire within 1 hour.</source>
3355 </target> 3369 </target>
3356 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3370 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3357 </trans-unit> 3371 </trans-unit>
3358 <trans-unit id="6641024648411549335"> 3372
3359 <source>Host</source>
3360 <target>Amfitrió</target>
3361 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3362 </trans-unit>
3363 <trans-unit id="6571718060636962350" datatype="html"> 3373 <trans-unit id="6571718060636962350" datatype="html">
3364 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3374 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3365 <target state="new">Redundancy allowed 3375 <target state="new">Redundancy allowed
@@ -3371,9 +3381,9 @@ The link will expire within 1 hour.</source>
3371 <trans-unit id="9160510009013134726" datatype="html"> 3381 <trans-unit id="9160510009013134726" datatype="html">
3372 <source>Unfollow</source> 3382 <source>Unfollow</source>
3373 <target state="new">Unfollow</target> 3383 <target state="new">Unfollow</target>
3374 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3384
3375 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3385
3376 </trans-unit> 3386 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3377 <trans-unit id="8246779176913476983" datatype="html"> 3387 <trans-unit id="8246779176913476983" datatype="html">
3378 <source>Open instance in a new tab</source> 3388 <source>Open instance in a new tab</source>
3379 <target state="new">Open instance in a new tab</target> 3389 <target state="new">Open instance in a new tab</target>
@@ -3384,13 +3394,13 @@ The link will expire within 1 hour.</source>
3384 <trans-unit id="9132918641931433659" datatype="html"> 3394 <trans-unit id="9132918641931433659" datatype="html">
3385 <source>No host found matching current filters.</source> 3395 <source>No host found matching current filters.</source>
3386 <target state="new">No host found matching current filters.</target> 3396 <target state="new">No host found matching current filters.</target>
3387 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3397
3388 </trans-unit> 3398 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3389 <trans-unit id="7274241885665071790" datatype="html"> 3399 <trans-unit id="7274241885665071790" datatype="html">
3390 <source>Your instance is not following anyone.</source> 3400 <source>Your instance is not following anyone.</source>
3391 <target state="new">Your instance is not following anyone.</target> 3401 <target state="new">Your instance is not following anyone.</target>
3392 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3402
3393 </trans-unit> 3403 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3394 <trans-unit id="4774348799569692380" datatype="html"> 3404 <trans-unit id="4774348799569692380" datatype="html">
3395 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3405 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3396 <target state="new">Showing 3406 <target state="new">Showing
@@ -3400,16 +3410,8 @@ The link will expire within 1 hour.</source>
3400 </target> 3410 </target>
3401 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3411 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3402 </trans-unit> 3412 </trans-unit>
3403 <trans-unit id="6275803119759621687" datatype="html"> 3413
3404 <source>Follow domains</source> 3414
3405 <target state="new">Follow domains</target>
3406 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3407 </trans-unit>
3408 <trans-unit id="1268699198448750610" datatype="html">
3409 <source>Follow instances</source>
3410 <target state="new">Follow instances</target>
3411 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3412 </trans-unit>
3413 <trans-unit id="9216117865911519658" datatype="html"> 3415 <trans-unit id="9216117865911519658" datatype="html">
3414 <source>Action</source> 3416 <source>Action</source>
3415 <target state="new">Action</target> 3417 <target state="new">Action</target>
@@ -3438,9 +3440,9 @@ The link will expire within 1 hour.</source>
3438 <trans-unit id="8286337167859377104"> 3440 <trans-unit id="8286337167859377104">
3439 <source>Create user</source> 3441 <source>Create user</source>
3440 <target>Afegeix un usuari</target> 3442 <target>Afegeix un usuari</target>
3441 3443 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-create.component.ts</context><context context-type="linenumber">93</context></context-group>
3442 3444 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.html</context><context context-type="linenumber">20</context></context-group>
3443 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-create.component.ts</context><context context-type="linenumber">93</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.html</context><context context-type="linenumber">20</context></context-group></trans-unit> 3445 </trans-unit>
3444 <trans-unit id="8363291180171434623" datatype="html"> 3446 <trans-unit id="8363291180171434623" datatype="html">
3445 <source>Table parameters</source> 3447 <source>Table parameters</source>
3446 <target state="new">Table parameters</target> 3448 <target state="new">Table parameters</target>
@@ -3459,11 +3461,11 @@ The link will expire within 1 hour.</source>
3459 <trans-unit id="5248717555542428023"> 3461 <trans-unit id="5248717555542428023">
3460 <source>Username</source> 3462 <source>Username</source>
3461 <target>Nom d'usuari</target> 3463 <target>Nom d'usuari</target>
3462 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3464
3463 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3465
3464 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3466
3465 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3467
3466 </trans-unit> 3468 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3467 <trans-unit id="5428411040014095392" datatype="html"> 3469 <trans-unit id="5428411040014095392" datatype="html">
3468 <source>e.g. jane_doe</source> 3470 <source>e.g. jane_doe</source>
3469 <target state="new">e.g. jane_doe</target> 3471 <target state="new">e.g. jane_doe</target>
@@ -3493,9 +3495,9 @@ The link will expire within 1 hour.</source>
3493 <trans-unit id="4145496584631696119"> 3495 <trans-unit id="4145496584631696119">
3494 <source>Role</source> 3496 <source>Role</source>
3495 <target>Rol</target> 3497 <target>Rol</target>
3496 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3498
3497 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3499
3498 </trans-unit> 3500 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3499 <trans-unit id="7046347992315328430" datatype="html"> 3501 <trans-unit id="7046347992315328430" datatype="html">
3500 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3502 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3501 <target state="new"> 3503 <target state="new">
@@ -3520,15 +3522,9 @@ The link will expire within 1 hour.</source>
3520 <trans-unit id="2622255144026150901" datatype="html"> 3522 <trans-unit id="2622255144026150901" datatype="html">
3521 <source>Auth plugin</source> 3523 <source>Auth plugin</source>
3522 <target state="new">Auth plugin</target> 3524 <target state="new">Auth plugin</target>
3523 <context-group purpose="location"> 3525
3524 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3526
3525 <context context-type="linenumber">188</context> 3527 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3526 </context-group>
3527 <context-group purpose="location">
3528 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3529 <context context-type="linenumber">188</context>
3530 </context-group>
3531 </trans-unit>
3532 <trans-unit id="588099657508661970" datatype="html"> 3528 <trans-unit id="588099657508661970" datatype="html">
3533 <source>None (local authentication)</source> 3529 <source>None (local authentication)</source>
3534 <target state="new">None (local authentication)</target> 3530 <target state="new">None (local authentication)</target>
@@ -3793,6 +3789,12 @@ The link will expire within 1 hour.</source>
3793 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3789 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3794 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3790 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3795 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3791 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3792 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3793 <source>Follower</source><target state="new">Follower</target>
3794 <context-group purpose="location">
3795 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3796 <context context-type="linenumber">24</context>
3797 </context-group>
3796 </trans-unit> 3798 </trans-unit>
3797 <trans-unit id="4691552465058437520" datatype="html"> 3799 <trans-unit id="4691552465058437520" datatype="html">
3798 <source>Commented video</source> 3800 <source>Commented video</source>
@@ -4134,8 +4136,8 @@ The link will expire within 1 hour.</source>
4134 <target state="new"> 4136 <target state="new">
4135 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 4137 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
4136 </target> 4138 </target>
4137 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 4139
4138 </trans-unit> 4140 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
4139 <trans-unit id="4058814854824495833" datatype="html"> 4141 <trans-unit id="4058814854824495833" datatype="html">
4140 <source>Mute domains</source> 4142 <source>Mute domains</source>
4141 <target state="new">Mute domains</target> 4143 <target state="new">Mute domains</target>
@@ -6144,11 +6146,8 @@ color: red;
6144 <trans-unit id="5512878593724620692" datatype="html"> 6146 <trans-unit id="5512878593724620692" datatype="html">
6145 <source>CHANNELS</source> 6147 <source>CHANNELS</source>
6146 <target state="new">CHANNELS</target> 6148 <target state="new">CHANNELS</target>
6147 <context-group purpose="location"> 6149
6148 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 6150 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
6149 <context context-type="linenumber">82</context>
6150 </context-group>
6151 </trans-unit>
6152 <trans-unit id="3666829335406793239" datatype="html"> 6151 <trans-unit id="3666829335406793239" datatype="html">
6153 <source>This account does not have channels.</source> 6152 <source>This account does not have channels.</source>
6154 <target state="new">This account does not have channels.</target> 6153 <target state="new">This account does not have channels.</target>
@@ -6193,6 +6192,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6193It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 6192It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
6194channel with the same name (<x id="PH_2"/>)!</target> 6193channel with the same name (<x id="PH_2"/>)!</target>
6195 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 6194 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
6195 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
6196 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
6197 <context-group purpose="location">
6198 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
6199 <context context-type="linenumber">48</context>
6200 </context-group>
6196 </trans-unit> 6201 </trans-unit>
6197 <trans-unit id="5387007581996837469" datatype="html"> 6202 <trans-unit id="5387007581996837469" datatype="html">
6198 <source>My Channels</source> 6203 <source>My Channels</source>
@@ -6782,13 +6787,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6782 <trans-unit id="6979021199788941693" datatype="html"> 6787 <trans-unit id="6979021199788941693" datatype="html">
6783 <source>Your message has been sent.</source> 6788 <source>Your message has been sent.</source>
6784 <target state="new">Your message has been sent.</target> 6789 <target state="new">Your message has been sent.</target>
6785 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group> 6790
6786 </trans-unit> 6791 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6787 <trans-unit id="2072135752262464360" datatype="html"> 6792 <trans-unit id="2072135752262464360" datatype="html">
6788 <source>You already sent this form recently</source> 6793 <source>You already sent this form recently</source>
6789 <target state="new">You already sent this form recently</target> 6794 <target state="new">You already sent this form recently</target>
6790 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group> 6795
6791 </trans-unit> 6796 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6792 <trans-unit id="819067926858619041" datatype="html"> 6797 <trans-unit id="819067926858619041" datatype="html">
6793 <source>Account videos</source> 6798 <source>Account videos</source>
6794 <target state="new">Account videos</target> 6799 <target state="new">Account videos</target>
@@ -6835,13 +6840,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6835 <target state="new"> 6840 <target state="new">
6836 <x id="PH"/> direct account followers 6841 <x id="PH"/> direct account followers
6837 </target> 6842 </target>
6838 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6843
6839 </trans-unit> 6844 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6840 <trans-unit id="6250999352462648289" datatype="html"> 6845 <trans-unit id="6250999352462648289" datatype="html">
6841 <source>Report this account</source> 6846 <source>Report this account</source>
6842 <target state="new">Report this account</target> 6847 <target state="new">Report this account</target>
6843 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6848
6844 </trans-unit> 6849 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6845 <trans-unit id="1504521795586863905" datatype="html"> 6850 <trans-unit id="1504521795586863905" datatype="html">
6846 <source>VIDEOS</source> 6851 <source>VIDEOS</source>
6847 <target state="translated">VÍDEOS</target> 6852 <target state="translated">VÍDEOS</target>
@@ -6851,31 +6856,21 @@ channel with the same name (<x id="PH_2"/>)!</target>
6851 <trans-unit id="25349740244798533" datatype="html"> 6856 <trans-unit id="25349740244798533" datatype="html">
6852 <source>Username copied</source> 6857 <source>Username copied</source>
6853 <target state="new">Username copied</target> 6858 <target state="new">Username copied</target>
6854 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6859
6855 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6860
6856 </trans-unit> 6861 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6857 <trans-unit id="9221735175659318025" datatype="html"> 6862 <trans-unit id="9221735175659318025" datatype="html">
6858 <source>1 subscriber</source> 6863 <source>1 subscriber</source>
6859 <target state="new">1 subscriber</target> 6864 <target state="new">1 subscriber</target>
6860 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6865
6861 </trans-unit> 6866 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6862 <trans-unit id="4097331874769079975" datatype="html"> 6867 <trans-unit id="4097331874769079975" datatype="html">
6863 <source><x id="PH"/> subscribers</source> 6868 <source><x id="PH"/> subscribers</source>
6864 <target state="new"><x id="PH"/> subscribers</target> 6869 <target state="new"><x id="PH"/> subscribers</target>
6865 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6870
6866 </trans-unit> 6871 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6867 <trans-unit id="4682675125751819107" datatype="html"> 6872
6868 <source>Instances you follow</source> 6873
6869 <target state="new">Instances you follow</target>
6870 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6871 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6872 </trans-unit>
6873 <trans-unit id="8899833753704589712" datatype="html">
6874 <source>Instances following you</source>
6875 <target state="new">Instances following you</target>
6876 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6877 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6878 </trans-unit>
6879 <trans-unit id="1035838766454786107" datatype="html"> 6874 <trans-unit id="1035838766454786107" datatype="html">
6880 <source>Audio-only</source> 6875 <source>Audio-only</source>
6881 <target state="new">Audio-only</target> 6876 <target state="new">Audio-only</target>
@@ -6925,6 +6920,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6925 <source>Auto (via ffmpeg)</source> 6920 <source>Auto (via ffmpeg)</source>
6926 <target state="new">Auto (via ffmpeg)</target> 6921 <target state="new">Auto (via ffmpeg)</target>
6927 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6922 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6923 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6924 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6925 <context-group purpose="location">
6926 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6927 <context context-type="linenumber">3</context>
6928 </context-group>
6928 </trans-unit> 6929 </trans-unit>
6929 <trans-unit id="931255636742351800" datatype="html"> 6930 <trans-unit id="931255636742351800" datatype="html">
6930 <source>No limit</source> 6931 <source>No limit</source>
@@ -7075,18 +7076,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
7075 <trans-unit id="2127446333083057097" datatype="html"> 7076 <trans-unit id="2127446333083057097" datatype="html">
7076 <source>Domain is required.</source> 7077 <source>Domain is required.</source>
7077 <target state="new">Domain is required.</target> 7078 <target state="new">Domain is required.</target>
7078 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 7079
7079 </trans-unit> 7080 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
7080 <trans-unit id="6780793142903080663" datatype="html"> 7081 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
7081 <source>Domains entered are invalid.</source> 7082 <context-group purpose="location">
7082 <target state="new">Domains entered are invalid.</target> 7083 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7083 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 7084 <context context-type="linenumber">93</context>
7084 </trans-unit> 7085 </context-group>
7085 <trans-unit id="5886492514458202177" datatype="html"> 7086 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
7086 <source>Domains entered contain duplicates.</source> 7087 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
7087 <target state="new">Domains entered contain duplicates.</target> 7088 <context-group purpose="location">
7088 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 7089 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7090 <context context-type="linenumber">94</context>
7091 </context-group>
7092 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
7093 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
7094 <context-group purpose="location">
7095 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7096 <context context-type="linenumber">102</context>
7097 </context-group>
7098 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
7099 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
7100 <context-group purpose="location">
7101 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7102 <context context-type="linenumber">103</context>
7103 </context-group>
7089 </trans-unit> 7104 </trans-unit>
7105
7106
7090 <trans-unit id="240806681889331244"> 7107 <trans-unit id="240806681889331244">
7091 <source>Unlimited</source> 7108 <source>Unlimited</source>
7092 <target>Il·limitat</target> 7109 <target>Il·limitat</target>
@@ -7246,26 +7263,52 @@ channel with the same name (<x id="PH_2"/>)!</target>
7246 <x id="PH"/> removed from instance followers 7263 <x id="PH"/> removed from instance followers
7247 </target> 7264 </target>
7248 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 7265 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
7266 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
7267 <source>Follow</source><target state="new">Follow</target>
7268 <context-group purpose="location">
7269 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7270 <context context-type="linenumber">3</context>
7271 </context-group>
7272 <context-group purpose="location">
7273 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7274 <context context-type="linenumber">37</context>
7275 </context-group>
7276 <context-group purpose="location">
7277 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7278 <context context-type="linenumber">18</context>
7279 </context-group>
7280 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
7281 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
7282 <context-group purpose="location">
7283 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7284 <context context-type="linenumber">11</context>
7285 </context-group>
7249 </trans-unit> 7286 </trans-unit>
7250 <trans-unit id="2740793005745065895"> 7287 <trans-unit id="2740793005745065895">
7251 <source><x id="PH"/> is not valid </source> 7288 <source><x id="PH"/> is not valid </source>
7252 <target> 7289 <target>
7253 <x id="PH"/> no és vàlid 7290 <x id="PH"/> no és vàlid
7254 </target> 7291 </target>
7255 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 7292
7256 </trans-unit> 7293 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
7257 <trans-unit id="2355066641781598196"> 7294 <trans-unit id="2355066641781598196">
7258 <source>Follow request(s) sent!</source> 7295 <source>Follow request(s) sent!</source>
7259 <target>Sol·licituds de seguiment enviades!</target> 7296 <target>Sol·licituds de seguiment enviades!</target>
7260 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 7297
7298 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
7299 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
7300 <context-group purpose="location">
7301 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7302 <context context-type="linenumber">3</context>
7303 </context-group>
7261 </trans-unit> 7304 </trans-unit>
7262 <trans-unit id="4245720728052819482"> 7305 <trans-unit id="4245720728052819482">
7263 <source>Do you really want to unfollow <x id="PH"/>?</source> 7306 <source>Do you really want to unfollow <x id="PH"/>?</source>
7264 <target>Realment vols deixar de seguir 7307 <target>Realment vols deixar de seguir
7265 <x id="PH"/>? 7308 <x id="PH"/>?
7266 </target> 7309 </target>
7267 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 7310
7268 </trans-unit> 7311 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
7269 <trans-unit id="9160510009013134726"> 7312 <trans-unit id="9160510009013134726">
7270 <source>Unfollow</source> 7313 <source>Unfollow</source>
7271 <target>No segueixis</target> 7314 <target>No segueixis</target>
@@ -7276,8 +7319,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7276 <target>Ja no segueixes a 7319 <target>Ja no segueixes a
7277 <x id="PH"/> . 7320 <x id="PH"/> .
7278 </target> 7321 </target>
7279 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 7322
7280 </trans-unit> 7323 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
7281 <trans-unit id="2593763089859685916" datatype="html"> 7324 <trans-unit id="2593763089859685916" datatype="html">
7282 <source>enabled</source> 7325 <source>enabled</source>
7283 <target state="new">enabled</target> 7326 <target state="new">enabled</target>
@@ -7768,9 +7811,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7768 <trans-unit id="1519954996184640001"> 7811 <trans-unit id="1519954996184640001">
7769 <source>Error</source> 7812 <source>Error</source>
7770 <target>Error</target> 7813 <target>Error</target>
7771 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7814
7772 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7815
7773 </trans-unit> 7816 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7774 <trans-unit id="5076187961693950167" datatype="html"> 7817 <trans-unit id="5076187961693950167" datatype="html">
7775 <source>Standard logs</source> 7818 <source>Standard logs</source>
7776 <target state="new">Standard logs</target> 7819 <target state="new">Standard logs</target>
@@ -7786,8 +7829,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7786 <target>Usuari 7829 <target>Usuari
7787 <x id="PH"/> creat. 7830 <x id="PH"/> creat.
7788 </target> 7831 </target>
7789 7832 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-create.component.ts</context><context context-type="linenumber">76</context></context-group>
7790 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-create.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 7833 </trans-unit>
7791 <trans-unit id="8286337167859377104" datatype="html"> 7834 <trans-unit id="8286337167859377104" datatype="html">
7792 <source>Create user</source> 7835 <source>Create user</source>
7793 <target state="new">Create user</target> 7836 <target state="new">Create user</target>
@@ -7815,16 +7858,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7815 <target state="new">Update user password</target> 7858 <target state="new">Update user password</target>
7816 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7859 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7817 </trans-unit> 7860 </trans-unit>
7818 <trans-unit id="177544274549739411" datatype="html"> 7861
7819 <source>Following list</source> 7862
7820 <target state="new">Following list</target>
7821 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7822 </trans-unit>
7823 <trans-unit id="8092429110007204784" datatype="html">
7824 <source>Followers list</source>
7825 <target state="new">Followers list</target>
7826 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7827 </trans-unit>
7828 <trans-unit id="780323526182667308" datatype="html"> 7863 <trans-unit id="780323526182667308" datatype="html">
7829 <source>User <x id="PH"/> updated.</source> 7864 <source>User <x id="PH"/> updated.</source>
7830 <target state="new">User 7865 <target state="new">User
@@ -7864,16 +7899,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7864 <target state="new">Federation</target> 7899 <target state="new">Federation</target>
7865 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7900 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7866 </trans-unit> 7901 </trans-unit>
7867 <trans-unit id="4682675125751819107" datatype="html"> 7902
7868 <source>Instances you follow</source> 7903
7869 <target state="new">Instances you follow</target>
7870 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7871 </trans-unit>
7872 <trans-unit id="8899833753704589712" datatype="html">
7873 <source>Instances following you</source>
7874 <target state="new">Instances following you</target>
7875 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7876 </trans-unit>
7877 <trans-unit id="3767259920053407667" datatype="html"> 7904 <trans-unit id="3767259920053407667" datatype="html">
7878 <source>Videos will be deleted, comments will be tombstoned.</source> 7905 <source>Videos will be deleted, comments will be tombstoned.</source>
7879 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7906 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7904,6 +7931,24 @@ channel with the same name (<x id="PH_2"/>)!</target>
7904 <target state="new">Set Email as Verified</target> 7931 <target state="new">Set Email as Verified</target>
7905 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7932 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7906 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7933 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7934 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7935 <source>Created</source><target state="new">Created</target>
7936 <context-group purpose="location">
7937 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7938 <context context-type="linenumber">115</context>
7939 </context-group>
7940 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7941 <source>Daily quota</source><target state="new">Daily quota</target>
7942 <context-group purpose="location">
7943 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7944 <context context-type="linenumber">120</context>
7945 </context-group>
7946 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7947 <source>Last login</source><target state="new">Last login</target>
7948 <context-group purpose="location">
7949 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7950 <context context-type="linenumber">122</context>
7951 </context-group>
7907 </trans-unit> 7952 </trans-unit>
7908 <trans-unit id="3403978719736970622" datatype="html"> 7953 <trans-unit id="3403978719736970622" datatype="html">
7909 <source>You cannot ban root.</source> 7954 <source>You cannot ban root.</source>
@@ -8217,13 +8262,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
8217 <target>Canal de vídeo 8262 <target>Canal de vídeo
8218 <x id="PH"/> creat. 8263 <x id="PH"/> creat.
8219 </target> 8264 </target>
8220 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 8265
8221 </trans-unit> 8266 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
8222 <trans-unit id="8723777130353305761" datatype="html"> 8267 <trans-unit id="8723777130353305761" datatype="html">
8223 <source>This name already exists on this instance.</source> 8268 <source>This name already exists on this instance.</source>
8224 <target state="new">This name already exists on this instance.</target> 8269 <target state="new">This name already exists on this instance.</target>
8225 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 8270
8226 </trans-unit> 8271 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
8227 <trans-unit id="7589345916094713536"> 8272 <trans-unit id="7589345916094713536">
8228 <source>Video channel <x id="PH"/> updated.</source> 8273 <source>Video channel <x id="PH"/> updated.</source>
8229 <target>Canal de vídeo 8274 <target>Canal de vídeo
@@ -8246,13 +8291,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8246 <target state="new">Banner deleted.</target> 8291 <target state="new">Banner deleted.</target>
8247 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 8292 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
8248 </trans-unit> 8293 </trans-unit>
8249 <trans-unit id="2575302837003821736" datatype="html"> 8294
8250 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
8251 <target state="new">Please type the display name of the video channel (
8252 <x id="PH"/>) to confirm
8253 </target>
8254 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
8255 </trans-unit>
8256 <trans-unit id="624066830180032195"> 8295 <trans-unit id="624066830180032195">
8257 <source>Video channel <x id="PH"/> deleted.</source> 8296 <source>Video channel <x id="PH"/> deleted.</source>
8258 <target>Canal de vídeo 8297 <target>Canal de vídeo
@@ -8418,6 +8457,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8418 <source>Ownership change request sent.</source> 8457 <source>Ownership change request sent.</source>
8419 <target state="new">Ownership change request sent.</target> 8458 <target state="new">Ownership change request sent.</target>
8420 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8459 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8460 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8461 <source>Sort by</source><target state="new">Sort by</target>
8462 <context-group purpose="location">
8463 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8464 <context context-type="linenumber">26</context>
8465 </context-group>
8421 </trans-unit> 8466 </trans-unit>
8422 <trans-unit id="3245220240937722814" datatype="html"> 8467 <trans-unit id="3245220240937722814" datatype="html">
8423 <source>My channels</source> 8468 <source>My channels</source>
@@ -8520,7 +8565,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8520 <target state="new">Subscribe to the account</target> 8565 <target state="new">Subscribe to the account</target>
8521 8566
8522 8567
8523 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8568 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8524 <trans-unit id="3131904093925601441" datatype="html"> 8569 <trans-unit id="3131904093925601441" datatype="html">
8525 <source>PLAYLISTS</source> 8570 <source>PLAYLISTS</source>
8526 <target state="new">PLAYLISTS</target> 8571 <target state="new">PLAYLISTS</target>
@@ -8567,35 +8612,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8567 <trans-unit id="3779524668013120370" datatype="html"> 8612 <trans-unit id="3779524668013120370" datatype="html">
8568 <source>Go to my subscriptions</source> 8613 <source>Go to my subscriptions</source>
8569 <target state="new">Go to my subscriptions</target> 8614 <target state="new">Go to my subscriptions</target>
8570 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8615
8571 </trans-unit> 8616 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8572 <trans-unit id="1136469849928650779" datatype="html"> 8617 <trans-unit id="1136469849928650779" datatype="html">
8573 <source>Go to my videos</source> 8618 <source>Go to my videos</source>
8574 <target state="new">Go to my videos</target> 8619 <target state="new">Go to my videos</target>
8575 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8620
8576 </trans-unit> 8621 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8577 <trans-unit id="7836683738999600376" datatype="html"> 8622 <trans-unit id="7836683738999600376" datatype="html">
8578 <source>Go to my imports</source> 8623 <source>Go to my imports</source>
8579 <target state="new">Go to my imports</target> 8624 <target state="new">Go to my imports</target>
8580 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8625
8581 </trans-unit> 8626 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8582 <trans-unit id="7511292153332773503" datatype="html"> 8627 <trans-unit id="7511292153332773503" datatype="html">
8583 <source>Go to my channels</source> 8628 <source>Go to my channels</source>
8584 <target state="new">Go to my channels</target> 8629 <target state="new">Go to my channels</target>
8585 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8630
8586 </trans-unit> 8631 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8587 <trans-unit id="2013324644839511073" datatype="html"> 8632 <trans-unit id="2013324644839511073" datatype="html">
8588 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8633 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8589Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8634Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8590 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8635 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8591Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8636Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8592 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8637
8593 </trans-unit> 8638 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8594 <trans-unit id="375263728166936544"> 8639 <trans-unit id="375263728166936544">
8595 <source>You need to reconnect.</source> 8640 <source>You need to reconnect.</source>
8596 <target>Necessites tornar a connectar.</target> 8641 <target>Necessites tornar a connectar.</target>
8597 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8642
8598 </trans-unit> 8643 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8599 <trans-unit id="2206638022166154361" datatype="html"> 8644 <trans-unit id="2206638022166154361" datatype="html">
8600 <source>Keyboard Shortcuts:</source> 8645 <source>Keyboard Shortcuts:</source>
8601 <target state="new">Keyboard Shortcuts:</target> 8646 <target state="new">Keyboard Shortcuts:</target>
@@ -8608,6 +8653,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8608 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8653 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8609 <context context-type="linenumber">98</context> 8654 <context context-type="linenumber">98</context>
8610 </context-group> 8655 </context-group>
8656 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8657 <source>In my library</source><target state="new">In my library</target>
8658 <context-group purpose="location">
8659 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8660 <context context-type="linenumber">104</context>
8661 </context-group>
8611 </trans-unit> 8662 </trans-unit>
8612 <trans-unit id="232050922346936574" datatype="html"> 8663 <trans-unit id="232050922346936574" datatype="html">
8613 <source>Trending</source> 8664 <source>Trending</source>
@@ -8636,38 +8687,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8636 <trans-unit id="1266887509445371246" datatype="html"> 8687 <trans-unit id="1266887509445371246" datatype="html">
8637 <source>Incorrect username or password.</source> 8688 <source>Incorrect username or password.</source>
8638 <target state="new">Incorrect username or password.</target> 8689 <target state="new">Incorrect username or password.</target>
8639 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8690
8640 </trans-unit> 8691 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8641 <trans-unit id="6974874606619467663" datatype="html"> 8692 <trans-unit id="6974874606619467663" datatype="html">
8642 <source>Your account is blocked.</source> 8693 <source>Your account is blocked.</source>
8643 <target state="new">Your account is blocked.</target> 8694 <target state="new">Your account is blocked.</target>
8644 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8695
8645 </trans-unit> 8696 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8646 <trans-unit id="7939914198003891823" datatype="html"> 8697 <trans-unit id="7939914198003891823" datatype="html">
8647 <source>any language</source> 8698 <source>any language</source>
8648 <target state="new">any language</target> 8699 <target state="new">any language</target>
8649 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8700
8650 </trans-unit> 8701 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8651 <trans-unit id="5633144232269377096" datatype="html"> 8702 <trans-unit id="5633144232269377096" datatype="html">
8652 <source>hide</source> 8703 <source>hide</source>
8653 <target state="new">hide</target> 8704 <target state="new">hide</target>
8654 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8705
8655 </trans-unit> 8706 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8656 <trans-unit id="8603861867909474404" datatype="html"> 8707 <trans-unit id="8603861867909474404" datatype="html">
8657 <source>blur</source> 8708 <source>blur</source>
8658 <target state="new">blur</target> 8709 <target state="new">blur</target>
8659 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8710
8660 </trans-unit> 8711 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8661 <trans-unit id="4534458451100881847" datatype="html"> 8712 <trans-unit id="4534458451100881847" datatype="html">
8662 <source>display</source> 8713 <source>display</source>
8663 <target state="new">display</target> 8714 <target state="new">display</target>
8664 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8715
8665 </trans-unit> 8716 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8666 <trans-unit id="4467323362722952678" datatype="html"> 8717 <trans-unit id="4467323362722952678" datatype="html">
8667 <source>Unknown</source> 8718 <source>Unknown</source>
8668 <target state="new">Unknown</target> 8719 <target state="new">Unknown</target>
8669 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8720
8670 </trans-unit> 8721 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8671 <trans-unit id="8781423666414310853"> 8722 <trans-unit id="8781423666414310853">
8672 <source>Your password has been successfully reset!</source> 8723 <source>Your password has been successfully reset!</source>
8673 <target>La contrasenya s'ha restablit correctament!</target> 8724 <target>La contrasenya s'ha restablit correctament!</target>
@@ -10278,18 +10329,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10278 <target>Hi ha massa intents, torna-ho a provar després de 10329 <target>Hi ha massa intents, torna-ho a provar després de
10279 <x id="PH"/> minuts. 10330 <x id="PH"/> minuts.
10280 </target> 10331 </target>
10281 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 10332
10282 </trans-unit> 10333 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
10283 <trans-unit id="4965472196059235310"> 10334 <trans-unit id="4965472196059235310">
10284 <source>Too many attempts, please try again later.</source> 10335 <source>Too many attempts, please try again later.</source>
10285 <target>Hi ha massa intents, torna-ho a provar més tard.</target> 10336 <target>Hi ha massa intents, torna-ho a provar més tard.</target>
10286 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 10337
10287 </trans-unit> 10338 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
10288 <trans-unit id="1693549688987384699"> 10339 <trans-unit id="1693549688987384699">
10289 <source>Server error. Please retry later.</source> 10340 <source>Server error. Please retry later.</source>
10290 <target>Error del servidor. Torna-ho a intentar més tard.</target> 10341 <target>Error del servidor. Torna-ho a intentar més tard.</target>
10291 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 10342
10292 </trans-unit> 10343 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
10293 <trans-unit id="5927402622550505067" datatype="html"> 10344 <trans-unit id="5927402622550505067" datatype="html">
10294 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 10345 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
10295 <target state="new">Subscribed to all current channels of 10346 <target state="new">Subscribed to all current channels of
@@ -10891,35 +10942,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10891 <trans-unit id="3284171506518522275" datatype="html"> 10942 <trans-unit id="3284171506518522275" datatype="html">
10892 <source>Your video was uploaded to your account and is private.</source> 10943 <source>Your video was uploaded to your account and is private.</source>
10893 <target state="new">Your video was uploaded to your account and is private.</target> 10944 <target state="new">Your video was uploaded to your account and is private.</target>
10894 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10945
10895 </trans-unit> 10946 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10896 <trans-unit id="5699822024600815733"> 10947 <trans-unit id="5699822024600815733">
10897 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10948 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10898 <target>Però es perdran les dades associades (etiquetes, descripció ...), estàs segur que vols deixar aquesta pàgina?</target> 10949 <target>Però es perdran les dades associades (etiquetes, descripció ...), estàs segur que vols deixar aquesta pàgina?</target>
10899 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10950
10900 </trans-unit> 10951 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10901 <trans-unit id="1219739004043110649"> 10952 <trans-unit id="1219739004043110649">
10902 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10953 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10903 <target>El teu vídeo encara no s'ha carregat, estàs segur que vols sortir d'aquesta pàgina?</target> 10954 <target>El teu vídeo encara no s'ha carregat, estàs segur que vols sortir d'aquesta pàgina?</target>
10904 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10955
10905 </trans-unit> 10956 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10906 <trans-unit id="6932865105766151309" datatype="html"> 10957 <trans-unit id="6932865105766151309" datatype="html">
10907 <source>Upload</source> 10958 <source>Upload</source>
10908 <target state="new">Upload</target> 10959 <target state="new">Upload</target>
10909 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10960
10910 </trans-unit> 10961 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10911 <trans-unit id="8278735427925094503" datatype="html"> 10962 <trans-unit id="8278735427925094503" datatype="html">
10912 <source>Upload <x id="PH"/> </source> 10963 <source>Upload <x id="PH"/> </source>
10913 <target state="translated">Puja 10964 <target state="translated">Puja
10914 <x id="PH"/> 10965 <x id="PH"/>
10915 </target> 10966 </target>
10916 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10967
10917 </trans-unit> 10968 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10918 <trans-unit id="5981816353437801748"> 10969 <trans-unit id="5981816353437801748">
10919 <source>Video published.</source> 10970 <source>Video published.</source>
10920 <target>Vídeo publicat.</target> 10971 <target>Vídeo publicat.</target>
10921 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10972
10922 </trans-unit> 10973 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10923 <trans-unit id="764164089183618119" datatype="html"> 10974 <trans-unit id="764164089183618119" datatype="html">
10924 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10975 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10925 <target state="new">You have unsaved changes! If you leave, your changes will be lost.</target> 10976 <target state="new">You have unsaved changes! If you leave, your changes will be lost.</target>
@@ -10966,28 +11017,28 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10966 <trans-unit id="961774488937452220" datatype="html"> 11017 <trans-unit id="961774488937452220" datatype="html">
10967 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 11018 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10968 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 11019 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10969 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group> 11020
10970 </trans-unit> 11021 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10971 <trans-unit id="5761611056224181752" datatype="html"> 11022 <trans-unit id="5761611056224181752" datatype="html">
10972 <source>Redirection</source> 11023 <source>Redirection</source>
10973 <target state="new">Redirection</target> 11024 <target state="new">Redirection</target>
10974 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group> 11025
10975 </trans-unit> 11026 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10976 <trans-unit id="8858527736400081688"> 11027 <trans-unit id="8858527736400081688">
10977 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 11028 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10978 <target>Aquest vídeo conté contingut madur o explícit. Estàs segur que el vols veure?</target> 11029 <target>Aquest vídeo conté contingut madur o explícit. Estàs segur que el vols veure?</target>
10979 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group> 11030
10980 </trans-unit> 11031 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10981 <trans-unit id="3937119019020041049"> 11032 <trans-unit id="3937119019020041049">
10982 <source>Mature or explicit content</source> 11033 <source>Mature or explicit content</source>
10983 <target>Contingut madur o explícit</target> 11034 <target>Contingut madur o explícit</target>
10984 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group> 11035
10985 </trans-unit> 11036 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10986 <trans-unit id="1755474755114288376" datatype="html"> 11037 <trans-unit id="1755474755114288376" datatype="html">
10987 <source>Up Next</source> 11038 <source>Up Next</source>
10988 <target state="new">Up Next</target> 11039 <target state="new">Up Next</target>
10989 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group> 11040
10990 </trans-unit> 11041 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10991 <trans-unit id="2159130950882492111" datatype="html"> 11042 <trans-unit id="2159130950882492111" datatype="html">
10992 <source>Cancel</source> 11043 <source>Cancel</source>
10993 <target state="new">Cancel</target> 11044 <target state="new">Cancel</target>
@@ -10996,63 +11047,63 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10996 <trans-unit id="3354816756665089864" datatype="html"> 11047 <trans-unit id="3354816756665089864" datatype="html">
10997 <source>Autoplay is suspended</source> 11048 <source>Autoplay is suspended</source>
10998 <target state="new">Autoplay is suspended</target> 11049 <target state="new">Autoplay is suspended</target>
10999 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group> 11050
11000 </trans-unit> 11051 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
11001 <trans-unit id="7895294730547405228" datatype="html"> 11052 <trans-unit id="7895294730547405228" datatype="html">
11002 <source>Enter/exit fullscreen (requires player focus)</source> 11053 <source>Enter/exit fullscreen (requires player focus)</source>
11003 <target state="new">Enter/exit fullscreen (requires player focus)</target> 11054 <target state="new">Enter/exit fullscreen (requires player focus)</target>
11004 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group> 11055
11005 </trans-unit> 11056 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
11006 <trans-unit id="7618388257165864759" datatype="html"> 11057 <trans-unit id="7618388257165864759" datatype="html">
11007 <source>Play/Pause the video (requires player focus)</source> 11058 <source>Play/Pause the video (requires player focus)</source>
11008 <target state="new">Play/Pause the video (requires player focus)</target> 11059 <target state="new">Play/Pause the video (requires player focus)</target>
11009 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group> 11060
11010 </trans-unit> 11061 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
11011 <trans-unit id="7761890399634216630" datatype="html"> 11062 <trans-unit id="7761890399634216630" datatype="html">
11012 <source>Mute/unmute the video (requires player focus)</source> 11063 <source>Mute/unmute the video (requires player focus)</source>
11013 <target state="new">Mute/unmute the video (requires player focus)</target> 11064 <target state="new">Mute/unmute the video (requires player focus)</target>
11014 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group> 11065
11015 </trans-unit> 11066 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
11016 <trans-unit id="5996585232248234904" datatype="html"> 11067 <trans-unit id="5996585232248234904" datatype="html">
11017 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 11068 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
11018 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 11069 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
11019 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group> 11070
11020 </trans-unit> 11071 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
11021 <trans-unit id="3748765405903319998" datatype="html"> 11072 <trans-unit id="3748765405903319998" datatype="html">
11022 <source>Increase the volume (requires player focus)</source> 11073 <source>Increase the volume (requires player focus)</source>
11023 <target state="new">Increase the volume (requires player focus)</target> 11074 <target state="new">Increase the volume (requires player focus)</target>
11024 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group> 11075
11025 </trans-unit> 11076 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
11026 <trans-unit id="5810704036407159982" datatype="html"> 11077 <trans-unit id="5810704036407159982" datatype="html">
11027 <source>Decrease the volume (requires player focus)</source> 11078 <source>Decrease the volume (requires player focus)</source>
11028 <target state="new">Decrease the volume (requires player focus)</target> 11079 <target state="new">Decrease the volume (requires player focus)</target>
11029 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group> 11080
11030 </trans-unit> 11081 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
11031 <trans-unit id="2622048822548065691" datatype="html"> 11082 <trans-unit id="2622048822548065691" datatype="html">
11032 <source>Seek the video forward (requires player focus)</source> 11083 <source>Seek the video forward (requires player focus)</source>
11033 <target state="new">Seek the video forward (requires player focus)</target> 11084 <target state="new">Seek the video forward (requires player focus)</target>
11034 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group> 11085
11035 </trans-unit> 11086 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
11036 <trans-unit id="6540078205109221153" datatype="html"> 11087 <trans-unit id="6540078205109221153" datatype="html">
11037 <source>Seek the video backward (requires player focus)</source> 11088 <source>Seek the video backward (requires player focus)</source>
11038 <target state="new">Seek the video backward (requires player focus)</target> 11089 <target state="new">Seek the video backward (requires player focus)</target>
11039 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group> 11090
11040 </trans-unit> 11091 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
11041 <trans-unit id="1956491957766210808" datatype="html"> 11092 <trans-unit id="1956491957766210808" datatype="html">
11042 <source>Increase playback rate (requires player focus)</source> 11093 <source>Increase playback rate (requires player focus)</source>
11043 <target state="new">Increase playback rate (requires player focus)</target> 11094 <target state="new">Increase playback rate (requires player focus)</target>
11044 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group> 11095
11045 </trans-unit> 11096 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
11046 <trans-unit id="5495529997674803186" datatype="html"> 11097 <trans-unit id="5495529997674803186" datatype="html">
11047 <source>Decrease playback rate (requires player focus)</source> 11098 <source>Decrease playback rate (requires player focus)</source>
11048 <target state="new">Decrease playback rate (requires player focus)</target> 11099 <target state="new">Decrease playback rate (requires player focus)</target>
11049 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group> 11100
11050 </trans-unit> 11101 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
11051 <trans-unit id="3178343147230721210" datatype="html"> 11102 <trans-unit id="3178343147230721210" datatype="html">
11052 <source>Navigate in the video frame by frame (requires player focus)</source> 11103 <source>Navigate in the video frame by frame (requires player focus)</source>
11053 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 11104 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
11054 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group> 11105
11055 </trans-unit> 11106 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
11056 <trans-unit id="8025996572234182184" datatype="html"> 11107 <trans-unit id="8025996572234182184" datatype="html">
11057 <source>Like the video</source> 11108 <source>Like the video</source>
11058 <target state="new">Like the video</target> 11109 <target state="new">Like the video</target>
diff --git a/client/src/locale/angular.cs-CZ.xlf b/client/src/locale/angular.cs-CZ.xlf
index 2bfcfb63b..61423462f 100644
--- a/client/src/locale/angular.cs-CZ.xlf
+++ b/client/src/locale/angular.cs-CZ.xlf
@@ -249,7 +249,7 @@
249 249
250 250
251 251
252 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 252 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
253 <trans-unit id="1486537403020619891" datatype="html"> 253 <trans-unit id="1486537403020619891" datatype="html">
254 <source>My watch history</source> 254 <source>My watch history</source>
255 <target state="new">My watch history</target> 255 <target state="new">My watch history</target>
@@ -325,23 +325,23 @@
325 <trans-unit id="5674286808255988565"> 325 <trans-unit id="5674286808255988565">
326 <source>Create</source> 326 <source>Create</source>
327 <target>Vytvořit</target> 327 <target>Vytvořit</target>
328 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 328
329 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 329
330 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 330
331 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 331
332 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 332
333 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 333
334 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 334
335 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 335
336 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 336
337 </trans-unit> 337 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
338 <trans-unit id="1006562256968398209" datatype="html"> 338 <trans-unit id="1006562256968398209" datatype="html">
339 <source>video</source> 339 <source>video</source>
340 <target state="new">video</target> 340 <target state="new">video</target>
341 341
342 342
343 343
344 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 344 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
345 <trans-unit id="6438815964972582865" datatype="html"> 345 <trans-unit id="6438815964972582865" datatype="html">
346 <source>The following link contains a private token and should not be shared with anyone.</source> 346 <source>The following link contains a private token and should not be shared with anyone.</source>
347 <target state="translated">Následující odkaz obsahuje soukromý token a neměl by být s nikým sdílen.</target> 347 <target state="translated">Následující odkaz obsahuje soukromý token a neměl by být s nikým sdílen.</target>
@@ -414,13 +414,13 @@
414 <trans-unit id="6995024616159044376" datatype="html"> 414 <trans-unit id="6995024616159044376" datatype="html">
415 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 415 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
416 <target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 416 <target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
417 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 417
418 </trans-unit> 418 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
419 <trans-unit id="7873395933409147217" datatype="html"> 419 <trans-unit id="7873395933409147217" datatype="html">
420 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 420 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
421 <target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 421 <target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
422 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 422
423 </trans-unit> 423 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
424 <trans-unit id="5235042777215655908" datatype="html"> 424 <trans-unit id="5235042777215655908" datatype="html">
425 <source>subtitles</source> 425 <source>subtitles</source>
426 <target state="translated">Titulky</target> 426 <target state="translated">Titulky</target>
@@ -870,10 +870,10 @@
870 <trans-unit id="2602586221576511475"> 870 <trans-unit id="2602586221576511475">
871 <source>Video quota</source> 871 <source>Video quota</source>
872 <target>Limit na videa</target> 872 <target>Limit na videa</target>
873 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 873
874 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 874
875 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 875
876 </trans-unit> 876 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
877 <trans-unit id="1502595455339510144"> 877 <trans-unit id="1502595455339510144">
878 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 878 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
879 <target> 879 <target>
@@ -958,6 +958,30 @@
958 <target state="new">Federation</target> 958 <target state="new">Federation</target>
959 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 959 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
960 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 960 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
961 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
962 <source>Following</source><target state="new">Following</target>
963 <context-group purpose="location">
964 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
965 <context context-type="linenumber">29</context>
966 </context-group>
967 <context-group purpose="location">
968 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
969 <context context-type="linenumber">31</context>
970 </context-group>
971 <context-group purpose="location">
972 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
973 <context context-type="linenumber">28</context>
974 </context-group>
975 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
976 <source>Followers</source><target state="new">Followers</target>
977 <context-group purpose="location">
978 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
979 <context context-type="linenumber">34</context>
980 </context-group>
981 <context-group purpose="location">
982 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
983 <context context-type="linenumber">37</context>
984 </context-group>
961 </trans-unit> 985 </trans-unit>
962 <trans-unit id="3541687134897970106" datatype="html"> 986 <trans-unit id="3541687134897970106" datatype="html">
963 <source>followers</source> 987 <source>followers</source>
@@ -1039,7 +1063,7 @@
1039 1063
1040 1064
1041 1065
1042 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 1066 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
1043 <trans-unit id="3616223838716839702"> 1067 <trans-unit id="3616223838716839702">
1044 <source>Ban this user</source> 1068 <source>Ban this user</source>
1045 <target>Zablokovat tohoto uživatele</target> 1069 <target>Zablokovat tohoto uživatele</target>
@@ -1110,19 +1134,13 @@
1110 <trans-unit id="7252854992688790751" datatype="html"> 1134 <trans-unit id="7252854992688790751" datatype="html">
1111 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1135 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1112 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1136 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1113 <context-group purpose="location"> 1137
1114 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1138 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1115 <context context-type="linenumber">60,62</context>
1116 </context-group>
1117 </trans-unit>
1118 <trans-unit id="7215649348148521605" datatype="html"> 1139 <trans-unit id="7215649348148521605" datatype="html">
1119 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1140 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1120 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1141 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1121 <context-group purpose="location"> 1142
1122 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1143 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1123 <context context-type="linenumber">65,67</context>
1124 </context-group>
1125 </trans-unit>
1126 <trans-unit id="2392488717875840729"> 1144 <trans-unit id="2392488717875840729">
1127 <source>User</source> 1145 <source>User</source>
1128 <target>Uživatel</target> 1146 <target>Uživatel</target>
@@ -1133,69 +1151,69 @@
1133 <source>Username or email address</source> 1151 <source>Username or email address</source>
1134 <target>Uživatelské jméno nebo e-mail</target> 1152 <target>Uživatelské jméno nebo e-mail</target>
1135 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1153 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1154 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1155 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1156 <context-group purpose="location">
1157 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1158 <context context-type="linenumber">33,34</context>
1159 </context-group>
1136 </trans-unit> 1160 </trans-unit>
1137 <trans-unit id="1431416938026210429"> 1161 <trans-unit id="1431416938026210429">
1138 <source>Password</source> 1162 <source>Password</source>
1139 <target>Heslo</target> 1163 <target>Heslo</target>
1140 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1164
1141 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1165
1142 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1166
1143 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1167
1144 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1168
1145 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1169
1146 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1170
1147 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1171
1148 </trans-unit> 1172 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1149 <trans-unit id="8715156686857791956" datatype="html"> 1173 <trans-unit id="8715156686857791956" datatype="html">
1150 <source>Click here to reset your password</source> 1174 <source>Click here to reset your password</source>
1151 <target state="new">Click here to reset your password</target> 1175 <target state="new">Click here to reset your password</target>
1152 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1176
1153 </trans-unit> 1177 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1154 <trans-unit id="892063502898494584" datatype="html"> 1178 <trans-unit id="892063502898494584" datatype="html">
1155 <source>I forgot my password</source> 1179 <source>I forgot my password</source>
1156 <target state="new">I forgot my password</target> 1180 <target state="new">I forgot my password</target>
1157 <context-group purpose="location"> 1181
1158 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1182 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1159 <context context-type="linenumber">47</context>
1160 </context-group>
1161 </trans-unit>
1162 <trans-unit id="2101170466365500913" datatype="html"> 1183 <trans-unit id="2101170466365500913" datatype="html">
1163 <source>Logging into an account lets you publish content</source> 1184 <source>Logging into an account lets you publish content</source>
1164 <target state="new"> Logging into an account lets you publish content </target> 1185 <target state="new"> Logging into an account lets you publish content </target>
1165 <context-group purpose="location"> 1186
1166 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1187 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1167 <context context-type="linenumber">56,57</context>
1168 </context-group>
1169 </trans-unit>
1170 <trans-unit id="2454050363478003966"> 1188 <trans-unit id="2454050363478003966">
1171 <source>Login</source> 1189 <source>Login</source>
1172 <target>Přihlásit</target> 1190 <target>Přihlásit</target>
1173 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1191
1174 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1192
1175 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1193
1176 </trans-unit> 1194 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1177 <trans-unit id="3183213940445113677" datatype="html"> 1195 <trans-unit id="3183213940445113677" datatype="html">
1178 <source>Or sign in with</source> 1196 <source>Or sign in with</source>
1179 <target state="new">Or sign in with</target> 1197 <target state="new">Or sign in with</target>
1180 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1198
1181 </trans-unit> 1199 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1182 <trans-unit id="3238209155172574367"> 1200 <trans-unit id="3238209155172574367">
1183 <source>Forgot your password</source> 1201 <source>Forgot your password</source>
1184 <target>Zapomenuté heslo</target> 1202 <target>Zapomenuté heslo</target>
1185 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1203
1186 </trans-unit> 1204 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1187 <trans-unit id="87327320394367488" datatype="html"> 1205 <trans-unit id="87327320394367488" datatype="html">
1188 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1206 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1189 <target state="new"> 1207 <target state="new">
1190 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1208 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
1191 </target> 1209 </target>
1192 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1210
1193 </trans-unit> 1211 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1194 <trans-unit id="3188014010833256853" datatype="html"> 1212 <trans-unit id="3188014010833256853" datatype="html">
1195 <source>Enter your email address and we will send you a link to reset your password.</source> 1213 <source>Enter your email address and we will send you a link to reset your password.</source>
1196 <target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1214 <target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1197 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1215
1198 </trans-unit> 1216 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1199 <trans-unit id="1190256911880544559" datatype="html"> 1217 <trans-unit id="1190256911880544559" datatype="html">
1200 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1218 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1201The link will expire within 1 hour.</source> 1219The link will expire within 1 hour.</source>
@@ -1206,26 +1224,26 @@ The link will expire within 1 hour.</target>
1206 <trans-unit id="4768749765465246664"> 1224 <trans-unit id="4768749765465246664">
1207 <source>Email</source> 1225 <source>Email</source>
1208 <target>E-mail</target> 1226 <target>E-mail</target>
1209 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1227
1210 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1228
1211 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1229
1212 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1230
1213 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1231
1214 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1232
1215 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1233
1216 </trans-unit> 1234 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1217 <trans-unit id="3967269098753656610"> 1235 <trans-unit id="3967269098753656610">
1218 <source>Email address</source> 1236 <source>Email address</source>
1219 <target>E-mailová adresa</target> 1237 <target>E-mailová adresa</target>
1220 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1238
1221 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1239
1222 </trans-unit> 1240 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1223 <trans-unit id="7808756054397155068" datatype="html"> 1241 <trans-unit id="7808756054397155068" datatype="html">
1224 <source>Reset</source> 1242 <source>Reset</source>
1225 <target state="new">Reset</target> 1243 <target state="new">Reset</target>
1226 <note priority="1" from="description">Password reset button</note> 1244 <note priority="1" from="description">Password reset button</note>
1227 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1245
1228 </trans-unit> 1246 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1229 <trans-unit id="4319634264526091601" datatype="html"> 1247 <trans-unit id="4319634264526091601" datatype="html">
1230 <source>on this instance</source> 1248 <source>on this instance</source>
1231 <target state="new">on this instance</target> 1249 <target state="new">on this instance</target>
@@ -1593,9 +1611,9 @@ The link will expire within 1 hour.</target>
1593 <trans-unit id="2308975396733519902"> 1611 <trans-unit id="2308975396733519902">
1594 <source>Create an account</source> 1612 <source>Create an account</source>
1595 <target>Vytvořit účet</target> 1613 <target>Vytvořit účet</target>
1596 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1614
1597 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1615
1598 </trans-unit> 1616 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1599 <trans-unit id="3058024914967508975" datatype="html"> 1617 <trans-unit id="3058024914967508975" datatype="html">
1600 <source>My videos</source> 1618 <source>My videos</source>
1601 <target state="new">My videos</target> 1619 <target state="new">My videos</target>
@@ -1662,10 +1680,10 @@ The link will expire within 1 hour.</target>
1662 <trans-unit id="1504521795586863905" datatype="html"> 1680 <trans-unit id="1504521795586863905" datatype="html">
1663 <source>VIDEOS</source> 1681 <source>VIDEOS</source>
1664 <target state="new">VIDEOS</target> 1682 <target state="new">VIDEOS</target>
1665 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1683
1666 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1684
1667 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1685
1668 </trans-unit> 1686 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1669 <trans-unit id="667372110624203230" datatype="html"> 1687 <trans-unit id="667372110624203230" datatype="html">
1670 <source>Import jobs concurrency</source> 1688 <source>Import jobs concurrency</source>
1671 <target state="new">Import jobs concurrency</target> 1689 <target state="new">Import jobs concurrency</target>
@@ -1743,8 +1761,8 @@ The link will expire within 1 hour.</target>
1743 <trans-unit id="4424964105331349857" datatype="html"> 1761 <trans-unit id="4424964105331349857" datatype="html">
1744 <source>I'm a teapot</source> 1762 <source>I'm a teapot</source>
1745 <target state="new">I'm a teapot</target> 1763 <target state="new">I'm a teapot</target>
1746 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1764
1747 </trans-unit> 1765 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1748 <trans-unit id="1597262876035959248" datatype="html"> 1766 <trans-unit id="1597262876035959248" datatype="html">
1749 <source>That's an error.</source> 1767 <source>That's an error.</source>
1750 <target state="new">That's an error.</target> 1768 <target state="new">That's an error.</target>
@@ -1837,8 +1855,8 @@ The link will expire within 1 hour.</target>
1837 <trans-unit id="2971365540217107489" datatype="html"> 1855 <trans-unit id="2971365540217107489" datatype="html">
1838 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1856 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1839 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1857 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1840 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1858
1841 </trans-unit> 1859 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1842 1860
1843 <trans-unit id="5131854469652959713" datatype="html"> 1861 <trans-unit id="5131854469652959713" datatype="html">
1844 <source>GLOBAL SEARCH</source> 1862 <source>GLOBAL SEARCH</source>
@@ -2612,8 +2630,8 @@ The link will expire within 1 hour.</target>
2612 <trans-unit id="6161604372916832458" datatype="html"> 2630 <trans-unit id="6161604372916832458" datatype="html">
2613 <source>Upload on hold</source> 2631 <source>Upload on hold</source>
2614 <target state="new">Upload on hold</target> 2632 <target state="new">Upload on hold</target>
2615 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2633
2616 </trans-unit> 2634 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2617 <trans-unit id="285180972645018518" datatype="html"> 2635 <trans-unit id="285180972645018518" datatype="html">
2618 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2636 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2619 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2637 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3325,11 +3343,7 @@ The link will expire within 1 hour.</target>
3325 <target>ID</target> 3343 <target>ID</target>
3326 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3344 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3327 </trans-unit> 3345 </trans-unit>
3328 <trans-unit id="2265605798180116441"> 3346
3329 <source>Follower handle</source>
3330 <target state="new">Follower handle</target>
3331 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3332 </trans-unit>
3333 <trans-unit id="5911214550882917183"> 3347 <trans-unit id="5911214550882917183">
3334 <source>State</source> 3348 <source>State</source>
3335 <target>Stav</target> 3349 <target>Stav</target>
@@ -3401,11 +3415,7 @@ The link will expire within 1 hour.</target>
3401 </target> 3415 </target>
3402 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3416 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3403 </trans-unit> 3417 </trans-unit>
3404 <trans-unit id="6641024648411549335"> 3418
3405 <source>Host</source>
3406 <target>Host</target>
3407 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3408 </trans-unit>
3409 <trans-unit id="6571718060636962350" datatype="html"> 3419 <trans-unit id="6571718060636962350" datatype="html">
3410 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3420 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3411 <target state="new">Redundancy allowed 3421 <target state="new">Redundancy allowed
@@ -3417,9 +3427,9 @@ The link will expire within 1 hour.</target>
3417 <trans-unit id="9160510009013134726" datatype="html"> 3427 <trans-unit id="9160510009013134726" datatype="html">
3418 <source>Unfollow</source> 3428 <source>Unfollow</source>
3419 <target state="new">Unfollow</target> 3429 <target state="new">Unfollow</target>
3420 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3430
3421 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3431
3422 </trans-unit> 3432 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3423 <trans-unit id="8246779176913476983" datatype="html"> 3433 <trans-unit id="8246779176913476983" datatype="html">
3424 <source>Open instance in a new tab</source> 3434 <source>Open instance in a new tab</source>
3425 <target state="new">Open instance in a new tab</target> 3435 <target state="new">Open instance in a new tab</target>
@@ -3430,13 +3440,13 @@ The link will expire within 1 hour.</target>
3430 <trans-unit id="9132918641931433659" datatype="html"> 3440 <trans-unit id="9132918641931433659" datatype="html">
3431 <source>No host found matching current filters.</source> 3441 <source>No host found matching current filters.</source>
3432 <target state="new">No host found matching current filters.</target> 3442 <target state="new">No host found matching current filters.</target>
3433 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3443
3434 </trans-unit> 3444 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3435 <trans-unit id="7274241885665071790" datatype="html"> 3445 <trans-unit id="7274241885665071790" datatype="html">
3436 <source>Your instance is not following anyone.</source> 3446 <source>Your instance is not following anyone.</source>
3437 <target state="new">Your instance is not following anyone.</target> 3447 <target state="new">Your instance is not following anyone.</target>
3438 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3448
3439 </trans-unit> 3449 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3440 <trans-unit id="4774348799569692380" datatype="html"> 3450 <trans-unit id="4774348799569692380" datatype="html">
3441 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3451 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3442 <target state="new">Showing 3452 <target state="new">Showing
@@ -3446,16 +3456,8 @@ The link will expire within 1 hour.</target>
3446 </target> 3456 </target>
3447 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3457 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3448 </trans-unit> 3458 </trans-unit>
3449 <trans-unit id="6275803119759621687" datatype="html"> 3459
3450 <source>Follow domains</source> 3460
3451 <target state="new">Follow domains</target>
3452 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3453 </trans-unit>
3454 <trans-unit id="1268699198448750610" datatype="html">
3455 <source>Follow instances</source>
3456 <target state="new">Follow instances</target>
3457 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3458 </trans-unit>
3459 <trans-unit id="9216117865911519658" datatype="html"> 3461 <trans-unit id="9216117865911519658" datatype="html">
3460 <source>Action</source> 3462 <source>Action</source>
3461 <target state="new">Action</target> 3463 <target state="new">Action</target>
@@ -3505,11 +3507,11 @@ The link will expire within 1 hour.</target>
3505 <trans-unit id="5248717555542428023"> 3507 <trans-unit id="5248717555542428023">
3506 <source>Username</source> 3508 <source>Username</source>
3507 <target>Uživatelské jméno</target> 3509 <target>Uživatelské jméno</target>
3508 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3510
3509 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3511
3510 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3512
3511 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3513
3512 </trans-unit> 3514 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3513 <trans-unit id="5428411040014095392" datatype="html"> 3515 <trans-unit id="5428411040014095392" datatype="html">
3514 <source>e.g. jane_doe</source> 3516 <source>e.g. jane_doe</source>
3515 <target state="new">e.g. jane_doe</target> 3517 <target state="new">e.g. jane_doe</target>
@@ -3539,9 +3541,9 @@ The link will expire within 1 hour.</target>
3539 <trans-unit id="4145496584631696119"> 3541 <trans-unit id="4145496584631696119">
3540 <source>Role</source> 3542 <source>Role</source>
3541 <target>Role</target> 3543 <target>Role</target>
3542 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3544
3543 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3545
3544 </trans-unit> 3546 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3545 <trans-unit id="7046347992315328430" datatype="html"> 3547 <trans-unit id="7046347992315328430" datatype="html">
3546 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3548 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3547 <target state="new"> 3549 <target state="new">
@@ -3566,15 +3568,9 @@ The link will expire within 1 hour.</target>
3566 <trans-unit id="2622255144026150901" datatype="html"> 3568 <trans-unit id="2622255144026150901" datatype="html">
3567 <source>Auth plugin</source> 3569 <source>Auth plugin</source>
3568 <target state="new">Auth plugin</target> 3570 <target state="new">Auth plugin</target>
3569 <context-group purpose="location"> 3571
3570 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3572
3571 <context context-type="linenumber">188</context> 3573 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3572 </context-group>
3573 <context-group purpose="location">
3574 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3575 <context context-type="linenumber">188</context>
3576 </context-group>
3577 </trans-unit>
3578 <trans-unit id="588099657508661970" datatype="html"> 3574 <trans-unit id="588099657508661970" datatype="html">
3579 <source>None (local authentication)</source> 3575 <source>None (local authentication)</source>
3580 <target state="new">None (local authentication)</target> 3576 <target state="new">None (local authentication)</target>
@@ -3836,6 +3832,12 @@ The link will expire within 1 hour.</target>
3836 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3832 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3837 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3833 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3838 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3834 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3835 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3836 <source>Follower</source><target state="new">Follower</target>
3837 <context-group purpose="location">
3838 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3839 <context context-type="linenumber">24</context>
3840 </context-group>
3839 </trans-unit> 3841 </trans-unit>
3840 <trans-unit id="4691552465058437520" datatype="html"> 3842 <trans-unit id="4691552465058437520" datatype="html">
3841 <source>Commented video</source> 3843 <source>Commented video</source>
@@ -4173,8 +4175,8 @@ The link will expire within 1 hour.</target>
4173 <target state="new"> 4175 <target state="new">
4174 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 4176 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
4175 </target> 4177 </target>
4176 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 4178
4177 </trans-unit> 4179 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
4178 <trans-unit id="4058814854824495833" datatype="html"> 4180 <trans-unit id="4058814854824495833" datatype="html">
4179 <source>Mute domains</source> 4181 <source>Mute domains</source>
4180 <target state="new">Mute domains</target> 4182 <target state="new">Mute domains</target>
@@ -6176,11 +6178,8 @@ color: red;
6176 <trans-unit id="5512878593724620692" datatype="html"> 6178 <trans-unit id="5512878593724620692" datatype="html">
6177 <source>CHANNELS</source> 6179 <source>CHANNELS</source>
6178 <target state="new">CHANNELS</target> 6180 <target state="new">CHANNELS</target>
6179 <context-group purpose="location"> 6181
6180 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 6182 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
6181 <context context-type="linenumber">82</context>
6182 </context-group>
6183 </trans-unit>
6184 <trans-unit id="3666829335406793239"> 6183 <trans-unit id="3666829335406793239">
6185 <source>This account does not have channels.</source> 6184 <source>This account does not have channels.</source>
6186 <target>Tento účet nemá žádné kanály.</target> 6185 <target>Tento účet nemá žádné kanály.</target>
@@ -6225,6 +6224,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6225It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 6224It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
6226channel with the same name (<x id="PH_2"/>)!</target> 6225channel with the same name (<x id="PH_2"/>)!</target>
6227 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 6226 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
6227 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
6228 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
6229 <context-group purpose="location">
6230 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
6231 <context context-type="linenumber">48</context>
6232 </context-group>
6228 </trans-unit> 6233 </trans-unit>
6229 <trans-unit id="5387007581996837469" datatype="html"> 6234 <trans-unit id="5387007581996837469" datatype="html">
6230 <source>My Channels</source> 6235 <source>My Channels</source>
@@ -6824,12 +6829,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6824 <source>Your message has been sent.</source> 6829 <source>Your message has been sent.</source>
6825 <target state="new">Your message has been sent.</target> 6830 <target state="new">Your message has been sent.</target>
6826 6831
6827 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6832 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6828 <trans-unit id="2072135752262464360" datatype="html"> 6833 <trans-unit id="2072135752262464360" datatype="html">
6829 <source>You already sent this form recently</source> 6834 <source>You already sent this form recently</source>
6830 <target state="new">You already sent this form recently</target> 6835 <target state="new">You already sent this form recently</target>
6831 6836
6832 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6837 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6833 <trans-unit id="819067926858619041" datatype="html"> 6838 <trans-unit id="819067926858619041" datatype="html">
6834 <source>Account videos</source> 6839 <source>Account videos</source>
6835 <target state="new">Account videos</target> 6840 <target state="new">Account videos</target>
@@ -6874,13 +6879,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6874 <target state="new"> 6879 <target state="new">
6875 <x id="PH"/> direct account followers 6880 <x id="PH"/> direct account followers
6876 </target> 6881 </target>
6877 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6882
6878 </trans-unit> 6883 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6879 <trans-unit id="6250999352462648289" datatype="html"> 6884 <trans-unit id="6250999352462648289" datatype="html">
6880 <source>Report this account</source> 6885 <source>Report this account</source>
6881 <target state="new">Report this account</target> 6886 <target state="new">Report this account</target>
6882 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6887
6883 </trans-unit> 6888 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6884 <trans-unit id="1504521795586863905" datatype="html"> 6889 <trans-unit id="1504521795586863905" datatype="html">
6885 <source>VIDEOS</source> 6890 <source>VIDEOS</source>
6886 <target state="new">VIDEOS</target> 6891 <target state="new">VIDEOS</target>
@@ -6890,31 +6895,21 @@ channel with the same name (<x id="PH_2"/>)!</target>
6890 <trans-unit id="25349740244798533" datatype="html"> 6895 <trans-unit id="25349740244798533" datatype="html">
6891 <source>Username copied</source> 6896 <source>Username copied</source>
6892 <target state="new">Username copied</target> 6897 <target state="new">Username copied</target>
6893 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6898
6894 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6899
6895 </trans-unit> 6900 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6896 <trans-unit id="9221735175659318025" datatype="html"> 6901 <trans-unit id="9221735175659318025" datatype="html">
6897 <source>1 subscriber</source> 6902 <source>1 subscriber</source>
6898 <target state="new">1 subscriber</target> 6903 <target state="new">1 subscriber</target>
6899 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6904
6900 </trans-unit> 6905 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6901 <trans-unit id="4097331874769079975" datatype="html"> 6906 <trans-unit id="4097331874769079975" datatype="html">
6902 <source><x id="PH"/> subscribers</source> 6907 <source><x id="PH"/> subscribers</source>
6903 <target state="new"><x id="PH"/> subscribers</target> 6908 <target state="new"><x id="PH"/> subscribers</target>
6904 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6909
6905 </trans-unit> 6910 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6906 <trans-unit id="4682675125751819107" datatype="html"> 6911
6907 <source>Instances you follow</source> 6912
6908 <target state="new">Instances you follow</target>
6909 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6910 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6911 </trans-unit>
6912 <trans-unit id="8899833753704589712" datatype="html">
6913 <source>Instances following you</source>
6914 <target state="new">Instances following you</target>
6915 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6916 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6917 </trans-unit>
6918 <trans-unit id="1035838766454786107" datatype="html"> 6913 <trans-unit id="1035838766454786107" datatype="html">
6919 <source>Audio-only</source> 6914 <source>Audio-only</source>
6920 <target state="new">Audio-only</target> 6915 <target state="new">Audio-only</target>
@@ -6964,6 +6959,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6964 <source>Auto (via ffmpeg)</source> 6959 <source>Auto (via ffmpeg)</source>
6965 <target state="new">Auto (via ffmpeg)</target> 6960 <target state="new">Auto (via ffmpeg)</target>
6966 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6961 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6962 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6963 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6964 <context-group purpose="location">
6965 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6966 <context context-type="linenumber">3</context>
6967 </context-group>
6967 </trans-unit> 6968 </trans-unit>
6968 <trans-unit id="931255636742351800" datatype="html"> 6969 <trans-unit id="931255636742351800" datatype="html">
6969 <source>No limit</source> 6970 <source>No limit</source>
@@ -7114,18 +7115,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
7114 <trans-unit id="2127446333083057097" datatype="html"> 7115 <trans-unit id="2127446333083057097" datatype="html">
7115 <source>Domain is required.</source> 7116 <source>Domain is required.</source>
7116 <target state="new">Domain is required.</target> 7117 <target state="new">Domain is required.</target>
7117 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 7118
7118 </trans-unit> 7119 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
7119 <trans-unit id="6780793142903080663" datatype="html"> 7120 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
7120 <source>Domains entered are invalid.</source> 7121 <context-group purpose="location">
7121 <target state="new">Domains entered are invalid.</target> 7122 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7122 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 7123 <context context-type="linenumber">93</context>
7123 </trans-unit> 7124 </context-group>
7124 <trans-unit id="5886492514458202177" datatype="html"> 7125 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
7125 <source>Domains entered contain duplicates.</source> 7126 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
7126 <target state="new">Domains entered contain duplicates.</target> 7127 <context-group purpose="location">
7127 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 7128 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7129 <context context-type="linenumber">94</context>
7130 </context-group>
7131 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
7132 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
7133 <context-group purpose="location">
7134 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7135 <context context-type="linenumber">102</context>
7136 </context-group>
7137 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
7138 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
7139 <context-group purpose="location">
7140 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7141 <context context-type="linenumber">103</context>
7142 </context-group>
7128 </trans-unit> 7143 </trans-unit>
7144
7145
7129 <trans-unit id="240806681889331244"> 7146 <trans-unit id="240806681889331244">
7130 <source>Unlimited</source> 7147 <source>Unlimited</source>
7131 <target>Neomezeně</target> 7148 <target>Neomezeně</target>
@@ -7285,24 +7302,50 @@ channel with the same name (<x id="PH_2"/>)!</target>
7285 <x id="PH"/> removed from instance followers 7302 <x id="PH"/> removed from instance followers
7286 </target> 7303 </target>
7287 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 7304 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
7305 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
7306 <source>Follow</source><target state="new">Follow</target>
7307 <context-group purpose="location">
7308 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7309 <context context-type="linenumber">3</context>
7310 </context-group>
7311 <context-group purpose="location">
7312 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7313 <context context-type="linenumber">37</context>
7314 </context-group>
7315 <context-group purpose="location">
7316 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7317 <context context-type="linenumber">18</context>
7318 </context-group>
7319 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
7320 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
7321 <context-group purpose="location">
7322 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7323 <context context-type="linenumber">11</context>
7324 </context-group>
7288 </trans-unit> 7325 </trans-unit>
7289 <trans-unit id="2740793005745065895"> 7326 <trans-unit id="2740793005745065895">
7290 <source><x id="PH"/> is not valid </source> 7327 <source><x id="PH"/> is not valid </source>
7291 <target> 7328 <target>
7292 <x id="PH"/> není platný 7329 <x id="PH"/> není platný
7293 </target> 7330 </target>
7294 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 7331
7295 </trans-unit> 7332 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
7296 <trans-unit id="2355066641781598196"> 7333 <trans-unit id="2355066641781598196">
7297 <source>Follow request(s) sent!</source> 7334 <source>Follow request(s) sent!</source>
7298 <target>Požadavek odeslán!</target> 7335 <target>Požadavek odeslán!</target>
7299 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 7336
7337 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
7338 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
7339 <context-group purpose="location">
7340 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7341 <context context-type="linenumber">3</context>
7342 </context-group>
7300 </trans-unit> 7343 </trans-unit>
7301 <trans-unit id="4245720728052819482"> 7344 <trans-unit id="4245720728052819482">
7302 <source>Do you really want to unfollow <x id="PH"/>?</source> 7345 <source>Do you really want to unfollow <x id="PH"/>?</source>
7303 <target>Opravdu chcete zrušit odběr kanálu <x id="PH"/>?</target> 7346 <target>Opravdu chcete zrušit odběr kanálu <x id="PH"/>?</target>
7304 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 7347
7305 </trans-unit> 7348 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
7306 <trans-unit id="9160510009013134726"> 7349 <trans-unit id="9160510009013134726">
7307 <source>Unfollow</source> 7350 <source>Unfollow</source>
7308 <target>Zrušit odběr</target> 7351 <target>Zrušit odběr</target>
@@ -7311,8 +7354,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7311 <trans-unit id="3935234189109112926"> 7354 <trans-unit id="3935234189109112926">
7312 <source>You are not following <x id="PH"/> anymore.</source> 7355 <source>You are not following <x id="PH"/> anymore.</source>
7313 <target>Už dále neodebíráte <x id="PH"/>.</target> 7356 <target>Už dále neodebíráte <x id="PH"/>.</target>
7314 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 7357
7315 </trans-unit> 7358 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
7316 <trans-unit id="2593763089859685916" datatype="html"> 7359 <trans-unit id="2593763089859685916" datatype="html">
7317 <source>enabled</source> 7360 <source>enabled</source>
7318 <target state="new">enabled</target> 7361 <target state="new">enabled</target>
@@ -7788,9 +7831,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7788 <trans-unit id="1519954996184640001"> 7831 <trans-unit id="1519954996184640001">
7789 <source>Error</source> 7832 <source>Error</source>
7790 <target>Chyba</target> 7833 <target>Chyba</target>
7791 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7834
7792 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7835
7793 </trans-unit> 7836 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7794 <trans-unit id="5076187961693950167" datatype="html"> 7837 <trans-unit id="5076187961693950167" datatype="html">
7795 <source>Standard logs</source> 7838 <source>Standard logs</source>
7796 <target state="new">Standard logs</target> 7839 <target state="new">Standard logs</target>
@@ -7831,16 +7874,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7831 <target state="new">Update user password</target> 7874 <target state="new">Update user password</target>
7832 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7875 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7833 </trans-unit> 7876 </trans-unit>
7834 <trans-unit id="177544274549739411" datatype="html"> 7877
7835 <source>Following list</source> 7878
7836 <target state="new">Following list</target>
7837 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7838 </trans-unit>
7839 <trans-unit id="8092429110007204784" datatype="html">
7840 <source>Followers list</source>
7841 <target state="new">Followers list</target>
7842 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7843 </trans-unit>
7844 <trans-unit id="780323526182667308" datatype="html"> 7879 <trans-unit id="780323526182667308" datatype="html">
7845 <source>User <x id="PH"/> updated.</source> 7880 <source>User <x id="PH"/> updated.</source>
7846 <target state="translated">Uživatel <x id="PH"/> aktualizován.</target> 7881 <target state="translated">Uživatel <x id="PH"/> aktualizován.</target>
@@ -7878,16 +7913,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7878 <target state="new">Federation</target> 7913 <target state="new">Federation</target>
7879 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7914 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7880 </trans-unit> 7915 </trans-unit>
7881 <trans-unit id="4682675125751819107" datatype="html"> 7916
7882 <source>Instances you follow</source> 7917
7883 <target state="new">Instances you follow</target>
7884 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7885 </trans-unit>
7886 <trans-unit id="8899833753704589712" datatype="html">
7887 <source>Instances following you</source>
7888 <target state="new">Instances following you</target>
7889 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7890 </trans-unit>
7891 <trans-unit id="3767259920053407667" datatype="html"> 7918 <trans-unit id="3767259920053407667" datatype="html">
7892 <source>Videos will be deleted, comments will be tombstoned.</source> 7919 <source>Videos will be deleted, comments will be tombstoned.</source>
7893 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7920 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7918,6 +7945,24 @@ channel with the same name (<x id="PH_2"/>)!</target>
7918 <target state="new">Set Email as Verified</target> 7945 <target state="new">Set Email as Verified</target>
7919 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7946 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7920 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7947 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7948 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7949 <source>Created</source><target state="new">Created</target>
7950 <context-group purpose="location">
7951 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7952 <context context-type="linenumber">115</context>
7953 </context-group>
7954 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7955 <source>Daily quota</source><target state="new">Daily quota</target>
7956 <context-group purpose="location">
7957 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7958 <context context-type="linenumber">120</context>
7959 </context-group>
7960 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7961 <source>Last login</source><target state="new">Last login</target>
7962 <context-group purpose="location">
7963 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7964 <context context-type="linenumber">122</context>
7965 </context-group>
7921 </trans-unit> 7966 </trans-unit>
7922 <trans-unit id="3403978719736970622" datatype="html"> 7967 <trans-unit id="3403978719736970622" datatype="html">
7923 <source>You cannot ban root.</source> 7968 <source>You cannot ban root.</source>
@@ -8227,13 +8272,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
8227 <trans-unit id="1137937154872046253"> 8272 <trans-unit id="1137937154872046253">
8228 <source>Video channel <x id="PH"/> created.</source> 8273 <source>Video channel <x id="PH"/> created.</source>
8229 <target>Videokanál <x id="PH"/> vytvořen.</target> 8274 <target>Videokanál <x id="PH"/> vytvořen.</target>
8230 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 8275
8231 </trans-unit> 8276 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
8232 <trans-unit id="8723777130353305761" datatype="html"> 8277 <trans-unit id="8723777130353305761" datatype="html">
8233 <source>This name already exists on this instance.</source> 8278 <source>This name already exists on this instance.</source>
8234 <target state="new">This name already exists on this instance.</target> 8279 <target state="new">This name already exists on this instance.</target>
8235 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 8280
8236 </trans-unit> 8281 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
8237 <trans-unit id="7589345916094713536"> 8282 <trans-unit id="7589345916094713536">
8238 <source>Video channel <x id="PH"/> updated.</source> 8283 <source>Video channel <x id="PH"/> updated.</source>
8239 <target>Videokanál <x id="PH"/> aktualizován.</target> 8284 <target>Videokanál <x id="PH"/> aktualizován.</target>
@@ -8254,13 +8299,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8254 <target state="new">Banner deleted.</target> 8299 <target state="new">Banner deleted.</target>
8255 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 8300 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
8256 </trans-unit> 8301 </trans-unit>
8257 <trans-unit id="2575302837003821736" datatype="html"> 8302
8258 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
8259 <target state="new">Please type the display name of the video channel (
8260 <x id="PH"/>) to confirm
8261 </target>
8262 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
8263 </trans-unit>
8264 <trans-unit id="624066830180032195"> 8303 <trans-unit id="624066830180032195">
8265 <source>Video channel <x id="PH"/> deleted.</source> 8304 <source>Video channel <x id="PH"/> deleted.</source>
8266 <target>Videokanál <x id="PH"/> odstraněn.</target> 8305 <target>Videokanál <x id="PH"/> odstraněn.</target>
@@ -8412,6 +8451,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8412 <source>Ownership change request sent.</source> 8451 <source>Ownership change request sent.</source>
8413 <target state="new">Ownership change request sent.</target> 8452 <target state="new">Ownership change request sent.</target>
8414 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8453 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8454 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8455 <source>Sort by</source><target state="new">Sort by</target>
8456 <context-group purpose="location">
8457 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8458 <context context-type="linenumber">26</context>
8459 </context-group>
8415 </trans-unit> 8460 </trans-unit>
8416 <trans-unit id="3245220240937722814"> 8461 <trans-unit id="3245220240937722814">
8417 <source>My channels</source> 8462 <source>My channels</source>
@@ -8510,7 +8555,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8510 <target>Odebírat účet</target> 8555 <target>Odebírat účet</target>
8511 8556
8512 8557
8513 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8558 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8514 <trans-unit id="3131904093925601441" datatype="html"> 8559 <trans-unit id="3131904093925601441" datatype="html">
8515 <source>PLAYLISTS</source> 8560 <source>PLAYLISTS</source>
8516 <target state="new">PLAYLISTS</target> 8561 <target state="new">PLAYLISTS</target>
@@ -8557,35 +8602,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8557 <trans-unit id="3779524668013120370" datatype="html"> 8602 <trans-unit id="3779524668013120370" datatype="html">
8558 <source>Go to my subscriptions</source> 8603 <source>Go to my subscriptions</source>
8559 <target state="new">Go to my subscriptions</target> 8604 <target state="new">Go to my subscriptions</target>
8560 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8605
8561 </trans-unit> 8606 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8562 <trans-unit id="1136469849928650779" datatype="html"> 8607 <trans-unit id="1136469849928650779" datatype="html">
8563 <source>Go to my videos</source> 8608 <source>Go to my videos</source>
8564 <target state="new">Go to my videos</target> 8609 <target state="new">Go to my videos</target>
8565 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8610
8566 </trans-unit> 8611 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8567 <trans-unit id="7836683738999600376" datatype="html"> 8612 <trans-unit id="7836683738999600376" datatype="html">
8568 <source>Go to my imports</source> 8613 <source>Go to my imports</source>
8569 <target state="new">Go to my imports</target> 8614 <target state="new">Go to my imports</target>
8570 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8615
8571 </trans-unit> 8616 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8572 <trans-unit id="7511292153332773503" datatype="html"> 8617 <trans-unit id="7511292153332773503" datatype="html">
8573 <source>Go to my channels</source> 8618 <source>Go to my channels</source>
8574 <target state="new">Go to my channels</target> 8619 <target state="new">Go to my channels</target>
8575 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8620
8576 </trans-unit> 8621 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8577 <trans-unit id="2013324644839511073" datatype="html"> 8622 <trans-unit id="2013324644839511073" datatype="html">
8578 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8623 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8579Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8624Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8580 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8625 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8581Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8626Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8582 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8627
8583 </trans-unit> 8628 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8584 <trans-unit id="375263728166936544"> 8629 <trans-unit id="375263728166936544">
8585 <source>You need to reconnect.</source> 8630 <source>You need to reconnect.</source>
8586 <target>Musíte se znovu připojit.</target> 8631 <target>Musíte se znovu připojit.</target>
8587 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8632
8588 </trans-unit> 8633 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8589 <trans-unit id="2206638022166154361" datatype="html"> 8634 <trans-unit id="2206638022166154361" datatype="html">
8590 <source>Keyboard Shortcuts:</source> 8635 <source>Keyboard Shortcuts:</source>
8591 <target state="new">Keyboard Shortcuts:</target> 8636 <target state="new">Keyboard Shortcuts:</target>
@@ -8598,6 +8643,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8598 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8643 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8599 <context context-type="linenumber">98</context> 8644 <context context-type="linenumber">98</context>
8600 </context-group> 8645 </context-group>
8646 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8647 <source>In my library</source><target state="new">In my library</target>
8648 <context-group purpose="location">
8649 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8650 <context context-type="linenumber">104</context>
8651 </context-group>
8601 </trans-unit> 8652 </trans-unit>
8602 <trans-unit id="232050922346936574" datatype="html"> 8653 <trans-unit id="232050922346936574" datatype="html">
8603 <source>Trending</source> 8654 <source>Trending</source>
@@ -8626,38 +8677,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8626 <trans-unit id="1266887509445371246" datatype="html"> 8677 <trans-unit id="1266887509445371246" datatype="html">
8627 <source>Incorrect username or password.</source> 8678 <source>Incorrect username or password.</source>
8628 <target state="new">Incorrect username or password.</target> 8679 <target state="new">Incorrect username or password.</target>
8629 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8680
8630 </trans-unit> 8681 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8631 <trans-unit id="6974874606619467663" datatype="html"> 8682 <trans-unit id="6974874606619467663" datatype="html">
8632 <source>Your account is blocked.</source> 8683 <source>Your account is blocked.</source>
8633 <target state="new">Your account is blocked.</target> 8684 <target state="new">Your account is blocked.</target>
8634 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8685
8635 </trans-unit> 8686 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8636 <trans-unit id="7939914198003891823" datatype="html"> 8687 <trans-unit id="7939914198003891823" datatype="html">
8637 <source>any language</source> 8688 <source>any language</source>
8638 <target state="new">any language</target> 8689 <target state="new">any language</target>
8639 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8690
8640 </trans-unit> 8691 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8641 <trans-unit id="5633144232269377096" datatype="html"> 8692 <trans-unit id="5633144232269377096" datatype="html">
8642 <source>hide</source> 8693 <source>hide</source>
8643 <target state="new">hide</target> 8694 <target state="new">hide</target>
8644 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8695
8645 </trans-unit> 8696 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8646 <trans-unit id="8603861867909474404" datatype="html"> 8697 <trans-unit id="8603861867909474404" datatype="html">
8647 <source>blur</source> 8698 <source>blur</source>
8648 <target state="new">blur</target> 8699 <target state="new">blur</target>
8649 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8700
8650 </trans-unit> 8701 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8651 <trans-unit id="4534458451100881847" datatype="html"> 8702 <trans-unit id="4534458451100881847" datatype="html">
8652 <source>display</source> 8703 <source>display</source>
8653 <target state="new">display</target> 8704 <target state="new">display</target>
8654 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8705
8655 </trans-unit> 8706 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8656 <trans-unit id="4467323362722952678" datatype="html"> 8707 <trans-unit id="4467323362722952678" datatype="html">
8657 <source>Unknown</source> 8708 <source>Unknown</source>
8658 <target state="new">Unknown</target> 8709 <target state="new">Unknown</target>
8659 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8710
8660 </trans-unit> 8711 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8661 <trans-unit id="8781423666414310853"> 8712 <trans-unit id="8781423666414310853">
8662 <source>Your password has been successfully reset!</source> 8713 <source>Your password has been successfully reset!</source>
8663 <target>Vaše heslo bylo úspěšně resetováno!</target> 8714 <target>Vaše heslo bylo úspěšně resetováno!</target>
@@ -10252,18 +10303,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10252 <trans-unit id="968295009933361070"> 10303 <trans-unit id="968295009933361070">
10253 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 10304 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
10254 <target>Příliš mnoho pokusů, zkuste to prosím znovu za <x id="PH"/> minut.</target> 10305 <target>Příliš mnoho pokusů, zkuste to prosím znovu za <x id="PH"/> minut.</target>
10255 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 10306
10256 </trans-unit> 10307 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
10257 <trans-unit id="4965472196059235310"> 10308 <trans-unit id="4965472196059235310">
10258 <source>Too many attempts, please try again later.</source> 10309 <source>Too many attempts, please try again later.</source>
10259 <target>Příliš mnoho pokusů, zkuste to prosím později.</target> 10310 <target>Příliš mnoho pokusů, zkuste to prosím později.</target>
10260 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 10311
10261 </trans-unit> 10312 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
10262 <trans-unit id="1693549688987384699"> 10313 <trans-unit id="1693549688987384699">
10263 <source>Server error. Please retry later.</source> 10314 <source>Server error. Please retry later.</source>
10264 <target>Chyba serveru. Zkuste to prosím později.</target> 10315 <target>Chyba serveru. Zkuste to prosím později.</target>
10265 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 10316
10266 </trans-unit> 10317 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
10267 <trans-unit id="5927402622550505067" datatype="html"> 10318 <trans-unit id="5927402622550505067" datatype="html">
10268 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 10319 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
10269 <target state="new">Subscribed to all current channels of 10320 <target state="new">Subscribed to all current channels of
@@ -10858,35 +10909,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10858 <trans-unit id="3284171506518522275"> 10909 <trans-unit id="3284171506518522275">
10859 <source>Your video was uploaded to your account and is private.</source> 10910 <source>Your video was uploaded to your account and is private.</source>
10860 <target>Vaše video bylo nahráno na váš účet a je soukromé.</target> 10911 <target>Vaše video bylo nahráno na váš účet a je soukromé.</target>
10861 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10912
10862 </trans-unit> 10913 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10863 <trans-unit id="5699822024600815733"> 10914 <trans-unit id="5699822024600815733">
10864 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10915 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10865 <target>Ovšem přidružená data (štítky, popis...) budou ztraceny, opravdu chcete opustit tuto stránku?</target> 10916 <target>Ovšem přidružená data (štítky, popis...) budou ztraceny, opravdu chcete opustit tuto stránku?</target>
10866 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10917
10867 </trans-unit> 10918 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10868 <trans-unit id="1219739004043110649"> 10919 <trans-unit id="1219739004043110649">
10869 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10920 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10870 <target>Video ještě nebylo nahráno, opravdu chcete opustit tuto stránku?</target> 10921 <target>Video ještě nebylo nahráno, opravdu chcete opustit tuto stránku?</target>
10871 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10922
10872 </trans-unit> 10923 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10873 <trans-unit id="6932865105766151309" datatype="html"> 10924 <trans-unit id="6932865105766151309" datatype="html">
10874 <source>Upload</source> 10925 <source>Upload</source>
10875 <target state="new">Upload</target> 10926 <target state="new">Upload</target>
10876 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10927
10877 </trans-unit> 10928 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10878 <trans-unit id="8278735427925094503" datatype="html"> 10929 <trans-unit id="8278735427925094503" datatype="html">
10879 <source>Upload <x id="PH"/> </source> 10930 <source>Upload <x id="PH"/> </source>
10880 <target state="new">Upload 10931 <target state="new">Upload
10881 <x id="PH"/> 10932 <x id="PH"/>
10882 </target> 10933 </target>
10883 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10934
10884 </trans-unit> 10935 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10885 <trans-unit id="5981816353437801748"> 10936 <trans-unit id="5981816353437801748">
10886 <source>Video published.</source> 10937 <source>Video published.</source>
10887 <target>Video publikováno</target> 10938 <target>Video publikováno</target>
10888 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10939
10889 </trans-unit> 10940 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10890 <trans-unit id="764164089183618119"> 10941 <trans-unit id="764164089183618119">
10891 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10942 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10892 <target>Máte neuložené změny! Pokud odejdete, budou vaše změny ztraceny.</target> 10943 <target>Máte neuložené změny! Pokud odejdete, budou vaše změny ztraceny.</target>
@@ -10954,27 +11005,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10954 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 11005 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10955 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 11006 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10956 11007
10957 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 11008 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10958 <trans-unit id="5761611056224181752" datatype="html"> 11009 <trans-unit id="5761611056224181752" datatype="html">
10959 <source>Redirection</source> 11010 <source>Redirection</source>
10960 <target state="new">Redirection</target> 11011 <target state="new">Redirection</target>
10961 11012
10962 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 11013 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10963 <trans-unit id="8858527736400081688"> 11014 <trans-unit id="8858527736400081688">
10964 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 11015 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10965 <target>Toto video obsahuje citlivý materiál. Opravdu jej chcete přehrát?</target> 11016 <target>Toto video obsahuje citlivý materiál. Opravdu jej chcete přehrát?</target>
10966 11017
10967 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 11018 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10968 <trans-unit id="3937119019020041049"> 11019 <trans-unit id="3937119019020041049">
10969 <source>Mature or explicit content</source> 11020 <source>Mature or explicit content</source>
10970 <target>Obsahuje citlivý materiál</target> 11021 <target>Obsahuje citlivý materiál</target>
10971 11022
10972 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 11023 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10973 <trans-unit id="1755474755114288376" datatype="html"> 11024 <trans-unit id="1755474755114288376" datatype="html">
10974 <source>Up Next</source> 11025 <source>Up Next</source>
10975 <target state="new">Up Next</target> 11026 <target state="new">Up Next</target>
10976 11027
10977 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 11028 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10978 <trans-unit id="2159130950882492111" datatype="html"> 11029 <trans-unit id="2159130950882492111" datatype="html">
10979 <source>Cancel</source> 11030 <source>Cancel</source>
10980 <target state="new">Cancel</target> 11031 <target state="new">Cancel</target>
@@ -10984,62 +11035,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10984 <source>Autoplay is suspended</source> 11035 <source>Autoplay is suspended</source>
10985 <target state="new">Autoplay is suspended</target> 11036 <target state="new">Autoplay is suspended</target>
10986 11037
10987 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 11038 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10988 <trans-unit id="7895294730547405228" datatype="html"> 11039 <trans-unit id="7895294730547405228" datatype="html">
10989 <source>Enter/exit fullscreen (requires player focus)</source> 11040 <source>Enter/exit fullscreen (requires player focus)</source>
10990 <target state="new">Enter/exit fullscreen (requires player focus)</target> 11041 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10991 11042
10992 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 11043 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10993 <trans-unit id="7618388257165864759" datatype="html"> 11044 <trans-unit id="7618388257165864759" datatype="html">
10994 <source>Play/Pause the video (requires player focus)</source> 11045 <source>Play/Pause the video (requires player focus)</source>
10995 <target state="new">Play/Pause the video (requires player focus)</target> 11046 <target state="new">Play/Pause the video (requires player focus)</target>
10996 11047
10997 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 11048 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10998 <trans-unit id="7761890399634216630" datatype="html"> 11049 <trans-unit id="7761890399634216630" datatype="html">
10999 <source>Mute/unmute the video (requires player focus)</source> 11050 <source>Mute/unmute the video (requires player focus)</source>
11000 <target state="new">Mute/unmute the video (requires player focus)</target> 11051 <target state="new">Mute/unmute the video (requires player focus)</target>
11001 11052
11002 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 11053 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
11003 <trans-unit id="5996585232248234904" datatype="html"> 11054 <trans-unit id="5996585232248234904" datatype="html">
11004 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 11055 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
11005 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 11056 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
11006 11057
11007 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 11058 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
11008 <trans-unit id="3748765405903319998" datatype="html"> 11059 <trans-unit id="3748765405903319998" datatype="html">
11009 <source>Increase the volume (requires player focus)</source> 11060 <source>Increase the volume (requires player focus)</source>
11010 <target state="new">Increase the volume (requires player focus)</target> 11061 <target state="new">Increase the volume (requires player focus)</target>
11011 11062
11012 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 11063 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
11013 <trans-unit id="5810704036407159982" datatype="html"> 11064 <trans-unit id="5810704036407159982" datatype="html">
11014 <source>Decrease the volume (requires player focus)</source> 11065 <source>Decrease the volume (requires player focus)</source>
11015 <target state="new">Decrease the volume (requires player focus)</target> 11066 <target state="new">Decrease the volume (requires player focus)</target>
11016 11067
11017 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 11068 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
11018 <trans-unit id="2622048822548065691" datatype="html"> 11069 <trans-unit id="2622048822548065691" datatype="html">
11019 <source>Seek the video forward (requires player focus)</source> 11070 <source>Seek the video forward (requires player focus)</source>
11020 <target state="new">Seek the video forward (requires player focus)</target> 11071 <target state="new">Seek the video forward (requires player focus)</target>
11021 11072
11022 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 11073 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
11023 <trans-unit id="6540078205109221153" datatype="html"> 11074 <trans-unit id="6540078205109221153" datatype="html">
11024 <source>Seek the video backward (requires player focus)</source> 11075 <source>Seek the video backward (requires player focus)</source>
11025 <target state="new">Seek the video backward (requires player focus)</target> 11076 <target state="new">Seek the video backward (requires player focus)</target>
11026 11077
11027 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 11078 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
11028 <trans-unit id="1956491957766210808" datatype="html"> 11079 <trans-unit id="1956491957766210808" datatype="html">
11029 <source>Increase playback rate (requires player focus)</source> 11080 <source>Increase playback rate (requires player focus)</source>
11030 <target state="new">Increase playback rate (requires player focus)</target> 11081 <target state="new">Increase playback rate (requires player focus)</target>
11031 11082
11032 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 11083 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
11033 <trans-unit id="5495529997674803186" datatype="html"> 11084 <trans-unit id="5495529997674803186" datatype="html">
11034 <source>Decrease playback rate (requires player focus)</source> 11085 <source>Decrease playback rate (requires player focus)</source>
11035 <target state="new">Decrease playback rate (requires player focus)</target> 11086 <target state="new">Decrease playback rate (requires player focus)</target>
11036 11087
11037 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 11088 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
11038 <trans-unit id="3178343147230721210" datatype="html"> 11089 <trans-unit id="3178343147230721210" datatype="html">
11039 <source>Navigate in the video frame by frame (requires player focus)</source> 11090 <source>Navigate in the video frame by frame (requires player focus)</source>
11040 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 11091 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
11041 11092
11042 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 11093 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
11043 <trans-unit id="8025996572234182184"> 11094 <trans-unit id="8025996572234182184">
11044 <source>Like the video</source> 11095 <source>Like the video</source>
11045 <target>To se mi líbí</target> 11096 <target>To se mi líbí</target>
diff --git a/client/src/locale/angular.da-DK.xlf b/client/src/locale/angular.da-DK.xlf
index 687604dd9..763d1d8aa 100644
--- a/client/src/locale/angular.da-DK.xlf
+++ b/client/src/locale/angular.da-DK.xlf
@@ -194,7 +194,7 @@
194 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source> 194 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source>
195 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target> 195 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target>
196 196
197 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 197 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
198 <trans-unit id="1486537403020619891" datatype="html"> 198 <trans-unit id="1486537403020619891" datatype="html">
199 <source>My watch history</source> 199 <source>My watch history</source>
200 <target state="translated">Min videohistorik</target> 200 <target state="translated">Min videohistorik</target>
@@ -269,12 +269,12 @@
269 269
270 270
271 271
272 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 272 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
273 <trans-unit id="1006562256968398209" datatype="html"> 273 <trans-unit id="1006562256968398209" datatype="html">
274 <source>video</source> 274 <source>video</source>
275 <target state="new">video</target> 275 <target state="new">video</target>
276 276
277 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 277 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
278 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 278 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
279 279
280 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 280 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -336,10 +336,10 @@
336 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 336 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
337 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 337 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
338 338
339 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 339 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
340 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 340 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
341 341
342 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 342 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
343 343
344 <trans-unit id="5235042777215655908" datatype="html"> 344 <trans-unit id="5235042777215655908" datatype="html">
345 <source>subtitles</source> 345 <source>subtitles</source>
@@ -738,10 +738,10 @@
738 <trans-unit id="2602586221576511475" datatype="html"> 738 <trans-unit id="2602586221576511475" datatype="html">
739 <source>Video quota</source> 739 <source>Video quota</source>
740 <target state="new">Video quota</target> 740 <target state="new">Video quota</target>
741 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 741
742 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 742
743 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 743
744 </trans-unit> 744 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
745 <trans-unit id="1502595455339510144" datatype="html"> 745 <trans-unit id="1502595455339510144" datatype="html">
746 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 746 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
747 <target state="new"> 747 <target state="new">
@@ -825,7 +825,31 @@
825 <source>Federation</source> 825 <source>Federation</source>
826 <target state="new">Federation</target> 826 <target state="new">Federation</target>
827 827
828 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 828 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
829 <source>Following</source><target state="new">Following</target>
830 <context-group purpose="location">
831 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
832 <context context-type="linenumber">29</context>
833 </context-group>
834 <context-group purpose="location">
835 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
836 <context context-type="linenumber">31</context>
837 </context-group>
838 <context-group purpose="location">
839 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
840 <context context-type="linenumber">28</context>
841 </context-group>
842 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
843 <source>Followers</source><target state="new">Followers</target>
844 <context-group purpose="location">
845 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
846 <context context-type="linenumber">34</context>
847 </context-group>
848 <context-group purpose="location">
849 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
850 <context context-type="linenumber">37</context>
851 </context-group>
852 </trans-unit>
829 <trans-unit id="3541687134897970106" datatype="html"> 853 <trans-unit id="3541687134897970106" datatype="html">
830 <source>followers</source> 854 <source>followers</source>
831 <target state="new">followers</target> 855 <target state="new">followers</target>
@@ -900,7 +924,7 @@
900 924
901 925
902 926
903 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 927 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
904 <trans-unit id="3616223838716839702" datatype="html"> 928 <trans-unit id="3616223838716839702" datatype="html">
905 <source>Ban this user</source> 929 <source>Ban this user</source>
906 <target state="new">Ban this user</target> 930 <target state="new">Ban this user</target>
@@ -973,19 +997,13 @@
973 <trans-unit id="7252854992688790751" datatype="html"> 997 <trans-unit id="7252854992688790751" datatype="html">
974 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 998 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
975 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 999 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
976 <context-group purpose="location"> 1000
977 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1001 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
978 <context context-type="linenumber">60,62</context>
979 </context-group>
980 </trans-unit>
981 <trans-unit id="7215649348148521605" datatype="html"> 1002 <trans-unit id="7215649348148521605" datatype="html">
982 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1003 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
983 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1004 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
984 <context-group purpose="location"> 1005
985 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1006 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
986 <context context-type="linenumber">65,67</context>
987 </context-group>
988 </trans-unit>
989 <trans-unit id="2392488717875840729"> 1007 <trans-unit id="2392488717875840729">
990 <source>User</source> 1008 <source>User</source>
991 <target>Bruger</target> 1009 <target>Bruger</target>
@@ -996,68 +1014,68 @@
996 <source>Username or email address</source> 1014 <source>Username or email address</source>
997 <target>Brugernavn eller email-adresse</target> 1015 <target>Brugernavn eller email-adresse</target>
998 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1016 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1017 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1018 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1019 <context-group purpose="location">
1020 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1021 <context context-type="linenumber">33,34</context>
1022 </context-group>
999 </trans-unit> 1023 </trans-unit>
1000 <trans-unit id="1431416938026210429"> 1024 <trans-unit id="1431416938026210429">
1001 <source>Password</source> 1025 <source>Password</source>
1002 <target>Adgangskode</target> 1026 <target>Adgangskode</target>
1003 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1027
1004 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1028
1005 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1029
1006 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1030
1007 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1031
1008 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1032
1009 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1033
1010 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1034
1011 </trans-unit> 1035 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1012 <trans-unit id="8715156686857791956" datatype="html"> 1036 <trans-unit id="8715156686857791956" datatype="html">
1013 <source>Click here to reset your password</source> 1037 <source>Click here to reset your password</source>
1014 <target state="new">Click here to reset your password</target> 1038 <target state="new">Click here to reset your password</target>
1015 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1039
1016 </trans-unit> 1040 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1017 <trans-unit id="892063502898494584" datatype="html"> 1041 <trans-unit id="892063502898494584" datatype="html">
1018 <source>I forgot my password</source> 1042 <source>I forgot my password</source>
1019 <target state="new">I forgot my password</target> 1043 <target state="new">I forgot my password</target>
1020 <context-group purpose="location"> 1044
1021 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1045 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1022 <context context-type="linenumber">47</context>
1023 </context-group>
1024 </trans-unit>
1025 <trans-unit id="2101170466365500913" datatype="html"> 1046 <trans-unit id="2101170466365500913" datatype="html">
1026 <source>Logging into an account lets you publish content</source> 1047 <source>Logging into an account lets you publish content</source>
1027 <target state="new"> Logging into an account lets you publish content </target> 1048 <target state="new"> Logging into an account lets you publish content </target>
1028 <context-group purpose="location"> 1049
1029 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1050 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1030 <context context-type="linenumber">56,57</context>
1031 </context-group>
1032 </trans-unit>
1033 <trans-unit id="2454050363478003966" datatype="html"> 1051 <trans-unit id="2454050363478003966" datatype="html">
1034 <source>Login</source> 1052 <source>Login</source>
1035 <target state="new">Login</target> 1053 <target state="new">Login</target>
1036 1054
1037 1055
1038 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1056 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1039 <trans-unit id="3183213940445113677" datatype="html"> 1057 <trans-unit id="3183213940445113677" datatype="html">
1040 <source>Or sign in with</source> 1058 <source>Or sign in with</source>
1041 <target state="new">Or sign in with</target> 1059 <target state="new">Or sign in with</target>
1042 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1060
1043 </trans-unit> 1061 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1044 <trans-unit id="3238209155172574367"> 1062 <trans-unit id="3238209155172574367">
1045 <source>Forgot your password</source> 1063 <source>Forgot your password</source>
1046 <target>Glemt din adgangskode</target> 1064 <target>Glemt din adgangskode</target>
1047 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1065
1048 </trans-unit> 1066 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1049 <trans-unit id="87327320394367488" datatype="html"> 1067 <trans-unit id="87327320394367488" datatype="html">
1050 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1068 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1051 <target state="new"> 1069 <target state="new">
1052 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1070 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
1053 </target> 1071 </target>
1054 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1072
1055 </trans-unit> 1073 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1056 <trans-unit id="3188014010833256853" datatype="html"> 1074 <trans-unit id="3188014010833256853" datatype="html">
1057 <source>Enter your email address and we will send you a link to reset your password.</source> 1075 <source>Enter your email address and we will send you a link to reset your password.</source>
1058 <target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1076 <target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1059 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1077
1060 </trans-unit> 1078 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1061 <trans-unit id="1190256911880544559" datatype="html"> 1079 <trans-unit id="1190256911880544559" datatype="html">
1062 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1080 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1063The link will expire within 1 hour.</source> 1081The link will expire within 1 hour.</source>
@@ -1068,26 +1086,26 @@ The link will expire within 1 hour.</target>
1068 <trans-unit id="4768749765465246664"> 1086 <trans-unit id="4768749765465246664">
1069 <source>Email</source> 1087 <source>Email</source>
1070 <target>Email</target> 1088 <target>Email</target>
1071 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1089
1072 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1090
1073 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1091
1074 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1092
1075 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1093
1076 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1094
1077 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1095
1078 </trans-unit> 1096 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1079 <trans-unit id="3967269098753656610"> 1097 <trans-unit id="3967269098753656610">
1080 <source>Email address</source> 1098 <source>Email address</source>
1081 <target>Email-adresse</target> 1099 <target>Email-adresse</target>
1082 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1100
1083 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1101
1084 </trans-unit> 1102 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1085 <trans-unit id="7808756054397155068" datatype="html"> 1103 <trans-unit id="7808756054397155068" datatype="html">
1086 <source>Reset</source> 1104 <source>Reset</source>
1087 <target state="new">Reset</target> 1105 <target state="new">Reset</target>
1088 <note priority="1" from="description">Password reset button</note> 1106 <note priority="1" from="description">Password reset button</note>
1089 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1107
1090 </trans-unit> 1108 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1091 1109
1092 <trans-unit id="4319634264526091601" datatype="html"> 1110 <trans-unit id="4319634264526091601" datatype="html">
1093 <source>on this instance</source> 1111 <source>on this instance</source>
@@ -1460,7 +1478,7 @@ The link will expire within 1 hour.</target>
1460 <target>Opret en konto</target> 1478 <target>Opret en konto</target>
1461 1479
1462 1480
1463 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1481 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1464 1482
1465 <trans-unit id="3058024914967508975" datatype="html"> 1483 <trans-unit id="3058024914967508975" datatype="html">
1466 <source>My videos</source> 1484 <source>My videos</source>
@@ -1517,7 +1535,7 @@ The link will expire within 1 hour.</target>
1517 <source>VIDEOS</source> 1535 <source>VIDEOS</source>
1518 <target state="new">VIDEOS</target> 1536 <target state="new">VIDEOS</target>
1519 1537
1520 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 1538 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1521 <trans-unit id="667372110624203230" datatype="html"> 1539 <trans-unit id="667372110624203230" datatype="html">
1522 <source>Import jobs concurrency</source> 1540 <source>Import jobs concurrency</source>
1523 <target state="new">Import jobs concurrency</target> 1541 <target state="new">Import jobs concurrency</target>
@@ -1597,7 +1615,7 @@ The link will expire within 1 hour.</target>
1597 <source>I'm a teapot</source> 1615 <source>I'm a teapot</source>
1598 <target state="new">I'm a teapot</target> 1616 <target state="new">I'm a teapot</target>
1599 1617
1600 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit> 1618 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1601 <trans-unit id="1597262876035959248" datatype="html"> 1619 <trans-unit id="1597262876035959248" datatype="html">
1602 <source>That's an error.</source> 1620 <source>That's an error.</source>
1603 <target state="new">That's an error.</target> 1621 <target state="new">That's an error.</target>
@@ -1681,8 +1699,8 @@ The link will expire within 1 hour.</target>
1681 <trans-unit id="2971365540217107489" datatype="html"> 1699 <trans-unit id="2971365540217107489" datatype="html">
1682 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1700 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1683 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1701 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1684 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1702
1685 </trans-unit> 1703 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1686 1704
1687 <trans-unit id="5131854469652959713" datatype="html"> 1705 <trans-unit id="5131854469652959713" datatype="html">
1688 <source>GLOBAL SEARCH</source> 1706 <source>GLOBAL SEARCH</source>
@@ -2397,7 +2415,7 @@ The link will expire within 1 hour.</target>
2397 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2415 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2398 <source>Upload on hold</source><target state="new">Upload on hold</target> 2416 <source>Upload on hold</source><target state="new">Upload on hold</target>
2399 2417
2400 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2418 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2401 <trans-unit id="285180972645018518" datatype="html"> 2419 <trans-unit id="285180972645018518" datatype="html">
2402 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2420 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2403 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2421 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3125,11 +3143,7 @@ The link will expire within 1 hour.</target>
3125 <target>ID</target> 3143 <target>ID</target>
3126 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3144 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3127 </trans-unit> 3145 </trans-unit>
3128 <trans-unit id="2265605798180116441" datatype="html"> 3146
3129 <source>Follower handle</source>
3130 <target state="new">Follower handle</target>
3131
3132 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
3133 <trans-unit id="5911214550882917183" datatype="html"> 3147 <trans-unit id="5911214550882917183" datatype="html">
3134 <source>State</source> 3148 <source>State</source>
3135 <target state="new">State</target> 3149 <target state="new">State</target>
@@ -3204,11 +3218,7 @@ The link will expire within 1 hour.</target>
3204 </target> 3218 </target>
3205 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3219 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3206 </trans-unit> 3220 </trans-unit>
3207 <trans-unit id="6641024648411549335" datatype="html"> 3221
3208 <source>Host</source>
3209 <target state="new">Host</target>
3210
3211 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3212 <trans-unit id="6571718060636962350" datatype="html"> 3222 <trans-unit id="6571718060636962350" datatype="html">
3213 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3223 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3214 <target state="new">Redundancy allowed 3224 <target state="new">Redundancy allowed
@@ -3221,7 +3231,7 @@ The link will expire within 1 hour.</target>
3221 <source>Unfollow</source> 3231 <source>Unfollow</source>
3222 <target state="new">Unfollow</target> 3232 <target state="new">Unfollow</target>
3223 3233
3224 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3234 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3225 <trans-unit id="8246779176913476983" datatype="html"> 3235 <trans-unit id="8246779176913476983" datatype="html">
3226 <source>Open instance in a new tab</source> 3236 <source>Open instance in a new tab</source>
3227 <target state="new">Open instance in a new tab</target> 3237 <target state="new">Open instance in a new tab</target>
@@ -3233,12 +3243,12 @@ The link will expire within 1 hour.</target>
3233 <source>No host found matching current filters.</source> 3243 <source>No host found matching current filters.</source>
3234 <target state="new">No host found matching current filters.</target> 3244 <target state="new">No host found matching current filters.</target>
3235 3245
3236 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3246 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3237 <trans-unit id="7274241885665071790" datatype="html"> 3247 <trans-unit id="7274241885665071790" datatype="html">
3238 <source>Your instance is not following anyone.</source> 3248 <source>Your instance is not following anyone.</source>
3239 <target state="new">Your instance is not following anyone.</target> 3249 <target state="new">Your instance is not following anyone.</target>
3240 3250
3241 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3251 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3242 <trans-unit id="4774348799569692380" datatype="html"> 3252 <trans-unit id="4774348799569692380" datatype="html">
3243 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3253 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3244 <target state="new">Showing 3254 <target state="new">Showing
@@ -3248,16 +3258,8 @@ The link will expire within 1 hour.</target>
3248 </target> 3258 </target>
3249 3259
3250 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3260 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3251 <trans-unit id="6275803119759621687" datatype="html"> 3261
3252 <source>Follow domains</source> 3262 <trans-unit id="9216117865911519658" datatype="html">
3253 <target state="new">Follow domains</target>
3254
3255 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit>
3256 <trans-unit id="1268699198448750610" datatype="html">
3257 <source>Follow instances</source>
3258 <target state="new">Follow instances</target>
3259
3260 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3261 <source>Action</source><target state="new">Action</target> 3263 <source>Action</source><target state="new">Action</target>
3262 3264
3263 3265
@@ -3304,11 +3306,11 @@ The link will expire within 1 hour.</target>
3304 <trans-unit id="5248717555542428023"> 3306 <trans-unit id="5248717555542428023">
3305 <source>Username</source> 3307 <source>Username</source>
3306 <target>Brugernavn</target> 3308 <target>Brugernavn</target>
3307 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3309
3308 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3310
3309 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3311
3310 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3312
3311 </trans-unit> 3313 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3312 <trans-unit id="5428411040014095392" datatype="html"> 3314 <trans-unit id="5428411040014095392" datatype="html">
3313 <source>e.g. jane_doe</source> 3315 <source>e.g. jane_doe</source>
3314 <target state="new">e.g. jane_doe</target> 3316 <target state="new">e.g. jane_doe</target>
@@ -3338,9 +3340,9 @@ The link will expire within 1 hour.</target>
3338 <trans-unit id="4145496584631696119" datatype="html"> 3340 <trans-unit id="4145496584631696119" datatype="html">
3339 <source>Role</source> 3341 <source>Role</source>
3340 <target state="new">Role</target> 3342 <target state="new">Role</target>
3341 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3343
3342 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3344
3343 </trans-unit> 3345 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3344 <trans-unit id="7046347992315328430" datatype="html"> 3346 <trans-unit id="7046347992315328430" datatype="html">
3345 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3347 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3346 <target state="new"> 3348 <target state="new">
@@ -3365,15 +3367,9 @@ The link will expire within 1 hour.</target>
3365 <trans-unit id="2622255144026150901" datatype="html"> 3367 <trans-unit id="2622255144026150901" datatype="html">
3366 <source>Auth plugin</source> 3368 <source>Auth plugin</source>
3367 <target state="new">Auth plugin</target> 3369 <target state="new">Auth plugin</target>
3368 <context-group purpose="location"> 3370
3369 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3371
3370 <context context-type="linenumber">188</context> 3372 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3371 </context-group>
3372 <context-group purpose="location">
3373 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3374 <context context-type="linenumber">188</context>
3375 </context-group>
3376 </trans-unit>
3377 <trans-unit id="588099657508661970" datatype="html"> 3373 <trans-unit id="588099657508661970" datatype="html">
3378 <source>None (local authentication)</source> 3374 <source>None (local authentication)</source>
3379 <target state="new">None (local authentication)</target> 3375 <target state="new">None (local authentication)</target>
@@ -3632,7 +3628,13 @@ The link will expire within 1 hour.</target>
3632 3628
3633 3629
3634 3630
3635 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit> 3631 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3632 <source>Follower</source><target state="new">Follower</target>
3633 <context-group purpose="location">
3634 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3635 <context context-type="linenumber">24</context>
3636 </context-group>
3637 </trans-unit>
3636 <trans-unit id="4691552465058437520" datatype="html"> 3638 <trans-unit id="4691552465058437520" datatype="html">
3637 <source>Commented video</source> 3639 <source>Commented video</source>
3638 <target state="new">Commented video</target> 3640 <target state="new">Commented video</target>
@@ -3961,7 +3963,7 @@ The link will expire within 1 hour.</target>
3961 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 3963 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
3962 </target> 3964 </target>
3963 3965
3964 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3966 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3965 <trans-unit id="4058814854824495833" datatype="html"> 3967 <trans-unit id="4058814854824495833" datatype="html">
3966 <source>Mute domains</source> 3968 <source>Mute domains</source>
3967 <target state="new">Mute domains</target> 3969 <target state="new">Mute domains</target>
@@ -5922,11 +5924,8 @@ color: red;
5922 5924
5923 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5925 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5924 <source>CHANNELS</source><target state="new">CHANNELS</target> 5926 <source>CHANNELS</source><target state="new">CHANNELS</target>
5925 <context-group purpose="location"> 5927
5926 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5928 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5927 <context context-type="linenumber">82</context>
5928 </context-group>
5929 </trans-unit>
5930 5929
5931 <trans-unit id="3666829335406793239" datatype="html"> 5930 <trans-unit id="3666829335406793239" datatype="html">
5932 <source>This account does not have channels.</source> 5931 <source>This account does not have channels.</source>
@@ -5966,7 +5965,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
5966It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 5965It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
5967channel with the same name (<x id="PH_2"/>)!</target> 5966channel with the same name (<x id="PH_2"/>)!</target>
5968 5967
5969 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5968 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5969 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5970 <context-group purpose="location">
5971 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5972 <context context-type="linenumber">48</context>
5973 </context-group>
5974 </trans-unit>
5970 <trans-unit id="5387007581996837469" datatype="html"> 5975 <trans-unit id="5387007581996837469" datatype="html">
5971 <source>My Channels</source> 5976 <source>My Channels</source>
5972 <target state="new">My Channels</target> 5977 <target state="new">My Channels</target>
@@ -6568,12 +6573,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6568 <source>Your message has been sent.</source> 6573 <source>Your message has been sent.</source>
6569 <target state="new">Your message has been sent.</target> 6574 <target state="new">Your message has been sent.</target>
6570 6575
6571 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6576 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6572 <trans-unit id="2072135752262464360" datatype="html"> 6577 <trans-unit id="2072135752262464360" datatype="html">
6573 <source>You already sent this form recently</source> 6578 <source>You already sent this form recently</source>
6574 <target state="new">You already sent this form recently</target> 6579 <target state="new">You already sent this form recently</target>
6575 6580
6576 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6581 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6577 6582
6578 <trans-unit id="819067926858619041" datatype="html"> 6583 <trans-unit id="819067926858619041" datatype="html">
6579 <source>Account videos</source> 6584 <source>Account videos</source>
@@ -6623,12 +6628,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6623 <x id="PH"/> direct account followers 6628 <x id="PH"/> direct account followers
6624 </target> 6629 </target>
6625 6630
6626 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit> 6631 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6627 <trans-unit id="6250999352462648289" datatype="html"> 6632 <trans-unit id="6250999352462648289" datatype="html">
6628 <source>Report this account</source> 6633 <source>Report this account</source>
6629 <target state="new">Report this account</target> 6634 <target state="new">Report this account</target>
6630 6635
6631 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6636 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6632 6637
6633 6638
6634 <trans-unit id="1504521795586863905" datatype="html"> 6639 <trans-unit id="1504521795586863905" datatype="html">
@@ -6643,27 +6648,19 @@ channel with the same name (<x id="PH_2"/>)!</target>
6643 <target state="new">Username copied</target> 6648 <target state="new">Username copied</target>
6644 6649
6645 6650
6646 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit> 6651 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6647 <trans-unit id="9221735175659318025" datatype="html"> 6652 <trans-unit id="9221735175659318025" datatype="html">
6648 <source>1 subscriber</source> 6653 <source>1 subscriber</source>
6649 <target state="new">1 subscriber</target> 6654 <target state="new">1 subscriber</target>
6650 6655
6651 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit> 6656 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6652 <trans-unit id="4097331874769079975" datatype="html"> 6657 <trans-unit id="4097331874769079975" datatype="html">
6653 <source><x id="PH"/> subscribers</source> 6658 <source><x id="PH"/> subscribers</source>
6654 <target state="new"><x id="PH"/> subscribers</target> 6659 <target state="new"><x id="PH"/> subscribers</target>
6655 6660
6656 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6661 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6657 <trans-unit id="4682675125751819107" datatype="html"> 6662
6658 <source>Instances you follow</source> 6663
6659 <target state="new">Instances you follow</target>
6660
6661 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6662 <trans-unit id="8899833753704589712" datatype="html">
6663 <source>Instances following you</source>
6664 <target state="new">Instances following you</target>
6665
6666 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6667 <trans-unit id="1035838766454786107" datatype="html"> 6664 <trans-unit id="1035838766454786107" datatype="html">
6668 <source>Audio-only</source> 6665 <source>Audio-only</source>
6669 <target state="new">Audio-only</target> 6666 <target state="new">Audio-only</target>
@@ -6713,6 +6710,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6713 <source>Auto (via ffmpeg)</source> 6710 <source>Auto (via ffmpeg)</source>
6714 <target state="new">Auto (via ffmpeg)</target> 6711 <target state="new">Auto (via ffmpeg)</target>
6715 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6712 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6713 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6714 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6715 <context-group purpose="location">
6716 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6717 <context context-type="linenumber">3</context>
6718 </context-group>
6716 </trans-unit> 6719 </trans-unit>
6717 <trans-unit id="931255636742351800" datatype="html"> 6720 <trans-unit id="931255636742351800" datatype="html">
6718 <source>No limit</source> 6721 <source>No limit</source>
@@ -6855,18 +6858,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
6855 <trans-unit id="2127446333083057097" datatype="html"> 6858 <trans-unit id="2127446333083057097" datatype="html">
6856 <source>Domain is required.</source> 6859 <source>Domain is required.</source>
6857 <target state="new">Domain is required.</target> 6860 <target state="new">Domain is required.</target>
6858 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6861
6859 </trans-unit> 6862 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6860 <trans-unit id="6780793142903080663" datatype="html"> 6863 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6861 <source>Domains entered are invalid.</source> 6864 <context-group purpose="location">
6862 <target state="new">Domains entered are invalid.</target> 6865 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6863 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6866 <context context-type="linenumber">93</context>
6864 </trans-unit> 6867 </context-group>
6865 <trans-unit id="5886492514458202177" datatype="html"> 6868 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6866 <source>Domains entered contain duplicates.</source> 6869 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6867 <target state="new">Domains entered contain duplicates.</target> 6870 <context-group purpose="location">
6868 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6871 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6872 <context context-type="linenumber">94</context>
6873 </context-group>
6874 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6875 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6876 <context-group purpose="location">
6877 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6878 <context context-type="linenumber">102</context>
6879 </context-group>
6880 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6881 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6882 <context-group purpose="location">
6883 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6884 <context context-type="linenumber">103</context>
6885 </context-group>
6869 </trans-unit> 6886 </trans-unit>
6887
6888
6870 <trans-unit id="240806681889331244" datatype="html"> 6889 <trans-unit id="240806681889331244" datatype="html">
6871 <source>Unlimited</source> 6890 <source>Unlimited</source>
6872 <target state="new">Unlimited</target> 6891 <target state="new">Unlimited</target>
@@ -7026,24 +7045,50 @@ channel with the same name (<x id="PH_2"/>)!</target>
7026 <x id="PH"/> removed from instance followers 7045 <x id="PH"/> removed from instance followers
7027 </target> 7046 </target>
7028 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 7047 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
7048 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
7049 <source>Follow</source><target state="new">Follow</target>
7050 <context-group purpose="location">
7051 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7052 <context context-type="linenumber">3</context>
7053 </context-group>
7054 <context-group purpose="location">
7055 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7056 <context context-type="linenumber">37</context>
7057 </context-group>
7058 <context-group purpose="location">
7059 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7060 <context context-type="linenumber">18</context>
7061 </context-group>
7062 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
7063 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
7064 <context-group purpose="location">
7065 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7066 <context context-type="linenumber">11</context>
7067 </context-group>
7029 </trans-unit> 7068 </trans-unit>
7030 <trans-unit id="2740793005745065895" datatype="html"> 7069 <trans-unit id="2740793005745065895" datatype="html">
7031 <source><x id="PH"/> is not valid </source> 7070 <source><x id="PH"/> is not valid </source>
7032 <target state="new"> 7071 <target state="new">
7033 <x id="PH"/> is not valid 7072 <x id="PH"/> is not valid
7034 </target> 7073 </target>
7035 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 7074
7036 </trans-unit> 7075 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
7037 <trans-unit id="2355066641781598196" datatype="html"> 7076 <trans-unit id="2355066641781598196" datatype="html">
7038 <source>Follow request(s) sent!</source> 7077 <source>Follow request(s) sent!</source>
7039 <target state="new">Follow request(s) sent!</target> 7078 <target state="new">Follow request(s) sent!</target>
7040 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 7079
7080 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
7081 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
7082 <context-group purpose="location">
7083 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7084 <context context-type="linenumber">3</context>
7085 </context-group>
7041 </trans-unit> 7086 </trans-unit>
7042 <trans-unit id="4245720728052819482" datatype="html"> 7087 <trans-unit id="4245720728052819482" datatype="html">
7043 <source>Do you really want to unfollow <x id="PH"/>?</source> 7088 <source>Do you really want to unfollow <x id="PH"/>?</source>
7044 <target state="translated">Do you really want to unfollow <x id="PH"/>?</target> 7089 <target state="translated">Do you really want to unfollow <x id="PH"/>?</target>
7045 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 7090
7046 </trans-unit> 7091 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
7047 <trans-unit id="9160510009013134726" datatype="html"> 7092 <trans-unit id="9160510009013134726" datatype="html">
7048 <source>Unfollow</source> 7093 <source>Unfollow</source>
7049 <target state="new">Unfollow</target> 7094 <target state="new">Unfollow</target>
@@ -7054,8 +7099,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7054 <target state="new">You are not following 7099 <target state="new">You are not following
7055 <x id="PH"/> anymore. 7100 <x id="PH"/> anymore.
7056 </target> 7101 </target>
7057 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 7102
7058 </trans-unit> 7103 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
7059 <trans-unit id="2593763089859685916" datatype="html"> 7104 <trans-unit id="2593763089859685916" datatype="html">
7060 <source>enabled</source> 7105 <source>enabled</source>
7061 <target state="new">enabled</target> 7106 <target state="new">enabled</target>
@@ -7518,9 +7563,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7518 <trans-unit id="1519954996184640001" datatype="html"> 7563 <trans-unit id="1519954996184640001" datatype="html">
7519 <source>Error</source> 7564 <source>Error</source>
7520 <target state="new">Error</target> 7565 <target state="new">Error</target>
7521 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7566
7522 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7567
7523 </trans-unit> 7568 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7524 <trans-unit id="5076187961693950167" datatype="html"> 7569 <trans-unit id="5076187961693950167" datatype="html">
7525 <source>Standard logs</source> 7570 <source>Standard logs</source>
7526 <target state="new">Standard logs</target> 7571 <target state="new">Standard logs</target>
@@ -7565,16 +7610,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7565 <target state="new">Update user password</target> 7610 <target state="new">Update user password</target>
7566 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7611 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7567 </trans-unit> 7612 </trans-unit>
7568 <trans-unit id="177544274549739411" datatype="html"> 7613
7569 <source>Following list</source> 7614
7570 <target state="new">Following list</target>
7571 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7572 </trans-unit>
7573 <trans-unit id="8092429110007204784" datatype="html">
7574 <source>Followers list</source>
7575 <target state="new">Followers list</target>
7576 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7577 </trans-unit>
7578 <trans-unit id="780323526182667308" datatype="html"> 7615 <trans-unit id="780323526182667308" datatype="html">
7579 <source>User <x id="PH"/> updated.</source> 7616 <source>User <x id="PH"/> updated.</source>
7580 <target state="new">User 7617 <target state="new">User
@@ -7614,16 +7651,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7614 <target state="new">Federation</target> 7651 <target state="new">Federation</target>
7615 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7652 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7616 </trans-unit> 7653 </trans-unit>
7617 <trans-unit id="4682675125751819107" datatype="html"> 7654
7618 <source>Instances you follow</source> 7655
7619 <target state="new">Instances you follow</target>
7620 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7621 </trans-unit>
7622 <trans-unit id="8899833753704589712" datatype="html">
7623 <source>Instances following you</source>
7624 <target state="new">Instances following you</target>
7625 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7626 </trans-unit>
7627 <trans-unit id="3767259920053407667" datatype="html"> 7656 <trans-unit id="3767259920053407667" datatype="html">
7628 <source>Videos will be deleted, comments will be tombstoned.</source> 7657 <source>Videos will be deleted, comments will be tombstoned.</source>
7629 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7658 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7654,7 +7683,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7654 <target state="new">Set Email as Verified</target> 7683 <target state="new">Set Email as Verified</target>
7655 7684
7656 7685
7657 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7686 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7687 <source>Created</source><target state="new">Created</target>
7688 <context-group purpose="location">
7689 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7690 <context context-type="linenumber">115</context>
7691 </context-group>
7692 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7693 <source>Daily quota</source><target state="new">Daily quota</target>
7694 <context-group purpose="location">
7695 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7696 <context context-type="linenumber">120</context>
7697 </context-group>
7698 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7699 <source>Last login</source><target state="new">Last login</target>
7700 <context-group purpose="location">
7701 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7702 <context context-type="linenumber">122</context>
7703 </context-group>
7704 </trans-unit>
7658 <trans-unit id="3403978719736970622" datatype="html"> 7705 <trans-unit id="3403978719736970622" datatype="html">
7659 <source>You cannot ban root.</source> 7706 <source>You cannot ban root.</source>
7660 <target state="new">You cannot ban root.</target> 7707 <target state="new">You cannot ban root.</target>
@@ -7964,12 +8011,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
7964 <x id="PH"/> created. 8011 <x id="PH"/> created.
7965 </target> 8012 </target>
7966 8013
7967 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 8014 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7968 <trans-unit id="8723777130353305761" datatype="html"> 8015 <trans-unit id="8723777130353305761" datatype="html">
7969 <source>This name already exists on this instance.</source> 8016 <source>This name already exists on this instance.</source>
7970 <target state="new">This name already exists on this instance.</target> 8017 <target state="new">This name already exists on this instance.</target>
7971 8018
7972 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 8019 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7973 <trans-unit id="7589345916094713536" datatype="html"> 8020 <trans-unit id="7589345916094713536" datatype="html">
7974 <source>Video channel <x id="PH"/> updated.</source> 8021 <source>Video channel <x id="PH"/> updated.</source>
7975 <target state="new">Video channel 8022 <target state="new">Video channel
@@ -7986,13 +8033,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7986 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 8033 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7987 8034
7988 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 8035 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7989 <trans-unit id="2575302837003821736" datatype="html"> 8036
7990 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7991 <target state="new">Please type the display name of the video channel (
7992 <x id="PH"/>) to confirm
7993 </target>
7994
7995 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7996 <trans-unit id="624066830180032195" datatype="html"> 8037 <trans-unit id="624066830180032195" datatype="html">
7997 <source>Video channel <x id="PH"/> deleted.</source> 8038 <source>Video channel <x id="PH"/> deleted.</source>
7998 <target state="new">Video channel 8039 <target state="new">Video channel
@@ -8151,6 +8192,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8151 <source>Ownership change request sent.</source> 8192 <source>Ownership change request sent.</source>
8152 <target state="new">Ownership change request sent.</target> 8193 <target state="new">Ownership change request sent.</target>
8153 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8194 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8195 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8196 <source>Sort by</source><target state="new">Sort by</target>
8197 <context-group purpose="location">
8198 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8199 <context context-type="linenumber">26</context>
8200 </context-group>
8154 </trans-unit> 8201 </trans-unit>
8155 <trans-unit id="3245220240937722814" datatype="html"> 8202 <trans-unit id="3245220240937722814" datatype="html">
8156 <source>My channels</source> 8203 <source>My channels</source>
@@ -8254,7 +8301,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8254 <target state="new">Subscribe to the account</target> 8301 <target state="new">Subscribe to the account</target>
8255 8302
8256 8303
8257 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 8304 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
8258 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 8305 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
8259 <context-group purpose="location"> 8306 <context-group purpose="location">
8260 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 8307 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -8300,35 +8347,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8300 <trans-unit id="3779524668013120370" datatype="html"> 8347 <trans-unit id="3779524668013120370" datatype="html">
8301 <source>Go to my subscriptions</source> 8348 <source>Go to my subscriptions</source>
8302 <target state="new">Go to my subscriptions</target> 8349 <target state="new">Go to my subscriptions</target>
8303 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8350
8304 </trans-unit> 8351 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8305 <trans-unit id="1136469849928650779" datatype="html"> 8352 <trans-unit id="1136469849928650779" datatype="html">
8306 <source>Go to my videos</source> 8353 <source>Go to my videos</source>
8307 <target state="new">Go to my videos</target> 8354 <target state="new">Go to my videos</target>
8308 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8355
8309 </trans-unit> 8356 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8310 <trans-unit id="7836683738999600376" datatype="html"> 8357 <trans-unit id="7836683738999600376" datatype="html">
8311 <source>Go to my imports</source> 8358 <source>Go to my imports</source>
8312 <target state="new">Go to my imports</target> 8359 <target state="new">Go to my imports</target>
8313 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8360
8314 </trans-unit> 8361 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8315 <trans-unit id="7511292153332773503" datatype="html"> 8362 <trans-unit id="7511292153332773503" datatype="html">
8316 <source>Go to my channels</source> 8363 <source>Go to my channels</source>
8317 <target state="new">Go to my channels</target> 8364 <target state="new">Go to my channels</target>
8318 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8365
8319 </trans-unit> 8366 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8320 <trans-unit id="2013324644839511073" datatype="html"> 8367 <trans-unit id="2013324644839511073" datatype="html">
8321 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8368 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8322Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8369Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8323 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8370 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8324Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8371Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8325 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8372
8326 </trans-unit> 8373 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8327 <trans-unit id="375263728166936544" datatype="html"> 8374 <trans-unit id="375263728166936544" datatype="html">
8328 <source>You need to reconnect.</source> 8375 <source>You need to reconnect.</source>
8329 <target state="new">You need to reconnect.</target> 8376 <target state="new">You need to reconnect.</target>
8330 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8377
8331 </trans-unit> 8378 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8332 <trans-unit id="2206638022166154361" datatype="html"> 8379 <trans-unit id="2206638022166154361" datatype="html">
8333 <source>Keyboard Shortcuts:</source> 8380 <source>Keyboard Shortcuts:</source>
8334 <target state="new">Keyboard Shortcuts:</target> 8381 <target state="new">Keyboard Shortcuts:</target>
@@ -8339,6 +8386,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8339 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8386 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8340 <context context-type="linenumber">98</context> 8387 <context context-type="linenumber">98</context>
8341 </context-group> 8388 </context-group>
8389 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8390 <source>In my library</source><target state="new">In my library</target>
8391 <context-group purpose="location">
8392 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8393 <context context-type="linenumber">104</context>
8394 </context-group>
8342 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 8395 </trans-unit><trans-unit id="232050922346936574" datatype="html">
8343 <source>Trending</source><target state="new">Trending</target> 8396 <source>Trending</source><target state="new">Trending</target>
8344 8397
@@ -8362,38 +8415,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8362 <source>Incorrect username or password.</source> 8415 <source>Incorrect username or password.</source>
8363 <target state="new">Incorrect username or password.</target> 8416 <target state="new">Incorrect username or password.</target>
8364 8417
8365 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 8418 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8366 <trans-unit id="6974874606619467663" datatype="html"> 8419 <trans-unit id="6974874606619467663" datatype="html">
8367 <source>Your account is blocked.</source> 8420 <source>Your account is blocked.</source>
8368 <target state="new">Your account is blocked.</target> 8421 <target state="new">Your account is blocked.</target>
8369 8422
8370 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 8423 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8371 <trans-unit id="7939914198003891823" datatype="html"> 8424 <trans-unit id="7939914198003891823" datatype="html">
8372 <source>any language</source> 8425 <source>any language</source>
8373 <target state="new">any language</target> 8426 <target state="new">any language</target>
8374 8427
8375 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8428 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8376 8429
8377 <trans-unit id="5633144232269377096" datatype="html"> 8430 <trans-unit id="5633144232269377096" datatype="html">
8378 <source>hide</source> 8431 <source>hide</source>
8379 <target state="new">hide</target> 8432 <target state="new">hide</target>
8380 8433
8381 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8434 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8382 <trans-unit id="8603861867909474404" datatype="html"> 8435 <trans-unit id="8603861867909474404" datatype="html">
8383 <source>blur</source> 8436 <source>blur</source>
8384 <target state="new">blur</target> 8437 <target state="new">blur</target>
8385 8438
8386 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8439 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8387 <trans-unit id="4534458451100881847" datatype="html"> 8440 <trans-unit id="4534458451100881847" datatype="html">
8388 <source>display</source> 8441 <source>display</source>
8389 <target state="new">display</target> 8442 <target state="new">display</target>
8390 8443
8391 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8444 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8392 <trans-unit id="4467323362722952678" datatype="html"> 8445 <trans-unit id="4467323362722952678" datatype="html">
8393 <source>Unknown</source> 8446 <source>Unknown</source>
8394 <target state="new">Unknown</target> 8447 <target state="new">Unknown</target>
8395 8448
8396 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8449 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8397 <trans-unit id="8781423666414310853" datatype="html"> 8450 <trans-unit id="8781423666414310853" datatype="html">
8398 <source>Your password has been successfully reset!</source> 8451 <source>Your password has been successfully reset!</source>
8399 <target state="new">Your password has been successfully reset!</target> 8452 <target state="new">Your password has been successfully reset!</target>
@@ -9977,18 +10030,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9977 <target state="new">Too many attempts, please try again after 10030 <target state="new">Too many attempts, please try again after
9978 <x id="PH"/> minutes. 10031 <x id="PH"/> minutes.
9979 </target> 10032 </target>
9980 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 10033
9981 </trans-unit> 10034 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9982 <trans-unit id="4965472196059235310" datatype="html"> 10035 <trans-unit id="4965472196059235310" datatype="html">
9983 <source>Too many attempts, please try again later.</source> 10036 <source>Too many attempts, please try again later.</source>
9984 <target state="new">Too many attempts, please try again later.</target> 10037 <target state="new">Too many attempts, please try again later.</target>
9985 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 10038
9986 </trans-unit> 10039 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9987 <trans-unit id="1693549688987384699" datatype="html"> 10040 <trans-unit id="1693549688987384699" datatype="html">
9988 <source>Server error. Please retry later.</source> 10041 <source>Server error. Please retry later.</source>
9989 <target state="new">Server error. Please retry later.</target> 10042 <target state="new">Server error. Please retry later.</target>
9990 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 10043
9991 </trans-unit> 10044 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9992 <trans-unit id="5927402622550505067" datatype="html"> 10045 <trans-unit id="5927402622550505067" datatype="html">
9993 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 10046 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9994 <target state="new">Subscribed to all current channels of 10047 <target state="new">Subscribed to all current channels of
@@ -10582,35 +10635,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10582 <source>Your video was uploaded to your account and is private.</source> 10635 <source>Your video was uploaded to your account and is private.</source>
10583 <target>Din video blev uploadet til din konto og er privat.</target> 10636 <target>Din video blev uploadet til din konto og er privat.</target>
10584 10637
10585 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10638 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10586 <trans-unit id="5699822024600815733" datatype="html"> 10639 <trans-unit id="5699822024600815733" datatype="html">
10587 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10640 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10588 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target> 10641 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target>
10589 10642
10590 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10643 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10591 <trans-unit id="1219739004043110649"> 10644 <trans-unit id="1219739004043110649">
10592 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10645 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10593 <target>Din video er ikke uploadet endnu, er du sikker på, at du vil forlade denne side?</target> 10646 <target>Din video er ikke uploadet endnu, er du sikker på, at du vil forlade denne side?</target>
10594 10647
10595 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10648 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10596 <trans-unit id="6932865105766151309" datatype="html"> 10649 <trans-unit id="6932865105766151309" datatype="html">
10597 <source>Upload</source> 10650 <source>Upload</source>
10598 <target state="new">Upload</target> 10651 <target state="new">Upload</target>
10599 10652
10600 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10653 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10601 <trans-unit id="8278735427925094503" datatype="html"> 10654 <trans-unit id="8278735427925094503" datatype="html">
10602 <source>Upload <x id="PH"/> </source> 10655 <source>Upload <x id="PH"/> </source>
10603 <target state="new">Upload 10656 <target state="new">Upload
10604 <x id="PH"/> 10657 <x id="PH"/>
10605 </target> 10658 </target>
10606 10659
10607 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10660 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10608 10661
10609 <trans-unit id="5981816353437801748"> 10662 <trans-unit id="5981816353437801748">
10610 <source>Video published.</source> 10663 <source>Video published.</source>
10611 <target>Video offentliggjort.</target> 10664 <target>Video offentliggjort.</target>
10612 10665
10613 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10666 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10614 10667
10615 10668
10616 <trans-unit id="764164089183618119" datatype="html"> 10669 <trans-unit id="764164089183618119" datatype="html">
@@ -10678,27 +10731,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10678 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10731 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10679 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10732 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10680 10733
10681 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10734 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10682 <trans-unit id="5761611056224181752" datatype="html"> 10735 <trans-unit id="5761611056224181752" datatype="html">
10683 <source>Redirection</source> 10736 <source>Redirection</source>
10684 <target state="new">Redirection</target> 10737 <target state="new">Redirection</target>
10685 10738
10686 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10739 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10687 <trans-unit id="8858527736400081688" datatype="html"> 10740 <trans-unit id="8858527736400081688" datatype="html">
10688 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10741 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10689 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target> 10742 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target>
10690 10743
10691 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10744 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10692 <trans-unit id="3937119019020041049" datatype="html"> 10745 <trans-unit id="3937119019020041049" datatype="html">
10693 <source>Mature or explicit content</source> 10746 <source>Mature or explicit content</source>
10694 <target state="new">Mature or explicit content</target> 10747 <target state="new">Mature or explicit content</target>
10695 10748
10696 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10749 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10697 <trans-unit id="1755474755114288376" datatype="html"> 10750 <trans-unit id="1755474755114288376" datatype="html">
10698 <source>Up Next</source> 10751 <source>Up Next</source>
10699 <target state="new">Up Next</target> 10752 <target state="new">Up Next</target>
10700 10753
10701 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10754 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10702 <trans-unit id="2159130950882492111" datatype="html"> 10755 <trans-unit id="2159130950882492111" datatype="html">
10703 <source>Cancel</source> 10756 <source>Cancel</source>
10704 <target state="new">Cancel</target> 10757 <target state="new">Cancel</target>
@@ -10708,62 +10761,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10708 <source>Autoplay is suspended</source> 10761 <source>Autoplay is suspended</source>
10709 <target state="new">Autoplay is suspended</target> 10762 <target state="new">Autoplay is suspended</target>
10710 10763
10711 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10764 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10712 <trans-unit id="7895294730547405228" datatype="html"> 10765 <trans-unit id="7895294730547405228" datatype="html">
10713 <source>Enter/exit fullscreen (requires player focus)</source> 10766 <source>Enter/exit fullscreen (requires player focus)</source>
10714 <target state="new">Enter/exit fullscreen (requires player focus)</target> 10767 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10715 10768
10716 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10769 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10717 <trans-unit id="7618388257165864759" datatype="html"> 10770 <trans-unit id="7618388257165864759" datatype="html">
10718 <source>Play/Pause the video (requires player focus)</source> 10771 <source>Play/Pause the video (requires player focus)</source>
10719 <target state="new">Play/Pause the video (requires player focus)</target> 10772 <target state="new">Play/Pause the video (requires player focus)</target>
10720 10773
10721 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10774 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10722 <trans-unit id="7761890399634216630" datatype="html"> 10775 <trans-unit id="7761890399634216630" datatype="html">
10723 <source>Mute/unmute the video (requires player focus)</source> 10776 <source>Mute/unmute the video (requires player focus)</source>
10724 <target state="new">Mute/unmute the video (requires player focus)</target> 10777 <target state="new">Mute/unmute the video (requires player focus)</target>
10725 10778
10726 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10779 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10727 <trans-unit id="5996585232248234904" datatype="html"> 10780 <trans-unit id="5996585232248234904" datatype="html">
10728 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10781 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10729 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10782 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10730 10783
10731 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10784 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10732 <trans-unit id="3748765405903319998" datatype="html"> 10785 <trans-unit id="3748765405903319998" datatype="html">
10733 <source>Increase the volume (requires player focus)</source> 10786 <source>Increase the volume (requires player focus)</source>
10734 <target state="new">Increase the volume (requires player focus)</target> 10787 <target state="new">Increase the volume (requires player focus)</target>
10735 10788
10736 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10789 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10737 <trans-unit id="5810704036407159982" datatype="html"> 10790 <trans-unit id="5810704036407159982" datatype="html">
10738 <source>Decrease the volume (requires player focus)</source> 10791 <source>Decrease the volume (requires player focus)</source>
10739 <target state="new">Decrease the volume (requires player focus)</target> 10792 <target state="new">Decrease the volume (requires player focus)</target>
10740 10793
10741 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10794 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10742 <trans-unit id="2622048822548065691" datatype="html"> 10795 <trans-unit id="2622048822548065691" datatype="html">
10743 <source>Seek the video forward (requires player focus)</source> 10796 <source>Seek the video forward (requires player focus)</source>
10744 <target state="new">Seek the video forward (requires player focus)</target> 10797 <target state="new">Seek the video forward (requires player focus)</target>
10745 10798
10746 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10799 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10747 <trans-unit id="6540078205109221153" datatype="html"> 10800 <trans-unit id="6540078205109221153" datatype="html">
10748 <source>Seek the video backward (requires player focus)</source> 10801 <source>Seek the video backward (requires player focus)</source>
10749 <target state="new">Seek the video backward (requires player focus)</target> 10802 <target state="new">Seek the video backward (requires player focus)</target>
10750 10803
10751 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10804 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10752 <trans-unit id="1956491957766210808" datatype="html"> 10805 <trans-unit id="1956491957766210808" datatype="html">
10753 <source>Increase playback rate (requires player focus)</source> 10806 <source>Increase playback rate (requires player focus)</source>
10754 <target state="new">Increase playback rate (requires player focus)</target> 10807 <target state="new">Increase playback rate (requires player focus)</target>
10755 10808
10756 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10809 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10757 <trans-unit id="5495529997674803186" datatype="html"> 10810 <trans-unit id="5495529997674803186" datatype="html">
10758 <source>Decrease playback rate (requires player focus)</source> 10811 <source>Decrease playback rate (requires player focus)</source>
10759 <target state="new">Decrease playback rate (requires player focus)</target> 10812 <target state="new">Decrease playback rate (requires player focus)</target>
10760 10813
10761 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10814 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10762 <trans-unit id="3178343147230721210" datatype="html"> 10815 <trans-unit id="3178343147230721210" datatype="html">
10763 <source>Navigate in the video frame by frame (requires player focus)</source> 10816 <source>Navigate in the video frame by frame (requires player focus)</source>
10764 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 10817 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10765 10818
10766 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10819 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10767 <trans-unit id="8025996572234182184" datatype="html"> 10820 <trans-unit id="8025996572234182184" datatype="html">
10768 <source>Like the video</source> 10821 <source>Like the video</source>
10769 <target state="new">Like the video</target> 10822 <target state="new">Like the video</target>
diff --git a/client/src/locale/angular.de-DE.xlf b/client/src/locale/angular.de-DE.xlf
index 6f2d33ba5..0a8ca956b 100644
--- a/client/src/locale/angular.de-DE.xlf
+++ b/client/src/locale/angular.de-DE.xlf
@@ -173,7 +173,7 @@
173 173
174 174
175 175
176 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 176 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
177 <trans-unit id="1486537403020619891" datatype="html"> 177 <trans-unit id="1486537403020619891" datatype="html">
178 <source>My watch history</source> 178 <source>My watch history</source>
179 <target state="translated">Mein Verlauf</target> 179 <target state="translated">Mein Verlauf</target>
@@ -242,23 +242,23 @@
242 <trans-unit id="5674286808255988565"> 242 <trans-unit id="5674286808255988565">
243 <source>Create</source> 243 <source>Create</source>
244 <target>Erstellen</target> 244 <target>Erstellen</target>
245 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 245
246 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 246
247 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 247
248 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 248
249 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 249
250 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 250
251 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 251
252 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 252
253 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 253
254 </trans-unit> 254 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
255 <trans-unit id="1006562256968398209" datatype="html"> 255 <trans-unit id="1006562256968398209" datatype="html">
256 <source>video</source> 256 <source>video</source>
257 <target state="translated">Video</target> 257 <target state="translated">Video</target>
258 258
259 259
260 260
261 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 261 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
262 <trans-unit id="6438815964972582865" datatype="html"> 262 <trans-unit id="6438815964972582865" datatype="html">
263 <source>The following link contains a private token and should not be shared with anyone.</source> 263 <source>The following link contains a private token and should not be shared with anyone.</source>
264 <target state="translated">Der folgende Link enthält ein privates Token und sollte nicht an Dritte weitergegeben werden.</target> 264 <target state="translated">Der folgende Link enthält ein privates Token und sollte nicht an Dritte weitergegeben werden.</target>
@@ -331,13 +331,13 @@
331 <trans-unit id="6995024616159044376" datatype="html"> 331 <trans-unit id="6995024616159044376" datatype="html">
332 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 332 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
333 <target state="translated">Ihr Videokontingent ist mit diesem Video überschritten (Videogröße: <x id="PH" equiv-text="videoSizeBytes"/>, verwendet: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, Kontingent: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 333 <target state="translated">Ihr Videokontingent ist mit diesem Video überschritten (Videogröße: <x id="PH" equiv-text="videoSizeBytes"/>, verwendet: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, Kontingent: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
334 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 334
335 </trans-unit> 335 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
336 <trans-unit id="7873395933409147217" datatype="html"> 336 <trans-unit id="7873395933409147217" datatype="html">
337 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 337 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
338 <target state="translated">Ihr tägliches Videokontingent wurde mit diesem Video überschritten (Videogröße: <x id="PH" equiv-text="videoSizeBytes"/>, verwendet: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, Kontingent: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 338 <target state="translated">Ihr tägliches Videokontingent wurde mit diesem Video überschritten (Videogröße: <x id="PH" equiv-text="videoSizeBytes"/>, verwendet: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, Kontingent: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
339 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 339
340 </trans-unit> 340 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
341 <trans-unit id="5235042777215655908" datatype="html"> 341 <trans-unit id="5235042777215655908" datatype="html">
342 <source>subtitles</source> 342 <source>subtitles</source>
343 <target state="translated">Untertitel</target> 343 <target state="translated">Untertitel</target>
@@ -775,10 +775,10 @@
775 <trans-unit id="2602586221576511475"> 775 <trans-unit id="2602586221576511475">
776 <source>Video quota</source> 776 <source>Video quota</source>
777 <target>Videokontingent</target> 777 <target>Videokontingent</target>
778 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 778
779 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 779
780 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 780
781 </trans-unit> 781 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
782 <trans-unit id="1502595455339510144"> 782 <trans-unit id="1502595455339510144">
783 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 783 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
784 <target>Unbegrenzt <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> pro Tag)<x id="CLOSE_TAG_NG_CONTAINER"/></target> 784 <target>Unbegrenzt <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> pro Tag)<x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -858,6 +858,30 @@
858 <target state="translated">Föderation</target> 858 <target state="translated">Föderation</target>
859 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 859 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
860 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 860 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
861 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
862 <source>Following</source><target state="new">Following</target>
863 <context-group purpose="location">
864 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
865 <context context-type="linenumber">29</context>
866 </context-group>
867 <context-group purpose="location">
868 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
869 <context context-type="linenumber">31</context>
870 </context-group>
871 <context-group purpose="location">
872 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
873 <context context-type="linenumber">28</context>
874 </context-group>
875 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
876 <source>Followers</source><target state="new">Followers</target>
877 <context-group purpose="location">
878 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
879 <context context-type="linenumber">34</context>
880 </context-group>
881 <context-group purpose="location">
882 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
883 <context context-type="linenumber">37</context>
884 </context-group>
861 </trans-unit> 885 </trans-unit>
862 <trans-unit id="3541687134897970106" datatype="html"> 886 <trans-unit id="3541687134897970106" datatype="html">
863 <source>followers</source> 887 <source>followers</source>
@@ -939,7 +963,7 @@
939 963
940 964
941 965
942 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 966 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
943 <trans-unit id="3616223838716839702"> 967 <trans-unit id="3616223838716839702">
944 <source>Ban this user</source> 968 <source>Ban this user</source>
945 <target>Diesen Nutzer sperren</target> 969 <target>Diesen Nutzer sperren</target>
@@ -1005,19 +1029,13 @@
1005 <trans-unit id="7252854992688790751" datatype="html"> 1029 <trans-unit id="7252854992688790751" datatype="html">
1006 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1030 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1007 <target state="translated">Diese Instanz erlaubt eine Registrierung. Lesen sie sich trotzdem die <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Richtlinien<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Richtlinien<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>durch, bevor sie sich ein Konto erstellen. Hier können sie auch nach einer anderen Instanz suchen, die ihren Wünschen entspricht: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1031 <target state="translated">Diese Instanz erlaubt eine Registrierung. Lesen sie sich trotzdem die <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Richtlinien<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Richtlinien<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>durch, bevor sie sich ein Konto erstellen. Hier können sie auch nach einer anderen Instanz suchen, die ihren Wünschen entspricht: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1008 <context-group purpose="location"> 1032
1009 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1033 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1010 <context context-type="linenumber">60,62</context>
1011 </context-group>
1012 </trans-unit>
1013 <trans-unit id="7215649348148521605" datatype="html"> 1034 <trans-unit id="7215649348148521605" datatype="html">
1014 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1035 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1015 <target state="translated">Diese Instanz erlaubt zurzeit keine Registrierung, sie können sich die <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Richtlinien<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> für mehr Details durchlesen, oder hier nach einer Instanz suchen, die Ihnen das Anlegen eines Kontos und das Hochladen von Videos erlaubt: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1036 <target state="translated">Diese Instanz erlaubt zurzeit keine Registrierung, sie können sich die <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Richtlinien<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> für mehr Details durchlesen, oder hier nach einer Instanz suchen, die Ihnen das Anlegen eines Kontos und das Hochladen von Videos erlaubt: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1016 <context-group purpose="location"> 1037
1017 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1038 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1018 <context context-type="linenumber">65,67</context>
1019 </context-group>
1020 </trans-unit>
1021 <trans-unit id="2392488717875840729"> 1039 <trans-unit id="2392488717875840729">
1022 <source>User</source> 1040 <source>User</source>
1023 <target>Benutzer</target> 1041 <target>Benutzer</target>
@@ -1028,67 +1046,67 @@
1028 <source>Username or email address</source> 1046 <source>Username or email address</source>
1029 <target>Benutzername oder E-Mail-Adresse</target> 1047 <target>Benutzername oder E-Mail-Adresse</target>
1030 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1048 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1049 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1050 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1051 <context-group purpose="location">
1052 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1053 <context context-type="linenumber">33,34</context>
1054 </context-group>
1031 </trans-unit> 1055 </trans-unit>
1032 <trans-unit id="1431416938026210429"> 1056 <trans-unit id="1431416938026210429">
1033 <source>Password</source> 1057 <source>Password</source>
1034 <target>Passwort</target> 1058 <target>Passwort</target>
1035 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1059
1036 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1060
1037 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1061
1038 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1062
1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1063
1040 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1064
1041 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1065
1042 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1066
1043 </trans-unit> 1067 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1044 <trans-unit id="8715156686857791956" datatype="html"> 1068 <trans-unit id="8715156686857791956" datatype="html">
1045 <source>Click here to reset your password</source> 1069 <source>Click here to reset your password</source>
1046 <target state="translated">Klicken Sie hier, um Ihr Passwort zurückzusetzen</target> 1070 <target state="translated">Klicken Sie hier, um Ihr Passwort zurückzusetzen</target>
1047 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1071
1048 </trans-unit> 1072 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1049 <trans-unit id="892063502898494584" datatype="html"> 1073 <trans-unit id="892063502898494584" datatype="html">
1050 <source>I forgot my password</source> 1074 <source>I forgot my password</source>
1051 <target state="translated">Ich habe mein Passwort vergessen</target> 1075 <target state="translated">Ich habe mein Passwort vergessen</target>
1052 <context-group purpose="location"> 1076
1053 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1077 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1054 <context context-type="linenumber">47</context>
1055 </context-group>
1056 </trans-unit>
1057 <trans-unit id="2101170466365500913" datatype="html"> 1078 <trans-unit id="2101170466365500913" datatype="html">
1058 <source>Logging into an account lets you publish content</source> 1079 <source>Logging into an account lets you publish content</source>
1059 <target state="translated">Durch das Anmelden können sie Inhalte hochladen</target> 1080 <target state="translated">Durch das Anmelden können sie Inhalte hochladen</target>
1060 <context-group purpose="location"> 1081
1061 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1082 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1062 <context context-type="linenumber">56,57</context>
1063 </context-group>
1064 </trans-unit>
1065 <trans-unit id="2454050363478003966"> 1083 <trans-unit id="2454050363478003966">
1066 <source>Login</source> 1084 <source>Login</source>
1067 <target>Anmelden</target> 1085 <target>Anmelden</target>
1068 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1086
1069 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1087
1070 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1088
1071 </trans-unit> 1089 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1072 <trans-unit id="3183213940445113677" datatype="html"> 1090 <trans-unit id="3183213940445113677" datatype="html">
1073 <source>Or sign in with</source> 1091 <source>Or sign in with</source>
1074 <target state="translated">Oder anmelden mit</target> 1092 <target state="translated">Oder anmelden mit</target>
1075 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1093
1076 </trans-unit> 1094 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1077 <trans-unit id="3238209155172574367"> 1095 <trans-unit id="3238209155172574367">
1078 <source>Forgot your password</source> 1096 <source>Forgot your password</source>
1079 <target>Passwort vergessen</target> 1097 <target>Passwort vergessen</target>
1080 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1098
1081 </trans-unit> 1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1082 <trans-unit id="87327320394367488" datatype="html"> 1100 <trans-unit id="87327320394367488" datatype="html">
1083 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1101 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1084 <target state="translated">Es tut uns Leid, du kannst dein Kennwort nicht wiederherstellen weil dein Instanzadministrator das PeerTube-E-Mail-System nicht konfiguriert hat.</target> 1102 <target state="translated">Es tut uns Leid, du kannst dein Kennwort nicht wiederherstellen weil dein Instanzadministrator das PeerTube-E-Mail-System nicht konfiguriert hat.</target>
1085 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1103
1086 </trans-unit> 1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1087 <trans-unit id="3188014010833256853" datatype="html"> 1105 <trans-unit id="3188014010833256853" datatype="html">
1088 <source>Enter your email address and we will send you a link to reset your password.</source> 1106 <source>Enter your email address and we will send you a link to reset your password.</source>
1089 <target state="translated">Geben Sie Ihre E-Mail-Adresse ein und wir senden Ihnen einen Link zum Zurücksetzen Ihres Passworts.</target> 1107 <target state="translated">Geben Sie Ihre E-Mail-Adresse ein und wir senden Ihnen einen Link zum Zurücksetzen Ihres Passworts.</target>
1090 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1108
1091 </trans-unit> 1109 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1092 <trans-unit id="1190256911880544559" datatype="html"> 1110 <trans-unit id="1190256911880544559" datatype="html">
1093 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1111 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1094The link will expire within 1 hour.</source> 1112The link will expire within 1 hour.</source>
@@ -1098,26 +1116,26 @@ The link will expire within 1 hour.</source>
1098 <trans-unit id="4768749765465246664"> 1116 <trans-unit id="4768749765465246664">
1099 <source>Email</source> 1117 <source>Email</source>
1100 <target>E-Mail</target> 1118 <target>E-Mail</target>
1101 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1119
1102 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1120
1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1121
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1122
1105 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1123
1106 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1124
1107 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1125
1108 </trans-unit> 1126 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1109 <trans-unit id="3967269098753656610"> 1127 <trans-unit id="3967269098753656610">
1110 <source>Email address</source> 1128 <source>Email address</source>
1111 <target>E-Mail-Adresse</target> 1129 <target>E-Mail-Adresse</target>
1112 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1130
1113 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1131
1114 </trans-unit> 1132 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1115 <trans-unit id="7808756054397155068" datatype="html"> 1133 <trans-unit id="7808756054397155068" datatype="html">
1116 <source>Reset</source> 1134 <source>Reset</source>
1117 <target state="translated">Zurücksetzen</target> 1135 <target state="translated">Zurücksetzen</target>
1118 <note priority="1" from="description">Password reset button</note> 1136 <note priority="1" from="description">Password reset button</note>
1119 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1137
1120 </trans-unit> 1138 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1121 <trans-unit id="4319634264526091601" datatype="html"> 1139 <trans-unit id="4319634264526091601" datatype="html">
1122 <source>on this instance</source> 1140 <source>on this instance</source>
1123 <target state="translated">auf dieser Instanz</target> 1141 <target state="translated">auf dieser Instanz</target>
@@ -1444,9 +1462,9 @@ Hilf mit PeerTube zu übersetzen!</target>
1444 <trans-unit id="2308975396733519902"> 1462 <trans-unit id="2308975396733519902">
1445 <source>Create an account</source> 1463 <source>Create an account</source>
1446 <target>Konto erstellen</target> 1464 <target>Konto erstellen</target>
1447 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1465
1448 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1466
1449 </trans-unit> 1467 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1450 <trans-unit id="3058024914967508975" datatype="html"> 1468 <trans-unit id="3058024914967508975" datatype="html">
1451 <source>My videos</source> 1469 <source>My videos</source>
1452 <target state="translated">Meine Videos</target> 1470 <target state="translated">Meine Videos</target>
@@ -1513,10 +1531,10 @@ Hilf mit PeerTube zu übersetzen!</target>
1513 <trans-unit id="1504521795586863905" datatype="html"> 1531 <trans-unit id="1504521795586863905" datatype="html">
1514 <source>VIDEOS</source> 1532 <source>VIDEOS</source>
1515 <target state="translated">VIDEOS</target> 1533 <target state="translated">VIDEOS</target>
1516 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1534
1517 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1535
1518 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1536
1519 </trans-unit> 1537 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1520 <trans-unit id="667372110624203230" datatype="html"> 1538 <trans-unit id="667372110624203230" datatype="html">
1521 <source>Import jobs concurrency</source> 1539 <source>Import jobs concurrency</source>
1522 <target state="translated">Gleichzeitigkeit von Importaufträgen</target> 1540 <target state="translated">Gleichzeitigkeit von Importaufträgen</target>
@@ -1594,8 +1612,8 @@ Hilf mit PeerTube zu übersetzen!</target>
1594 <trans-unit id="4424964105331349857" datatype="html"> 1612 <trans-unit id="4424964105331349857" datatype="html">
1595 <source>I'm a teapot</source> 1613 <source>I'm a teapot</source>
1596 <target state="translated">Ich bin ein Teekessel</target> 1614 <target state="translated">Ich bin ein Teekessel</target>
1597 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1615
1598 </trans-unit> 1616 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1599 <trans-unit id="1597262876035959248" datatype="html"> 1617 <trans-unit id="1597262876035959248" datatype="html">
1600 <source>That's an error.</source> 1618 <source>That's an error.</source>
1601 <target state="translated">Dies ist ein Fehler.</target> 1619 <target state="translated">Dies ist ein Fehler.</target>
@@ -1688,8 +1706,8 @@ Hilf mit PeerTube zu übersetzen!</target>
1688 <trans-unit id="2971365540217107489" datatype="html"> 1706 <trans-unit id="2971365540217107489" datatype="html">
1689 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1707 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1690 <target state="translated">Das Medium ist zu groß für den Server. Bitte wenden Sie sich an Ihren Administrator, wenn Sie das Größenlimit erhöhen möchten.</target> 1708 <target state="translated">Das Medium ist zu groß für den Server. Bitte wenden Sie sich an Ihren Administrator, wenn Sie das Größenlimit erhöhen möchten.</target>
1691 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1709
1692 </trans-unit> 1710 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1693 1711
1694 <trans-unit id="5131854469652959713" datatype="html"> 1712 <trans-unit id="5131854469652959713" datatype="html">
1695 <source>GLOBAL SEARCH</source> 1713 <source>GLOBAL SEARCH</source>
@@ -2430,8 +2448,8 @@ Hilf mit PeerTube zu übersetzen!</target>
2430 <trans-unit id="6161604372916832458" datatype="html"> 2448 <trans-unit id="6161604372916832458" datatype="html">
2431 <source>Upload on hold</source> 2449 <source>Upload on hold</source>
2432 <target state="translated">Upload angehalten</target> 2450 <target state="translated">Upload angehalten</target>
2433 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2451
2434 </trans-unit> 2452 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2435 <trans-unit id="285180972645018518" datatype="html"> 2453 <trans-unit id="285180972645018518" datatype="html">
2436 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2454 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2437 <target state="translated">Entschuldigung, Ihr Account kann keine Videos hochladen. Wenn Sie Videos hochladen möchten, muss ein Administrator Ihr Videokontingent freischalten.</target> 2455 <target state="translated">Entschuldigung, Ihr Account kann keine Videos hochladen. Wenn Sie Videos hochladen möchten, muss ein Administrator Ihr Videokontingent freischalten.</target>
@@ -3129,11 +3147,7 @@ Hilf mit PeerTube zu übersetzen!</target>
3129 <target>ID</target> 3147 <target>ID</target>
3130 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3148 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3131 </trans-unit> 3149 </trans-unit>
3132 <trans-unit id="2265605798180116441"> 3150
3133 <source>Follower handle</source>
3134 <target>Folger-Identifikator</target>
3135 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3136 </trans-unit>
3137 <trans-unit id="5911214550882917183"> 3151 <trans-unit id="5911214550882917183">
3138 <source>State</source> 3152 <source>State</source>
3139 <target>Status</target> 3153 <target>Status</target>
@@ -3199,11 +3213,7 @@ Hilf mit PeerTube zu übersetzen!</target>
3199 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target> 3213 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target>
3200 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3214 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3201 </trans-unit> 3215 </trans-unit>
3202 <trans-unit id="6641024648411549335"> 3216
3203 <source>Host</source>
3204 <target>Host</target>
3205 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3206 </trans-unit>
3207 <trans-unit id="6571718060636962350" datatype="html"> 3217 <trans-unit id="6571718060636962350" datatype="html">
3208 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3218 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3209 <target state="translated">Redundanz erlaubt <x id="START_TAG_P-SORTICON"/> <x id="CLOSE_TAG_P-SORTICON"/></target> 3219 <target state="translated">Redundanz erlaubt <x id="START_TAG_P-SORTICON"/> <x id="CLOSE_TAG_P-SORTICON"/></target>
@@ -3212,9 +3222,9 @@ Hilf mit PeerTube zu übersetzen!</target>
3212 <trans-unit id="9160510009013134726" datatype="html"> 3222 <trans-unit id="9160510009013134726" datatype="html">
3213 <source>Unfollow</source> 3223 <source>Unfollow</source>
3214 <target state="translated">Nichtmehr folgen</target> 3224 <target state="translated">Nichtmehr folgen</target>
3215 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3225
3216 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3226
3217 </trans-unit> 3227 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3218 <trans-unit id="8246779176913476983" datatype="html"> 3228 <trans-unit id="8246779176913476983" datatype="html">
3219 <source>Open instance in a new tab</source> 3229 <source>Open instance in a new tab</source>
3220 <target state="translated">Öffne die Instanz in einem neuen Tab</target> 3230 <target state="translated">Öffne die Instanz in einem neuen Tab</target>
@@ -3225,28 +3235,20 @@ Hilf mit PeerTube zu übersetzen!</target>
3225 <trans-unit id="9132918641931433659" datatype="html"> 3235 <trans-unit id="9132918641931433659" datatype="html">
3226 <source>No host found matching current filters.</source> 3236 <source>No host found matching current filters.</source>
3227 <target state="translated">Kein Host für die aktuellen Filter gefunden.</target> 3237 <target state="translated">Kein Host für die aktuellen Filter gefunden.</target>
3228 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3238
3229 </trans-unit> 3239 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3230 <trans-unit id="7274241885665071790" datatype="html"> 3240 <trans-unit id="7274241885665071790" datatype="html">
3231 <source>Your instance is not following anyone.</source> 3241 <source>Your instance is not following anyone.</source>
3232 <target state="translated">Ihre Instanz folgt niemandem.</target> 3242 <target state="translated">Ihre Instanz folgt niemandem.</target>
3233 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3243
3234 </trans-unit> 3244 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3235 <trans-unit id="4774348799569692380" datatype="html"> 3245 <trans-unit id="4774348799569692380" datatype="html">
3236 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3246 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3237 <target state="translated">Zeige <x id="INTERPOLATION"/> bis <x id="INTERPOLATION_1"/> von <x id="INTERPOLATION_2"/> Ho</target> 3247 <target state="translated">Zeige <x id="INTERPOLATION"/> bis <x id="INTERPOLATION_1"/> von <x id="INTERPOLATION_2"/> Ho</target>
3238 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3248 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3239 </trans-unit> 3249 </trans-unit>
3240 <trans-unit id="6275803119759621687" datatype="html"> 3250
3241 <source>Follow domains</source> 3251
3242 <target state="translated">Folge Domains</target>
3243 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3244 </trans-unit>
3245 <trans-unit id="1268699198448750610" datatype="html">
3246 <source>Follow instances</source>
3247 <target state="translated">Instanzen folgen</target>
3248 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3249 </trans-unit>
3250 <trans-unit id="9216117865911519658" datatype="html"> 3252 <trans-unit id="9216117865911519658" datatype="html">
3251 <source>Action</source> 3253 <source>Action</source>
3252 <target state="translated">Aktion</target> 3254 <target state="translated">Aktion</target>
@@ -3296,11 +3298,11 @@ Hilf mit PeerTube zu übersetzen!</target>
3296 <trans-unit id="5248717555542428023"> 3298 <trans-unit id="5248717555542428023">
3297 <source>Username</source> 3299 <source>Username</source>
3298 <target>Nutzername</target> 3300 <target>Nutzername</target>
3299 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3301
3300 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3302
3301 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3303
3302 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3304
3303 </trans-unit> 3305 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3304 <trans-unit id="5428411040014095392" datatype="html"> 3306 <trans-unit id="5428411040014095392" datatype="html">
3305 <source>e.g. jane_doe</source> 3307 <source>e.g. jane_doe</source>
3306 <target state="translated">z.B. max_mustermann</target> 3308 <target state="translated">z.B. max_mustermann</target>
@@ -3328,9 +3330,9 @@ Hilf mit PeerTube zu übersetzen!</target>
3328 <trans-unit id="4145496584631696119"> 3330 <trans-unit id="4145496584631696119">
3329 <source>Role</source> 3331 <source>Role</source>
3330 <target>Benutzerrolle</target> 3332 <target>Benutzerrolle</target>
3331 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3333
3332 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3334
3333 </trans-unit> 3335 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3334 <trans-unit id="7046347992315328430" datatype="html"> 3336 <trans-unit id="7046347992315328430" datatype="html">
3335 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3337 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3336 <target state="translated">Transkodierung aktiviert. Das Videokontingent wird anhand der <x id="START_TAG_STRONG"/>originalen<x id="CLOSE_TAG_STRONG"/> Videogröße berechnet. <x id="LINE_BREAK"/> Dieser Nutzer kann maximal ~ <x id="INTERPOLATION"/> hochladen. </target> 3338 <target state="translated">Transkodierung aktiviert. Das Videokontingent wird anhand der <x id="START_TAG_STRONG"/>originalen<x id="CLOSE_TAG_STRONG"/> Videogröße berechnet. <x id="LINE_BREAK"/> Dieser Nutzer kann maximal ~ <x id="INTERPOLATION"/> hochladen. </target>
@@ -3347,15 +3349,9 @@ Hilf mit PeerTube zu übersetzen!</target>
3347 <trans-unit id="2622255144026150901" datatype="html"> 3349 <trans-unit id="2622255144026150901" datatype="html">
3348 <source>Auth plugin</source> 3350 <source>Auth plugin</source>
3349 <target state="translated">Auth-Plugin</target> 3351 <target state="translated">Auth-Plugin</target>
3350 <context-group purpose="location"> 3352
3351 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3353
3352 <context context-type="linenumber">188</context> 3354 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3353 </context-group>
3354 <context-group purpose="location">
3355 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3356 <context context-type="linenumber">188</context>
3357 </context-group>
3358 </trans-unit>
3359 <trans-unit id="588099657508661970" datatype="html"> 3355 <trans-unit id="588099657508661970" datatype="html">
3360 <source>None (local authentication)</source> 3356 <source>None (local authentication)</source>
3361 <target state="translated">Keine (lokale Authentifizierung)</target> 3357 <target state="translated">Keine (lokale Authentifizierung)</target>
@@ -3606,6 +3602,12 @@ Hilf mit PeerTube zu übersetzen!</target>
3606 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3602 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3607 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3603 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3608 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3604 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3605 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3606 <source>Follower</source><target state="new">Follower</target>
3607 <context-group purpose="location">
3608 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3609 <context context-type="linenumber">24</context>
3610 </context-group>
3609 </trans-unit> 3611 </trans-unit>
3610 <trans-unit id="4691552465058437520" datatype="html"> 3612 <trans-unit id="4691552465058437520" datatype="html">
3611 <source>Commented video</source> 3613 <source>Commented video</source>
@@ -3906,8 +3908,8 @@ Hilf mit PeerTube zu übersetzen!</target>
3906 <trans-unit id="4917252294930256268" datatype="html"> 3908 <trans-unit id="4917252294930256268" datatype="html">
3907 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3909 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3908 <target state="translated">Es sieht so aus, dass Ihr Server kein HTTPS verwendet. Auf Ihrem Webserver muss TLS aktiviert sein, damit Sie Servern folgen können.</target> 3910 <target state="translated">Es sieht so aus, dass Ihr Server kein HTTPS verwendet. Auf Ihrem Webserver muss TLS aktiviert sein, damit Sie Servern folgen können.</target>
3909 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 3911
3910 </trans-unit> 3912 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3911 <trans-unit id="4058814854824495833" datatype="html"> 3913 <trans-unit id="4058814854824495833" datatype="html">
3912 <source>Mute domains</source> 3914 <source>Mute domains</source>
3913 <target state="translated">Domains stummschalten</target> 3915 <target state="translated">Domains stummschalten</target>
@@ -5856,11 +5858,8 @@ color: red;
5856 <trans-unit id="5512878593724620692" datatype="html"> 5858 <trans-unit id="5512878593724620692" datatype="html">
5857 <source>CHANNELS</source> 5859 <source>CHANNELS</source>
5858 <target state="translated">KANÄLE</target> 5860 <target state="translated">KANÄLE</target>
5859 <context-group purpose="location"> 5861
5860 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5862 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5861 <context context-type="linenumber">82</context>
5862 </context-group>
5863 </trans-unit>
5864 <trans-unit id="3666829335406793239"> 5863 <trans-unit id="3666829335406793239">
5865 <source>This account does not have channels.</source> 5864 <source>This account does not have channels.</source>
5866 <target>Dieses Konto hat keine Kanäle.</target> 5865 <target>Dieses Konto hat keine Kanäle.</target>
@@ -5899,6 +5898,12 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
5899channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 5898channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
5900 <target state="translated">Wollen Sie wirklich <x id="PH" equiv-text="videoChannel.displayName"/> löschen? Es löscht <x id="PH_1" equiv-text="videoChannel.videosCount"/> Videos, die in diesem Kanal hochgeladen wurden, und Sie können keinen weiteren Kanal mit demselben Namen (<x id="PH_2" equiv-text="videoChannel.name"/>) erstellen!</target> 5899 <target state="translated">Wollen Sie wirklich <x id="PH" equiv-text="videoChannel.displayName"/> löschen? Es löscht <x id="PH_1" equiv-text="videoChannel.videosCount"/> Videos, die in diesem Kanal hochgeladen wurden, und Sie können keinen weiteren Kanal mit demselben Namen (<x id="PH_2" equiv-text="videoChannel.name"/>) erstellen!</target>
5901 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 5900 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
5901 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
5902 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5903 <context-group purpose="location">
5904 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5905 <context context-type="linenumber">48</context>
5906 </context-group>
5902 </trans-unit> 5907 </trans-unit>
5903 <trans-unit id="5387007581996837469" datatype="html"> 5908 <trans-unit id="5387007581996837469" datatype="html">
5904 <source>My Channels</source> 5909 <source>My Channels</source>
@@ -6415,12 +6420,12 @@ Erstelle mein Konto</target>
6415 <source>Your message has been sent.</source> 6420 <source>Your message has been sent.</source>
6416 <target>Deine Nachricht wurde gesendet.</target> 6421 <target>Deine Nachricht wurde gesendet.</target>
6417 6422
6418 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6423 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6419 <trans-unit id="2072135752262464360"> 6424 <trans-unit id="2072135752262464360">
6420 <source>You already sent this form recently</source> 6425 <source>You already sent this form recently</source>
6421 <target>Du hast dieses Formular bereits kürzlich gesendet</target> 6426 <target>Du hast dieses Formular bereits kürzlich gesendet</target>
6422 6427
6423 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6428 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6424 <trans-unit id="819067926858619041" datatype="html"> 6429 <trans-unit id="819067926858619041" datatype="html">
6425 <source>Account videos</source> 6430 <source>Account videos</source>
6426 <target state="translated">Videos des Kontos</target> 6431 <target state="translated">Videos des Kontos</target>
@@ -6463,13 +6468,13 @@ Erstelle mein Konto</target>
6463 <trans-unit id="4856575356061361269" datatype="html"> 6468 <trans-unit id="4856575356061361269" datatype="html">
6464 <source><x id="PH"/> direct account followers </source> 6469 <source><x id="PH"/> direct account followers </source>
6465 <target state="translated"><x id="PH"/> direkte Kontofolgende </target> 6470 <target state="translated"><x id="PH"/> direkte Kontofolgende </target>
6466 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6471
6467 </trans-unit> 6472 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6468 <trans-unit id="6250999352462648289" datatype="html"> 6473 <trans-unit id="6250999352462648289" datatype="html">
6469 <source>Report this account</source> 6474 <source>Report this account</source>
6470 <target state="translated">Dieses Konto melden</target> 6475 <target state="translated">Dieses Konto melden</target>
6471 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6476
6472 </trans-unit> 6477 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6473 <trans-unit id="1504521795586863905" datatype="html"> 6478 <trans-unit id="1504521795586863905" datatype="html">
6474 <source>VIDEOS</source> 6479 <source>VIDEOS</source>
6475 <target state="translated">VIDEOS</target> 6480 <target state="translated">VIDEOS</target>
@@ -6479,31 +6484,21 @@ Erstelle mein Konto</target>
6479 <trans-unit id="25349740244798533"> 6484 <trans-unit id="25349740244798533">
6480 <source>Username copied</source> 6485 <source>Username copied</source>
6481 <target>Benutzername kopiert</target> 6486 <target>Benutzername kopiert</target>
6482 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6487
6483 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6488
6484 </trans-unit> 6489 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6485 <trans-unit id="9221735175659318025" datatype="html"> 6490 <trans-unit id="9221735175659318025" datatype="html">
6486 <source>1 subscriber</source> 6491 <source>1 subscriber</source>
6487 <target state="translated">1 Abonnent</target> 6492 <target state="translated">1 Abonnent</target>
6488 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6493
6489 </trans-unit> 6494 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6490 <trans-unit id="4097331874769079975" datatype="html"> 6495 <trans-unit id="4097331874769079975" datatype="html">
6491 <source><x id="PH"/> subscribers</source> 6496 <source><x id="PH"/> subscribers</source>
6492 <target state="translated"><x id="PH"/> Abonnenten</target> 6497 <target state="translated"><x id="PH"/> Abonnenten</target>
6493 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6498
6494 </trans-unit> 6499 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6495 <trans-unit id="4682675125751819107" datatype="html"> 6500
6496 <source>Instances you follow</source> 6501
6497 <target state="translated">Gefolgte Instanzen</target>
6498 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6499 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6500 </trans-unit>
6501 <trans-unit id="8899833753704589712" datatype="html">
6502 <source>Instances following you</source>
6503 <target state="translated">Folgende Instanzen</target>
6504 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6505 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6506 </trans-unit>
6507 <trans-unit id="1035838766454786107" datatype="html"> 6502 <trans-unit id="1035838766454786107" datatype="html">
6508 <source>Audio-only</source> 6503 <source>Audio-only</source>
6509 <target state="translated">Nur Ton</target> 6504 <target state="translated">Nur Ton</target>
@@ -6553,6 +6548,12 @@ Erstelle mein Konto</target>
6553 <source>Auto (via ffmpeg)</source> 6548 <source>Auto (via ffmpeg)</source>
6554 <target>Automatisch (über ffmpeg)</target> 6549 <target>Automatisch (über ffmpeg)</target>
6555 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6550 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6551 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6552 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6553 <context-group purpose="location">
6554 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6555 <context context-type="linenumber">3</context>
6556 </context-group>
6556 </trans-unit> 6557 </trans-unit>
6557 <trans-unit id="931255636742351800" datatype="html"> 6558 <trans-unit id="931255636742351800" datatype="html">
6558 <source>No limit</source> 6559 <source>No limit</source>
@@ -6701,18 +6702,34 @@ Erstelle mein Konto</target>
6701 <trans-unit id="2127446333083057097" datatype="html"> 6702 <trans-unit id="2127446333083057097" datatype="html">
6702 <source>Domain is required.</source> 6703 <source>Domain is required.</source>
6703 <target state="translated">Domain wird benötigt.</target> 6704 <target state="translated">Domain wird benötigt.</target>
6704 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6705
6705 </trans-unit> 6706 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6706 <trans-unit id="6780793142903080663" datatype="html"> 6707 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6707 <source>Domains entered are invalid.</source> 6708 <context-group purpose="location">
6708 <target state="translated">Eingegebene Domains sind ungültig.</target> 6709 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6709 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6710 <context context-type="linenumber">93</context>
6710 </trans-unit> 6711 </context-group>
6711 <trans-unit id="5886492514458202177" datatype="html"> 6712 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6712 <source>Domains entered contain duplicates.</source> 6713 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6713 <target state="translated">Eingegebene Domains enthalten Wiederholungen.</target> 6714 <context-group purpose="location">
6714 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6715 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6716 <context context-type="linenumber">94</context>
6717 </context-group>
6718 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6719 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6720 <context-group purpose="location">
6721 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6722 <context context-type="linenumber">102</context>
6723 </context-group>
6724 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6725 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6726 <context-group purpose="location">
6727 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6728 <context context-type="linenumber">103</context>
6729 </context-group>
6715 </trans-unit> 6730 </trans-unit>
6731
6732
6716 <trans-unit id="240806681889331244"> 6733 <trans-unit id="240806681889331244">
6717 <source>Unlimited</source> 6734 <source>Unlimited</source>
6718 <target>Unbegrenzt</target> 6735 <target>Unbegrenzt</target>
@@ -6872,24 +6889,50 @@ Erstelle mein Konto</target>
6872 <x id="PH"/> von den Instanzfolgern entfernt 6889 <x id="PH"/> von den Instanzfolgern entfernt
6873 </target> 6890 </target>
6874 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6891 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6892 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6893 <source>Follow</source><target state="new">Follow</target>
6894 <context-group purpose="location">
6895 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6896 <context context-type="linenumber">3</context>
6897 </context-group>
6898 <context-group purpose="location">
6899 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6900 <context context-type="linenumber">37</context>
6901 </context-group>
6902 <context-group purpose="location">
6903 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6904 <context context-type="linenumber">18</context>
6905 </context-group>
6906 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6907 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6908 <context-group purpose="location">
6909 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6910 <context context-type="linenumber">11</context>
6911 </context-group>
6875 </trans-unit> 6912 </trans-unit>
6876 <trans-unit id="2740793005745065895"> 6913 <trans-unit id="2740793005745065895">
6877 <source><x id="PH"/> is not valid </source> 6914 <source><x id="PH"/> is not valid </source>
6878 <target> 6915 <target>
6879 <x id="PH"/> ist ungültig 6916 <x id="PH"/> ist ungültig
6880 </target> 6917 </target>
6881 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6918
6882 </trans-unit> 6919 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6883 <trans-unit id="2355066641781598196"> 6920 <trans-unit id="2355066641781598196">
6884 <source>Follow request(s) sent!</source> 6921 <source>Follow request(s) sent!</source>
6885 <target>Anfrage(n) zum Folgen gesendet!</target> 6922 <target>Anfrage(n) zum Folgen gesendet!</target>
6886 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6923
6924 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6925 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6926 <context-group purpose="location">
6927 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6928 <context context-type="linenumber">3</context>
6929 </context-group>
6887 </trans-unit> 6930 </trans-unit>
6888 <trans-unit id="4245720728052819482"> 6931 <trans-unit id="4245720728052819482">
6889 <source>Do you really want to unfollow <x id="PH"/>?</source> 6932 <source>Do you really want to unfollow <x id="PH"/>?</source>
6890 <target>Möchtest du <x id="PH"/> wirklich nicht mehr folgen?</target> 6933 <target>Möchtest du <x id="PH"/> wirklich nicht mehr folgen?</target>
6891 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6934
6892 </trans-unit> 6935 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6893 <trans-unit id="9160510009013134726"> 6936 <trans-unit id="9160510009013134726">
6894 <source>Unfollow</source> 6937 <source>Unfollow</source>
6895 <target>Nicht mehr folgen</target> 6938 <target>Nicht mehr folgen</target>
@@ -6898,8 +6941,8 @@ Erstelle mein Konto</target>
6898 <trans-unit id="3935234189109112926"> 6941 <trans-unit id="3935234189109112926">
6899 <source>You are not following <x id="PH"/> anymore.</source> 6942 <source>You are not following <x id="PH"/> anymore.</source>
6900 <target>Du folgst <x id="PH"/> nicht mehr.</target> 6943 <target>Du folgst <x id="PH"/> nicht mehr.</target>
6901 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6944
6902 </trans-unit> 6945 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6903 <trans-unit id="2593763089859685916"> 6946 <trans-unit id="2593763089859685916">
6904 <source>enabled</source> 6947 <source>enabled</source>
6905 <target>aktiviert</target> 6948 <target>aktiviert</target>
@@ -7371,9 +7414,9 @@ Erstelle mein Konto</target>
7371 <trans-unit id="1519954996184640001"> 7414 <trans-unit id="1519954996184640001">
7372 <source>Error</source> 7415 <source>Error</source>
7373 <target>Fehler</target> 7416 <target>Fehler</target>
7374 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7417
7375 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7418
7376 </trans-unit> 7419 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7377 <trans-unit id="5076187961693950167" datatype="html"> 7420 <trans-unit id="5076187961693950167" datatype="html">
7378 <source>Standard logs</source> 7421 <source>Standard logs</source>
7379 <target state="translated">Standard Log-Dateien</target> 7422 <target state="translated">Standard Log-Dateien</target>
@@ -7414,16 +7457,8 @@ Erstelle mein Konto</target>
7414 <target>Benutzerkennwort geändert</target> 7457 <target>Benutzerkennwort geändert</target>
7415 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7458 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7416 </trans-unit> 7459 </trans-unit>
7417 <trans-unit id="177544274549739411" datatype="html"> 7460
7418 <source>Following list</source> 7461
7419 <target state="translated">Liste aller Gefolgten</target>
7420 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7421 </trans-unit>
7422 <trans-unit id="8092429110007204784" datatype="html">
7423 <source>Followers list</source>
7424 <target state="translated">Liste aller Follower</target>
7425 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7426 </trans-unit>
7427 <trans-unit id="780323526182667308" datatype="html"> 7462 <trans-unit id="780323526182667308" datatype="html">
7428 <source>User <x id="PH"/> updated.</source> 7463 <source>User <x id="PH"/> updated.</source>
7429 <target state="translated">Benutzer <x id="PH"/> aktualisiert.</target> 7464 <target state="translated">Benutzer <x id="PH"/> aktualisiert.</target>
@@ -7459,16 +7494,8 @@ Erstelle mein Konto</target>
7459 <target state="translated">Föderation</target> 7494 <target state="translated">Föderation</target>
7460 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7495 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7461 </trans-unit> 7496 </trans-unit>
7462 <trans-unit id="4682675125751819107" datatype="html"> 7497
7463 <source>Instances you follow</source> 7498
7464 <target state="translated">Gefolgte Instanzen</target>
7465 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7466 </trans-unit>
7467 <trans-unit id="8899833753704589712" datatype="html">
7468 <source>Instances following you</source>
7469 <target state="translated">Folgende Instanzen</target>
7470 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7471 </trans-unit>
7472 <trans-unit id="3767259920053407667" datatype="html"> 7499 <trans-unit id="3767259920053407667" datatype="html">
7473 <source>Videos will be deleted, comments will be tombstoned.</source> 7500 <source>Videos will be deleted, comments will be tombstoned.</source>
7474 <target state="translated">Videos werden gelöscht, Kommentare werden mit einem Grabstein markiert.</target> 7501 <target state="translated">Videos werden gelöscht, Kommentare werden mit einem Grabstein markiert.</target>
@@ -7499,6 +7526,24 @@ Erstelle mein Konto</target>
7499 <target>E-Mail als bestätigt setzen</target> 7526 <target>E-Mail als bestätigt setzen</target>
7500 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7527 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7501 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7528 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7529 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7530 <source>Created</source><target state="new">Created</target>
7531 <context-group purpose="location">
7532 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7533 <context context-type="linenumber">115</context>
7534 </context-group>
7535 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7536 <source>Daily quota</source><target state="new">Daily quota</target>
7537 <context-group purpose="location">
7538 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7539 <context context-type="linenumber">120</context>
7540 </context-group>
7541 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7542 <source>Last login</source><target state="new">Last login</target>
7543 <context-group purpose="location">
7544 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7545 <context context-type="linenumber">122</context>
7546 </context-group>
7502 </trans-unit> 7547 </trans-unit>
7503 <trans-unit id="3403978719736970622"> 7548 <trans-unit id="3403978719736970622">
7504 <source>You cannot ban root.</source> 7549 <source>You cannot ban root.</source>
@@ -7803,13 +7848,13 @@ Erstelle mein Konto</target>
7803 <trans-unit id="1137937154872046253"> 7848 <trans-unit id="1137937154872046253">
7804 <source>Video channel <x id="PH"/> created.</source> 7849 <source>Video channel <x id="PH"/> created.</source>
7805 <target>Videokanal <x id="PH"/> erstellt.</target> 7850 <target>Videokanal <x id="PH"/> erstellt.</target>
7806 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 7851
7807 </trans-unit> 7852 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7808 <trans-unit id="8723777130353305761"> 7853 <trans-unit id="8723777130353305761">
7809 <source>This name already exists on this instance.</source> 7854 <source>This name already exists on this instance.</source>
7810 <target>Dieser Name existiert bereits auf dieser Instanz.</target> 7855 <target>Dieser Name existiert bereits auf dieser Instanz.</target>
7811 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 7856
7812 </trans-unit> 7857 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7813 <trans-unit id="7589345916094713536"> 7858 <trans-unit id="7589345916094713536">
7814 <source>Video channel <x id="PH"/> updated.</source> 7859 <source>Video channel <x id="PH"/> updated.</source>
7815 <target>Videokanal <x id="PH"/> aktualisiert.</target> 7860 <target>Videokanal <x id="PH"/> aktualisiert.</target>
@@ -7830,11 +7875,7 @@ Erstelle mein Konto</target>
7830 <target state="translated">Banner gelöscht.</target> 7875 <target state="translated">Banner gelöscht.</target>
7831 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 7876 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
7832 </trans-unit> 7877 </trans-unit>
7833 <trans-unit id="2575302837003821736"> 7878
7834 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7835 <target>Bitte gib zum Bestätigen den Anzeigenamen des Videokanals ( <x id="PH"/>) ein</target>
7836 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
7837 </trans-unit>
7838 <trans-unit id="624066830180032195"> 7879 <trans-unit id="624066830180032195">
7839 <source>Video channel <x id="PH"/> deleted.</source> 7880 <source>Video channel <x id="PH"/> deleted.</source>
7840 <target>Videokanal <x id="PH"/> entfernt.</target> 7881 <target>Videokanal <x id="PH"/> entfernt.</target>
@@ -7986,6 +8027,12 @@ Erstelle mein Konto</target>
7986 <source>Ownership change request sent.</source> 8027 <source>Ownership change request sent.</source>
7987 <target>Eine Anfrage zur Änderung des Besitzers wurde versendet.</target> 8028 <target>Eine Anfrage zur Änderung des Besitzers wurde versendet.</target>
7988 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8029 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8030 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8031 <source>Sort by</source><target state="new">Sort by</target>
8032 <context-group purpose="location">
8033 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8034 <context context-type="linenumber">26</context>
8035 </context-group>
7989 </trans-unit> 8036 </trans-unit>
7990 <trans-unit id="3245220240937722814"> 8037 <trans-unit id="3245220240937722814">
7991 <source>My channels</source> 8038 <source>My channels</source>
@@ -8084,7 +8131,7 @@ Erstelle mein Konto</target>
8084 <target>Diesen Account abonnieren</target> 8131 <target>Diesen Account abonnieren</target>
8085 8132
8086 8133
8087 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8134 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8088 <trans-unit id="3131904093925601441" datatype="html"> 8135 <trans-unit id="3131904093925601441" datatype="html">
8089 <source>PLAYLISTS</source> 8136 <source>PLAYLISTS</source>
8090 <target state="translated">PLAYLISTS</target> 8137 <target state="translated">PLAYLISTS</target>
@@ -8131,34 +8178,34 @@ Erstelle mein Konto</target>
8131 <trans-unit id="3779524668013120370"> 8178 <trans-unit id="3779524668013120370">
8132 <source>Go to my subscriptions</source> 8179 <source>Go to my subscriptions</source>
8133 <target>Gehe zu meinen Abos</target> 8180 <target>Gehe zu meinen Abos</target>
8134 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8181
8135 </trans-unit> 8182 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8136 <trans-unit id="1136469849928650779"> 8183 <trans-unit id="1136469849928650779">
8137 <source>Go to my videos</source> 8184 <source>Go to my videos</source>
8138 <target>Zu meinen Videos gehen</target> 8185 <target>Zu meinen Videos gehen</target>
8139 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8186
8140 </trans-unit> 8187 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8141 <trans-unit id="7836683738999600376"> 8188 <trans-unit id="7836683738999600376">
8142 <source>Go to my imports</source> 8189 <source>Go to my imports</source>
8143 <target>Gehe zu meinen Importen</target> 8190 <target>Gehe zu meinen Importen</target>
8144 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8191
8145 </trans-unit> 8192 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8146 <trans-unit id="7511292153332773503"> 8193 <trans-unit id="7511292153332773503">
8147 <source>Go to my channels</source> 8194 <source>Go to my channels</source>
8148 <target>Gehe zu meinen Kanälen</target> 8195 <target>Gehe zu meinen Kanälen</target>
8149 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8196
8150 </trans-unit> 8197 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8151 <trans-unit id="2013324644839511073" datatype="html"> 8198 <trans-unit id="2013324644839511073" datatype="html">
8152 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8199 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8153Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8200Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8154 <target state="translated">Referenzen des OAuth-Clients können nicht abgerufen werden: <x id="PH" equiv-text="error.text"/>. Stellen Sie sicher, dass PeerTube korrekt konfiguriert ist (Ordner config/), speziell der Abschnitt "webserver".</target> 8201 <target state="translated">Referenzen des OAuth-Clients können nicht abgerufen werden: <x id="PH" equiv-text="error.text"/>. Stellen Sie sicher, dass PeerTube korrekt konfiguriert ist (Ordner config/), speziell der Abschnitt "webserver".</target>
8155 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8202
8156 </trans-unit> 8203 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8157 <trans-unit id="375263728166936544"> 8204 <trans-unit id="375263728166936544">
8158 <source>You need to reconnect.</source> 8205 <source>You need to reconnect.</source>
8159 <target>Bitte verbinde dich erneut.</target> 8206 <target>Bitte verbinde dich erneut.</target>
8160 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8207
8161 </trans-unit> 8208 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8162 <trans-unit id="2206638022166154361"> 8209 <trans-unit id="2206638022166154361">
8163 <source>Keyboard Shortcuts:</source> 8210 <source>Keyboard Shortcuts:</source>
8164 <target>Tastaturkürzel:</target> 8211 <target>Tastaturkürzel:</target>
@@ -8171,6 +8218,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8171 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8218 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8172 <context context-type="linenumber">98</context> 8219 <context context-type="linenumber">98</context>
8173 </context-group> 8220 </context-group>
8221 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8222 <source>In my library</source><target state="new">In my library</target>
8223 <context-group purpose="location">
8224 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8225 <context context-type="linenumber">104</context>
8226 </context-group>
8174 </trans-unit> 8227 </trans-unit>
8175 <trans-unit id="232050922346936574" datatype="html"> 8228 <trans-unit id="232050922346936574" datatype="html">
8176 <source>Trending</source> 8229 <source>Trending</source>
@@ -8199,38 +8252,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8199 <trans-unit id="1266887509445371246"> 8252 <trans-unit id="1266887509445371246">
8200 <source>Incorrect username or password.</source> 8253 <source>Incorrect username or password.</source>
8201 <target>Falscher Benutzername oder falsches Passwort.</target> 8254 <target>Falscher Benutzername oder falsches Passwort.</target>
8202 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8255
8203 </trans-unit> 8256 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8204 <trans-unit id="6974874606619467663" datatype="html"> 8257 <trans-unit id="6974874606619467663" datatype="html">
8205 <source>Your account is blocked.</source> 8258 <source>Your account is blocked.</source>
8206 <target state="translated">Ihr Konto wurde gesperrt.</target> 8259 <target state="translated">Ihr Konto wurde gesperrt.</target>
8207 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8260
8208 </trans-unit> 8261 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8209 <trans-unit id="7939914198003891823" datatype="html"> 8262 <trans-unit id="7939914198003891823" datatype="html">
8210 <source>any language</source> 8263 <source>any language</source>
8211 <target state="translated">jede Sprache</target> 8264 <target state="translated">jede Sprache</target>
8212 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8265
8213 </trans-unit> 8266 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8214 <trans-unit id="5633144232269377096" datatype="html"> 8267 <trans-unit id="5633144232269377096" datatype="html">
8215 <source>hide</source> 8268 <source>hide</source>
8216 <target state="translated">verstecken</target> 8269 <target state="translated">verstecken</target>
8217 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8270
8218 </trans-unit> 8271 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8219 <trans-unit id="8603861867909474404" datatype="html"> 8272 <trans-unit id="8603861867909474404" datatype="html">
8220 <source>blur</source> 8273 <source>blur</source>
8221 <target state="translated">verwischen</target> 8274 <target state="translated">verwischen</target>
8222 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8275
8223 </trans-unit> 8276 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8224 <trans-unit id="4534458451100881847" datatype="html"> 8277 <trans-unit id="4534458451100881847" datatype="html">
8225 <source>display</source> 8278 <source>display</source>
8226 <target state="translated">anzeigen</target> 8279 <target state="translated">anzeigen</target>
8227 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8280
8228 </trans-unit> 8281 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8229 <trans-unit id="4467323362722952678" datatype="html"> 8282 <trans-unit id="4467323362722952678" datatype="html">
8230 <source>Unknown</source> 8283 <source>Unknown</source>
8231 <target state="translated">Unbekannt</target> 8284 <target state="translated">Unbekannt</target>
8232 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8285
8233 </trans-unit> 8286 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8234 <trans-unit id="8781423666414310853"> 8287 <trans-unit id="8781423666414310853">
8235 <source>Your password has been successfully reset!</source> 8288 <source>Your password has been successfully reset!</source>
8236 <target>Dein Passwort wurde zurückgesetzt!</target> 8289 <target>Dein Passwort wurde zurückgesetzt!</target>
@@ -9810,18 +9863,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9810 <trans-unit id="968295009933361070"> 9863 <trans-unit id="968295009933361070">
9811 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9864 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9812 <target>Zu viele Versuche in kurzer Zeit. Bitte versuche es in <x id="PH"/> Minuten nochmal.</target> 9865 <target>Zu viele Versuche in kurzer Zeit. Bitte versuche es in <x id="PH"/> Minuten nochmal.</target>
9813 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9866
9814 </trans-unit> 9867 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9815 <trans-unit id="4965472196059235310"> 9868 <trans-unit id="4965472196059235310">
9816 <source>Too many attempts, please try again later.</source> 9869 <source>Too many attempts, please try again later.</source>
9817 <target>Zu viele Versuche in kurzer Zeit. Bitte versuche es später nochmal.</target> 9870 <target>Zu viele Versuche in kurzer Zeit. Bitte versuche es später nochmal.</target>
9818 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9871
9819 </trans-unit> 9872 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9820 <trans-unit id="1693549688987384699"> 9873 <trans-unit id="1693549688987384699">
9821 <source>Server error. Please retry later.</source> 9874 <source>Server error. Please retry later.</source>
9822 <target>Server-Fehler. Bitte später erneut versuchen.</target> 9875 <target>Server-Fehler. Bitte später erneut versuchen.</target>
9823 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9876
9824 </trans-unit> 9877 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9825 <trans-unit id="5927402622550505067" datatype="html"> 9878 <trans-unit id="5927402622550505067" datatype="html">
9826 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9879 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9827 <target state="translated">Alle Kanäle von <x id="PH"/> abonniert. Du wirst über neue Videos darin benachrichtigt.</target> 9880 <target state="translated">Alle Kanäle von <x id="PH"/> abonniert. Du wirst über neue Videos darin benachrichtigt.</target>
@@ -10410,35 +10463,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10410 <trans-unit id="3284171506518522275"> 10463 <trans-unit id="3284171506518522275">
10411 <source>Your video was uploaded to your account and is private.</source> 10464 <source>Your video was uploaded to your account and is private.</source>
10412 <target>Das Video wurde in dein Konto hochgeladen und ist privat.</target> 10465 <target>Das Video wurde in dein Konto hochgeladen und ist privat.</target>
10413 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10466
10414 </trans-unit> 10467 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10415 <trans-unit id="5699822024600815733"> 10468 <trans-unit id="5699822024600815733">
10416 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10469 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10417 <target>Weitere Infos (Tags, Beschreibung, ...) werden verworfen, wenn du diese Seite verlässt. Bist du dir sicher?</target> 10470 <target>Weitere Infos (Tags, Beschreibung, ...) werden verworfen, wenn du diese Seite verlässt. Bist du dir sicher?</target>
10418 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10471
10419 </trans-unit> 10472 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10420 <trans-unit id="1219739004043110649"> 10473 <trans-unit id="1219739004043110649">
10421 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10474 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10422 <target>Dein Video ist noch nicht hochgeladen. Willst du diese Seite wirklich verlassen?</target> 10475 <target>Dein Video ist noch nicht hochgeladen. Willst du diese Seite wirklich verlassen?</target>
10423 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10476
10424 </trans-unit> 10477 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10425 <trans-unit id="6932865105766151309" datatype="html"> 10478 <trans-unit id="6932865105766151309" datatype="html">
10426 <source>Upload</source> 10479 <source>Upload</source>
10427 <target state="translated">Hochladen</target> 10480 <target state="translated">Hochladen</target>
10428 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10481
10429 </trans-unit> 10482 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10430 <trans-unit id="8278735427925094503"> 10483 <trans-unit id="8278735427925094503">
10431 <source>Upload <x id="PH"/> </source> 10484 <source>Upload <x id="PH"/> </source>
10432 <target> 10485 <target>
10433 <x id="PH"/> hochladen 10486 <x id="PH"/> hochladen
10434 </target> 10487 </target>
10435 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10488
10436 </trans-unit> 10489 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10437 <trans-unit id="5981816353437801748"> 10490 <trans-unit id="5981816353437801748">
10438 <source>Video published.</source> 10491 <source>Video published.</source>
10439 <target>Video veröffentlicht.</target> 10492 <target>Video veröffentlicht.</target>
10440 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10493
10441 </trans-unit> 10494 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10442 <trans-unit id="764164089183618119"> 10495 <trans-unit id="764164089183618119">
10443 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10496 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10444 <target>Es gibt ungespeicherte Änderungen! Wenn du die Seite verlässt, gehen die Änderungen verloren.</target> 10497 <target>Es gibt ungespeicherte Änderungen! Wenn du die Seite verlässt, gehen die Änderungen verloren.</target>
@@ -10486,27 +10539,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10486 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10539 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10487 <target state="translated">Dieses Video ist auf dieser Instanz nicht verfügbar. Wollen Sie auf die Quellinstanz weitergeleitet werden: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10540 <target state="translated">Dieses Video ist auf dieser Instanz nicht verfügbar. Wollen Sie auf die Quellinstanz weitergeleitet werden: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10488 10541
10489 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10542 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10490 <trans-unit id="5761611056224181752" datatype="html"> 10543 <trans-unit id="5761611056224181752" datatype="html">
10491 <source>Redirection</source> 10544 <source>Redirection</source>
10492 <target state="translated">Weiterleitung</target> 10545 <target state="translated">Weiterleitung</target>
10493 10546
10494 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10547 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10495 <trans-unit id="8858527736400081688"> 10548 <trans-unit id="8858527736400081688">
10496 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10549 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10497 <target>Dieses Video enthält Inhalte, die möglicherweise für bestimmte Zuschauer ungeeignet sind oder von diesen als anstößig empfunden werden. Möchtest du es wirklich ansehen?</target> 10550 <target>Dieses Video enthält Inhalte, die möglicherweise für bestimmte Zuschauer ungeeignet sind oder von diesen als anstößig empfunden werden. Möchtest du es wirklich ansehen?</target>
10498 10551
10499 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10552 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10500 <trans-unit id="3937119019020041049"> 10553 <trans-unit id="3937119019020041049">
10501 <source>Mature or explicit content</source> 10554 <source>Mature or explicit content</source>
10502 <target>Inhalt, der möglicherweise für bestimmte Zuschauer ungeeignet oder anstößig ist</target> 10555 <target>Inhalt, der möglicherweise für bestimmte Zuschauer ungeeignet oder anstößig ist</target>
10503 10556
10504 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10557 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10505 <trans-unit id="1755474755114288376" datatype="html"> 10558 <trans-unit id="1755474755114288376" datatype="html">
10506 <source>Up Next</source> 10559 <source>Up Next</source>
10507 <target state="translated">Nächstes</target> 10560 <target state="translated">Nächstes</target>
10508 10561
10509 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10562 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10510 <trans-unit id="2159130950882492111" datatype="html"> 10563 <trans-unit id="2159130950882492111" datatype="html">
10511 <source>Cancel</source> 10564 <source>Cancel</source>
10512 <target state="translated">Abbrechen</target> 10565 <target state="translated">Abbrechen</target>
@@ -10516,62 +10569,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10516 <source>Autoplay is suspended</source> 10569 <source>Autoplay is suspended</source>
10517 <target state="translated">Autoplay ist unterbrochen</target> 10570 <target state="translated">Autoplay ist unterbrochen</target>
10518 10571
10519 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10572 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10520 <trans-unit id="7895294730547405228" datatype="html"> 10573 <trans-unit id="7895294730547405228" datatype="html">
10521 <source>Enter/exit fullscreen (requires player focus)</source> 10574 <source>Enter/exit fullscreen (requires player focus)</source>
10522 <target state="translated">Vollbildmodus betreten/verlassen (Player benötigt Fokus)</target> 10575 <target state="translated">Vollbildmodus betreten/verlassen (Player benötigt Fokus)</target>
10523 10576
10524 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10577 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10525 <trans-unit id="7618388257165864759" datatype="html"> 10578 <trans-unit id="7618388257165864759" datatype="html">
10526 <source>Play/Pause the video (requires player focus)</source> 10579 <source>Play/Pause the video (requires player focus)</source>
10527 <target state="translated">Video abspielen/pausieren (Player benötigt Fokus)</target> 10580 <target state="translated">Video abspielen/pausieren (Player benötigt Fokus)</target>
10528 10581
10529 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10582 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10530 <trans-unit id="7761890399634216630" datatype="html"> 10583 <trans-unit id="7761890399634216630" datatype="html">
10531 <source>Mute/unmute the video (requires player focus)</source> 10584 <source>Mute/unmute the video (requires player focus)</source>
10532 <target state="translated">Video stummschalten/Stummschaltung aufheben (Player benötigt Fokus)</target> 10585 <target state="translated">Video stummschalten/Stummschaltung aufheben (Player benötigt Fokus)</target>
10533 10586
10534 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10587 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10535 <trans-unit id="5996585232248234904" datatype="html"> 10588 <trans-unit id="5996585232248234904" datatype="html">
10536 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10589 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10537 <target state="translated">Springe zu einem Prozentsatz des Videos: 0 entspricht 0%, 9 entspricht 90% (Player benötigt Fokus)</target> 10590 <target state="translated">Springe zu einem Prozentsatz des Videos: 0 entspricht 0%, 9 entspricht 90% (Player benötigt Fokus)</target>
10538 10591
10539 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10592 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10540 <trans-unit id="3748765405903319998" datatype="html"> 10593 <trans-unit id="3748765405903319998" datatype="html">
10541 <source>Increase the volume (requires player focus)</source> 10594 <source>Increase the volume (requires player focus)</source>
10542 <target state="translated">Lautstärke erhöhen (Player benötigt Fokus)</target> 10595 <target state="translated">Lautstärke erhöhen (Player benötigt Fokus)</target>
10543 10596
10544 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10597 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10545 <trans-unit id="5810704036407159982" datatype="html"> 10598 <trans-unit id="5810704036407159982" datatype="html">
10546 <source>Decrease the volume (requires player focus)</source> 10599 <source>Decrease the volume (requires player focus)</source>
10547 <target state="translated">Lautstärke verringern (Player benötigt Fokus)</target> 10600 <target state="translated">Lautstärke verringern (Player benötigt Fokus)</target>
10548 10601
10549 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10602 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10550 <trans-unit id="2622048822548065691" datatype="html"> 10603 <trans-unit id="2622048822548065691" datatype="html">
10551 <source>Seek the video forward (requires player focus)</source> 10604 <source>Seek the video forward (requires player focus)</source>
10552 <target state="translated">Vorspulen (Player benötigt Fokus)</target> 10605 <target state="translated">Vorspulen (Player benötigt Fokus)</target>
10553 10606
10554 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10607 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10555 <trans-unit id="6540078205109221153" datatype="html"> 10608 <trans-unit id="6540078205109221153" datatype="html">
10556 <source>Seek the video backward (requires player focus)</source> 10609 <source>Seek the video backward (requires player focus)</source>
10557 <target state="translated">Zurückspulen (Player benötigt Fokus)</target> 10610 <target state="translated">Zurückspulen (Player benötigt Fokus)</target>
10558 10611
10559 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10612 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10560 <trans-unit id="1956491957766210808" datatype="html"> 10613 <trans-unit id="1956491957766210808" datatype="html">
10561 <source>Increase playback rate (requires player focus)</source> 10614 <source>Increase playback rate (requires player focus)</source>
10562 <target state="translated">Abspielgeschwindigkeit erhöhen (Player benötigt Fokus)</target> 10615 <target state="translated">Abspielgeschwindigkeit erhöhen (Player benötigt Fokus)</target>
10563 10616
10564 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10617 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10565 <trans-unit id="5495529997674803186" datatype="html"> 10618 <trans-unit id="5495529997674803186" datatype="html">
10566 <source>Decrease playback rate (requires player focus)</source> 10619 <source>Decrease playback rate (requires player focus)</source>
10567 <target state="translated">Abspielgeschwindigkeit verringern (Player benötigt Fokus)</target> 10620 <target state="translated">Abspielgeschwindigkeit verringern (Player benötigt Fokus)</target>
10568 10621
10569 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10622 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10570 <trans-unit id="3178343147230721210" datatype="html"> 10623 <trans-unit id="3178343147230721210" datatype="html">
10571 <source>Navigate in the video frame by frame (requires player focus)</source> 10624 <source>Navigate in the video frame by frame (requires player focus)</source>
10572 <target state="translated">Einen Frame weitergehen (Player benötigt Fokus)</target> 10625 <target state="translated">Einen Frame weitergehen (Player benötigt Fokus)</target>
10573 10626
10574 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10627 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10575 <trans-unit id="8025996572234182184"> 10628 <trans-unit id="8025996572234182184">
10576 <source>Like the video</source> 10629 <source>Like the video</source>
10577 <target>Das Video gefällt mir</target> 10630 <target>Das Video gefällt mir</target>
diff --git a/client/src/locale/angular.el-GR.xlf b/client/src/locale/angular.el-GR.xlf
index a3b37b853..a3fb28d38 100644
--- a/client/src/locale/angular.el-GR.xlf
+++ b/client/src/locale/angular.el-GR.xlf
@@ -163,7 +163,7 @@
163 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source> 163 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source>
164 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target> 164 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target>
165 165
166 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 166 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
167 <trans-unit id="1486537403020619891" datatype="html"> 167 <trans-unit id="1486537403020619891" datatype="html">
168 <source>My watch history</source> 168 <source>My watch history</source>
169 <target state="translated">Το ιστορικό μου</target> 169 <target state="translated">Το ιστορικό μου</target>
@@ -238,12 +238,12 @@
238 238
239 239
240 240
241 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 241 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
242 <trans-unit id="1006562256968398209" datatype="html"> 242 <trans-unit id="1006562256968398209" datatype="html">
243 <source>video</source> 243 <source>video</source>
244 <target state="translated">βίντεο</target> 244 <target state="translated">βίντεο</target>
245 245
246 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 246 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
247 <trans-unit id="6438815964972582865" datatype="html"> 247 <trans-unit id="6438815964972582865" datatype="html">
248 <source>The following link contains a private token and should not be shared with anyone.</source> 248 <source>The following link contains a private token and should not be shared with anyone.</source>
249 <target state="translated">Ο σύνδεσμος που ακολουθεί περιέχει απόρρητη συμβολοσειρά και δεν θα πρέπει να τον μοιραστείτε με τρίτους.</target> 249 <target state="translated">Ο σύνδεσμος που ακολουθεί περιέχει απόρρητη συμβολοσειρά και δεν θα πρέπει να τον μοιραστείτε με τρίτους.</target>
@@ -312,10 +312,10 @@
312 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 312 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
313 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 313 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
314 314
315 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 315 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
316 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 316 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
317 317
318 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 318 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
319 <trans-unit id="5235042777215655908" datatype="html"> 319 <trans-unit id="5235042777215655908" datatype="html">
320 <source>subtitles</source> 320 <source>subtitles</source>
321 <target state="translated">υπότιτλοι</target> 321 <target state="translated">υπότιτλοι</target>
@@ -724,10 +724,10 @@
724 <trans-unit id="2602586221576511475"> 724 <trans-unit id="2602586221576511475">
725 <source>Video quota</source> 725 <source>Video quota</source>
726 <target>Όριο βίντεο</target> 726 <target>Όριο βίντεο</target>
727 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 727
728 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 728
729 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 729
730 </trans-unit> 730 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
731 <trans-unit id="1502595455339510144"> 731 <trans-unit id="1502595455339510144">
732 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 732 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
733 <target> 733 <target>
@@ -811,7 +811,31 @@
811 <source>Federation</source> 811 <source>Federation</source>
812 <target state="new">Federation</target> 812 <target state="new">Federation</target>
813 813
814 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 814 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
815 <source>Following</source><target state="new">Following</target>
816 <context-group purpose="location">
817 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
818 <context context-type="linenumber">29</context>
819 </context-group>
820 <context-group purpose="location">
821 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
822 <context context-type="linenumber">31</context>
823 </context-group>
824 <context-group purpose="location">
825 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
826 <context context-type="linenumber">28</context>
827 </context-group>
828 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
829 <source>Followers</source><target state="new">Followers</target>
830 <context-group purpose="location">
831 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
832 <context context-type="linenumber">34</context>
833 </context-group>
834 <context-group purpose="location">
835 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
836 <context context-type="linenumber">37</context>
837 </context-group>
838 </trans-unit>
815 <trans-unit id="3541687134897970106" datatype="html"> 839 <trans-unit id="3541687134897970106" datatype="html">
816 <source>followers</source> 840 <source>followers</source>
817 <target state="translated">ακόλουθοι</target> 841 <target state="translated">ακόλουθοι</target>
@@ -885,7 +909,7 @@
885 909
886 910
887 911
888 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 912 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
889 <trans-unit id="3616223838716839702"> 913 <trans-unit id="3616223838716839702">
890 <source>Ban this user</source> 914 <source>Ban this user</source>
891 <target>Αποκλεισμός χρήστη</target> 915 <target>Αποκλεισμός χρήστη</target>
@@ -958,19 +982,13 @@
958 <trans-unit id="7252854992688790751" datatype="html"> 982 <trans-unit id="7252854992688790751" datatype="html">
959 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 983 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
960 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 984 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
961 <context-group purpose="location"> 985
962 <context context-type="sourcefile">src/app/+login/login.component.html</context> 986 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
963 <context context-type="linenumber">60,62</context>
964 </context-group>
965 </trans-unit>
966 <trans-unit id="7215649348148521605" datatype="html"> 987 <trans-unit id="7215649348148521605" datatype="html">
967 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 988 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
968 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 989 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
969 <context-group purpose="location"> 990
970 <context context-type="sourcefile">src/app/+login/login.component.html</context> 991 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
971 <context context-type="linenumber">65,67</context>
972 </context-group>
973 </trans-unit>
974 <trans-unit id="2392488717875840729"> 992 <trans-unit id="2392488717875840729">
975 <source>User</source> 993 <source>User</source>
976 <target>Χρήστης</target> 994 <target>Χρήστης</target>
@@ -981,68 +999,68 @@
981 <source>Username or email address</source> 999 <source>Username or email address</source>
982 <target>Όνομα χρήστη ή διεύθυνση e-mail</target> 1000 <target>Όνομα χρήστη ή διεύθυνση e-mail</target>
983 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1001 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1002 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1003 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1004 <context-group purpose="location">
1005 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1006 <context context-type="linenumber">33,34</context>
1007 </context-group>
984 </trans-unit> 1008 </trans-unit>
985 <trans-unit id="1431416938026210429"> 1009 <trans-unit id="1431416938026210429">
986 <source>Password</source> 1010 <source>Password</source>
987 <target>Κωδικός σύνδεσης</target> 1011 <target>Κωδικός σύνδεσης</target>
988 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1012
989 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1013
990 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1014
991 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1015
992 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1016
993 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1017
994 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1018
995 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1019
996 </trans-unit> 1020 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
997 <trans-unit id="8715156686857791956" datatype="html"> 1021 <trans-unit id="8715156686857791956" datatype="html">
998 <source>Click here to reset your password</source> 1022 <source>Click here to reset your password</source>
999 <target state="new">Click here to reset your password</target> 1023 <target state="new">Click here to reset your password</target>
1000 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1024
1001 </trans-unit> 1025 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1002 <trans-unit id="892063502898494584" datatype="html"> 1026 <trans-unit id="892063502898494584" datatype="html">
1003 <source>I forgot my password</source> 1027 <source>I forgot my password</source>
1004 <target state="translated">Ξέχασα τον κωδικό μου</target> 1028 <target state="translated">Ξέχασα τον κωδικό μου</target>
1005 <context-group purpose="location"> 1029
1006 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1030 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1007 <context context-type="linenumber">47</context>
1008 </context-group>
1009 </trans-unit>
1010 <trans-unit id="2101170466365500913" datatype="html"> 1031 <trans-unit id="2101170466365500913" datatype="html">
1011 <source>Logging into an account lets you publish content</source> 1032 <source>Logging into an account lets you publish content</source>
1012 <target state="new"> Logging into an account lets you publish content </target> 1033 <target state="new"> Logging into an account lets you publish content </target>
1013 <context-group purpose="location"> 1034
1014 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1035 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1015 <context context-type="linenumber">56,57</context>
1016 </context-group>
1017 </trans-unit>
1018 <trans-unit id="2454050363478003966"> 1036 <trans-unit id="2454050363478003966">
1019 <source>Login</source> 1037 <source>Login</source>
1020 <target>Σύνδεση</target> 1038 <target>Σύνδεση</target>
1021 1039
1022 1040
1023 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1041 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1024 <trans-unit id="3183213940445113677" datatype="html"> 1042 <trans-unit id="3183213940445113677" datatype="html">
1025 <source>Or sign in with</source> 1043 <source>Or sign in with</source>
1026 <target state="translated">Ή συνδεθείτε με:</target> 1044 <target state="translated">Ή συνδεθείτε με:</target>
1027 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1045
1028 </trans-unit> 1046 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1029 <trans-unit id="3238209155172574367"> 1047 <trans-unit id="3238209155172574367">
1030 <source>Forgot your password</source> 1048 <source>Forgot your password</source>
1031 <target>Ξεχάσατε τον κωδικό σύνδεσης</target> 1049 <target>Ξεχάσατε τον κωδικό σύνδεσης</target>
1032 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1050
1033 </trans-unit> 1051 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1034 <trans-unit id="87327320394367488" datatype="html"> 1052 <trans-unit id="87327320394367488" datatype="html">
1035 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1053 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1036 <target state="new"> 1054 <target state="new">
1037 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1055 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
1038 </target> 1056 </target>
1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1057
1040 </trans-unit> 1058 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1041 <trans-unit id="3188014010833256853" datatype="html"> 1059 <trans-unit id="3188014010833256853" datatype="html">
1042 <source>Enter your email address and we will send you a link to reset your password.</source> 1060 <source>Enter your email address and we will send you a link to reset your password.</source>
1043 <target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1061 <target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1044 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1062
1045 </trans-unit> 1063 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1046 <trans-unit id="1190256911880544559" datatype="html"> 1064 <trans-unit id="1190256911880544559" datatype="html">
1047 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1065 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1048The link will expire within 1 hour.</source> 1066The link will expire within 1 hour.</source>
@@ -1053,26 +1071,26 @@ The link will expire within 1 hour.</target>
1053 <trans-unit id="4768749765465246664"> 1071 <trans-unit id="4768749765465246664">
1054 <source>Email</source> 1072 <source>Email</source>
1055 <target>E-mail</target> 1073 <target>E-mail</target>
1056 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1074
1057 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1075
1058 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1076
1059 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1077
1060 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1078
1061 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1079
1062 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1080
1063 </trans-unit> 1081 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1064 <trans-unit id="3967269098753656610"> 1082 <trans-unit id="3967269098753656610">
1065 <source>Email address</source> 1083 <source>Email address</source>
1066 <target>Διεύθυνση e-mail</target> 1084 <target>Διεύθυνση e-mail</target>
1067 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1085
1068 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1086
1069 </trans-unit> 1087 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1070 <trans-unit id="7808756054397155068" datatype="html"> 1088 <trans-unit id="7808756054397155068" datatype="html">
1071 <source>Reset</source> 1089 <source>Reset</source>
1072 <target state="translated">Επαναφορά</target> 1090 <target state="translated">Επαναφορά</target>
1073 <note priority="1" from="description">Password reset button</note> 1091 <note priority="1" from="description">Password reset button</note>
1074 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1092
1075 </trans-unit> 1093 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1076 <trans-unit id="4319634264526091601" datatype="html"> 1094 <trans-unit id="4319634264526091601" datatype="html">
1077 <source>on this instance</source> 1095 <source>on this instance</source>
1078 <target state="new">on this instance</target> 1096 <target state="new">on this instance</target>
@@ -1440,7 +1458,7 @@ The link will expire within 1 hour.</target>
1440 <target>Δημιουργία λογαριασμού</target> 1458 <target>Δημιουργία λογαριασμού</target>
1441 1459
1442 1460
1443 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1461 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1444 1462
1445 <trans-unit id="3058024914967508975" datatype="html"> 1463 <trans-unit id="3058024914967508975" datatype="html">
1446 <source>My videos</source> 1464 <source>My videos</source>
@@ -1497,7 +1515,7 @@ The link will expire within 1 hour.</target>
1497 <source>VIDEOS</source> 1515 <source>VIDEOS</source>
1498 <target state="translated">ΒΙΝΤΕΟ</target> 1516 <target state="translated">ΒΙΝΤΕΟ</target>
1499 1517
1500 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 1518 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1501 <trans-unit id="667372110624203230" datatype="html"> 1519 <trans-unit id="667372110624203230" datatype="html">
1502 <source>Import jobs concurrency</source> 1520 <source>Import jobs concurrency</source>
1503 <target state="new">Import jobs concurrency</target> 1521 <target state="new">Import jobs concurrency</target>
@@ -1576,7 +1594,7 @@ The link will expire within 1 hour.</target>
1576 <source>I'm a teapot</source> 1594 <source>I'm a teapot</source>
1577 <target state="new">I'm a teapot</target> 1595 <target state="new">I'm a teapot</target>
1578 1596
1579 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit> 1597 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1580 <trans-unit id="1597262876035959248" datatype="html"> 1598 <trans-unit id="1597262876035959248" datatype="html">
1581 <source>That's an error.</source> 1599 <source>That's an error.</source>
1582 <target state="translated">Αυτό είναι ένα σφάλμα.</target> 1600 <target state="translated">Αυτό είναι ένα σφάλμα.</target>
@@ -1669,8 +1687,8 @@ The link will expire within 1 hour.</target>
1669 <trans-unit id="2971365540217107489" datatype="html"> 1687 <trans-unit id="2971365540217107489" datatype="html">
1670 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1688 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1671 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1689 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1672 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1690
1673 </trans-unit> 1691 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1674 1692
1675 <trans-unit id="5131854469652959713" datatype="html"> 1693 <trans-unit id="5131854469652959713" datatype="html">
1676 <source>GLOBAL SEARCH</source> 1694 <source>GLOBAL SEARCH</source>
@@ -2404,7 +2422,7 @@ The link will expire within 1 hour.</target>
2404 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2422 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2405 <source>Upload on hold</source><target state="new">Upload on hold</target> 2423 <source>Upload on hold</source><target state="new">Upload on hold</target>
2406 2424
2407 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2425 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2408 <trans-unit id="285180972645018518" datatype="html"> 2426 <trans-unit id="285180972645018518" datatype="html">
2409 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2427 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2410 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2428 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3150,11 +3168,7 @@ The link will expire within 1 hour.</target>
3150 <target>ID</target> 3168 <target>ID</target>
3151 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3169 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3152 </trans-unit> 3170 </trans-unit>
3153 <trans-unit id="2265605798180116441"> 3171
3154 <source>Follower handle</source>
3155 <target>Ψευδώνυμο (handle) ακολούθου</target>
3156
3157 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
3158 <trans-unit id="5911214550882917183"> 3172 <trans-unit id="5911214550882917183">
3159 <source>State</source> 3173 <source>State</source>
3160 <target>Κατάσταση</target> 3174 <target>Κατάσταση</target>
@@ -3229,11 +3243,7 @@ The link will expire within 1 hour.</target>
3229 </target> 3243 </target>
3230 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3244 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3231 </trans-unit> 3245 </trans-unit>
3232 <trans-unit id="6641024648411549335"> 3246
3233 <source>Host</source>
3234 <target>Κόμβος</target>
3235
3236 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3237 <trans-unit id="6571718060636962350" datatype="html"> 3247 <trans-unit id="6571718060636962350" datatype="html">
3238 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3248 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3239 <target state="new">Redundancy allowed 3249 <target state="new">Redundancy allowed
@@ -3246,7 +3256,7 @@ The link will expire within 1 hour.</target>
3246 <source>Unfollow</source> 3256 <source>Unfollow</source>
3247 <target state="translated">Αφαίρεση ακολούθησης</target> 3257 <target state="translated">Αφαίρεση ακολούθησης</target>
3248 3258
3249 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3259 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3250 <trans-unit id="8246779176913476983" datatype="html"> 3260 <trans-unit id="8246779176913476983" datatype="html">
3251 <source>Open instance in a new tab</source> 3261 <source>Open instance in a new tab</source>
3252 <target state="new">Open instance in a new tab</target> 3262 <target state="new">Open instance in a new tab</target>
@@ -3258,12 +3268,12 @@ The link will expire within 1 hour.</target>
3258 <source>No host found matching current filters.</source> 3268 <source>No host found matching current filters.</source>
3259 <target state="new">No host found matching current filters.</target> 3269 <target state="new">No host found matching current filters.</target>
3260 3270
3261 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3271 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3262 <trans-unit id="7274241885665071790" datatype="html"> 3272 <trans-unit id="7274241885665071790" datatype="html">
3263 <source>Your instance is not following anyone.</source> 3273 <source>Your instance is not following anyone.</source>
3264 <target state="new">Your instance is not following anyone.</target> 3274 <target state="new">Your instance is not following anyone.</target>
3265 3275
3266 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3276 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3267 <trans-unit id="4774348799569692380" datatype="html"> 3277 <trans-unit id="4774348799569692380" datatype="html">
3268 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3278 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3269 <target state="new">Showing 3279 <target state="new">Showing
@@ -3273,16 +3283,8 @@ The link will expire within 1 hour.</target>
3273 </target> 3283 </target>
3274 3284
3275 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3285 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3276 <trans-unit id="6275803119759621687" datatype="html"> 3286
3277 <source>Follow domains</source> 3287
3278 <target state="new">Follow domains</target>
3279
3280 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit>
3281 <trans-unit id="1268699198448750610" datatype="html">
3282 <source>Follow instances</source>
3283 <target state="new">Follow instances</target>
3284
3285 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit>
3286 <trans-unit id="9216117865911519658" datatype="html"> 3288 <trans-unit id="9216117865911519658" datatype="html">
3287 <source>Action</source> 3289 <source>Action</source>
3288 <target state="new">Action</target> 3290 <target state="new">Action</target>
@@ -3331,11 +3333,11 @@ The link will expire within 1 hour.</target>
3331 <trans-unit id="5248717555542428023"> 3333 <trans-unit id="5248717555542428023">
3332 <source>Username</source> 3334 <source>Username</source>
3333 <target>Όνομα χρήστη</target> 3335 <target>Όνομα χρήστη</target>
3334 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3336
3335 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3337
3336 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3338
3337 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3339
3338 </trans-unit> 3340 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3339 <trans-unit id="5428411040014095392" datatype="html"> 3341 <trans-unit id="5428411040014095392" datatype="html">
3340 <source>e.g. jane_doe</source> 3342 <source>e.g. jane_doe</source>
3341 <target state="new">e.g. jane_doe</target> 3343 <target state="new">e.g. jane_doe</target>
@@ -3365,9 +3367,9 @@ The link will expire within 1 hour.</target>
3365 <trans-unit id="4145496584631696119"> 3367 <trans-unit id="4145496584631696119">
3366 <source>Role</source> 3368 <source>Role</source>
3367 <target>Ρόλος</target> 3369 <target>Ρόλος</target>
3368 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3370
3369 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3371
3370 </trans-unit> 3372 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3371 <trans-unit id="7046347992315328430" datatype="html"> 3373 <trans-unit id="7046347992315328430" datatype="html">
3372 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3374 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3373 <target state="new"> 3375 <target state="new">
@@ -3392,15 +3394,9 @@ The link will expire within 1 hour.</target>
3392 <trans-unit id="2622255144026150901" datatype="html"> 3394 <trans-unit id="2622255144026150901" datatype="html">
3393 <source>Auth plugin</source> 3395 <source>Auth plugin</source>
3394 <target state="new">Auth plugin</target> 3396 <target state="new">Auth plugin</target>
3395 <context-group purpose="location"> 3397
3396 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3398
3397 <context context-type="linenumber">188</context> 3399 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3398 </context-group>
3399 <context-group purpose="location">
3400 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3401 <context context-type="linenumber">188</context>
3402 </context-group>
3403 </trans-unit>
3404 <trans-unit id="588099657508661970" datatype="html"> 3400 <trans-unit id="588099657508661970" datatype="html">
3405 <source>None (local authentication)</source> 3401 <source>None (local authentication)</source>
3406 <target state="new">None (local authentication)</target> 3402 <target state="new">None (local authentication)</target>
@@ -3660,7 +3656,13 @@ The link will expire within 1 hour.</target>
3660 3656
3661 3657
3662 3658
3663 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit> 3659 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3660 <source>Follower</source><target state="new">Follower</target>
3661 <context-group purpose="location">
3662 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3663 <context context-type="linenumber">24</context>
3664 </context-group>
3665 </trans-unit>
3664 <trans-unit id="4691552465058437520" datatype="html"> 3666 <trans-unit id="4691552465058437520" datatype="html">
3665 <source>Commented video</source> 3667 <source>Commented video</source>
3666 <target state="new">Commented video</target> 3668 <target state="new">Commented video</target>
@@ -3987,7 +3989,7 @@ The link will expire within 1 hour.</target>
3987 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 3989 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
3988 </target> 3990 </target>
3989 3991
3990 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3992 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3991 <trans-unit id="4058814854824495833" datatype="html"> 3993 <trans-unit id="4058814854824495833" datatype="html">
3992 <source>Mute domains</source> 3994 <source>Mute domains</source>
3993 <target state="new">Mute domains</target> 3995 <target state="new">Mute domains</target>
@@ -5985,11 +5987,8 @@ color: red;
5985 <trans-unit id="5512878593724620692" datatype="html"> 5987 <trans-unit id="5512878593724620692" datatype="html">
5986 <source>CHANNELS</source> 5988 <source>CHANNELS</source>
5987 <target state="new">CHANNELS</target> 5989 <target state="new">CHANNELS</target>
5988 <context-group purpose="location"> 5990
5989 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5991 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5990 <context context-type="linenumber">82</context>
5991 </context-group>
5992 </trans-unit>
5993 <trans-unit id="3666829335406793239"> 5992 <trans-unit id="3666829335406793239">
5994 <source>This account does not have channels.</source> 5993 <source>This account does not have channels.</source>
5995 <target>Αυτός ο λογαριασμός δεν έχει κανάλια.</target> 5994 <target>Αυτός ο λογαριασμός δεν έχει κανάλια.</target>
@@ -6032,7 +6031,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6032It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 6031It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
6033channel with the same name (<x id="PH_2"/>)!</target> 6032channel with the same name (<x id="PH_2"/>)!</target>
6034 6033
6035 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 6034 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
6035 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
6036 <context-group purpose="location">
6037 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
6038 <context context-type="linenumber">48</context>
6039 </context-group>
6040 </trans-unit>
6036 <trans-unit id="5387007581996837469" datatype="html"> 6041 <trans-unit id="5387007581996837469" datatype="html">
6037 <source>My Channels</source> 6042 <source>My Channels</source>
6038 <target state="new">My Channels</target> 6043 <target state="new">My Channels</target>
@@ -6630,12 +6635,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6630 <source>Your message has been sent.</source> 6635 <source>Your message has been sent.</source>
6631 <target>Το μήνυμά σας έχει σταλεί.</target> 6636 <target>Το μήνυμά σας έχει σταλεί.</target>
6632 6637
6633 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6638 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6634 <trans-unit id="2072135752262464360"> 6639 <trans-unit id="2072135752262464360">
6635 <source>You already sent this form recently</source> 6640 <source>You already sent this form recently</source>
6636 <target>Στείλατε ήδη αυτή τη φόρμα πρόσφατα</target> 6641 <target>Στείλατε ήδη αυτή τη φόρμα πρόσφατα</target>
6637 6642
6638 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6643 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6639 <trans-unit id="819067926858619041" datatype="html"> 6644 <trans-unit id="819067926858619041" datatype="html">
6640 <source>Account videos</source> 6645 <source>Account videos</source>
6641 <target state="new">Account videos</target> 6646 <target state="new">Account videos</target>
@@ -6681,13 +6686,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6681 <target state="new"> 6686 <target state="new">
6682 <x id="PH"/> direct account followers 6687 <x id="PH"/> direct account followers
6683 </target> 6688 </target>
6684 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6689
6685 </trans-unit> 6690 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6686 <trans-unit id="6250999352462648289" datatype="html"> 6691 <trans-unit id="6250999352462648289" datatype="html">
6687 <source>Report this account</source> 6692 <source>Report this account</source>
6688 <target state="new">Report this account</target> 6693 <target state="new">Report this account</target>
6689 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6694
6690 </trans-unit> 6695 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6691 <trans-unit id="1504521795586863905" datatype="html"> 6696 <trans-unit id="1504521795586863905" datatype="html">
6692 <source>VIDEOS</source> 6697 <source>VIDEOS</source>
6693 <target state="translated">ΒΙΝΤΕΟ</target> 6698 <target state="translated">ΒΙΝΤΕΟ</target>
@@ -6697,29 +6702,21 @@ channel with the same name (<x id="PH_2"/>)!</target>
6697 <trans-unit id="25349740244798533"> 6702 <trans-unit id="25349740244798533">
6698 <source>Username copied</source> 6703 <source>Username copied</source>
6699 <target>Το όνομα χρήστη αντιγράφτηκε</target> 6704 <target>Το όνομα χρήστη αντιγράφτηκε</target>
6700 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6705
6701 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6706
6702 </trans-unit> 6707 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6703 <trans-unit id="9221735175659318025" datatype="html"> 6708 <trans-unit id="9221735175659318025" datatype="html">
6704 <source>1 subscriber</source> 6709 <source>1 subscriber</source>
6705 <target state="new">1 subscriber</target> 6710 <target state="new">1 subscriber</target>
6706 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6711
6707 </trans-unit> 6712 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6708 <trans-unit id="4097331874769079975" datatype="html"> 6713 <trans-unit id="4097331874769079975" datatype="html">
6709 <source><x id="PH"/> subscribers</source> 6714 <source><x id="PH"/> subscribers</source>
6710 <target state="new"><x id="PH"/> subscribers</target> 6715 <target state="new"><x id="PH"/> subscribers</target>
6711 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group>
6712 </trans-unit>
6713 <trans-unit id="4682675125751819107" datatype="html">
6714 <source>Instances you follow</source>
6715 <target state="new">Instances you follow</target>
6716 6716
6717 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit> 6717 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6718 <trans-unit id="8899833753704589712" datatype="html"> 6718
6719 <source>Instances following you</source> 6719
6720 <target state="new">Instances following you</target>
6721
6722 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6723 <trans-unit id="1035838766454786107" datatype="html"> 6720 <trans-unit id="1035838766454786107" datatype="html">
6724 <source>Audio-only</source> 6721 <source>Audio-only</source>
6725 <target state="new">Audio-only</target> 6722 <target state="new">Audio-only</target>
@@ -6769,6 +6766,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6769 <source>Auto (via ffmpeg)</source> 6766 <source>Auto (via ffmpeg)</source>
6770 <target>Αυτόματα (με ffmpeg)</target> 6767 <target>Αυτόματα (με ffmpeg)</target>
6771 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6768 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6769 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6770 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6771 <context-group purpose="location">
6772 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6773 <context context-type="linenumber">3</context>
6774 </context-group>
6772 </trans-unit> 6775 </trans-unit>
6773 <trans-unit id="931255636742351800" datatype="html"> 6776 <trans-unit id="931255636742351800" datatype="html">
6774 <source>No limit</source> 6777 <source>No limit</source>
@@ -6911,18 +6914,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
6911 <trans-unit id="2127446333083057097" datatype="html"> 6914 <trans-unit id="2127446333083057097" datatype="html">
6912 <source>Domain is required.</source> 6915 <source>Domain is required.</source>
6913 <target state="new">Domain is required.</target> 6916 <target state="new">Domain is required.</target>
6914 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6917
6915 </trans-unit> 6918 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6916 <trans-unit id="6780793142903080663" datatype="html"> 6919 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6917 <source>Domains entered are invalid.</source> 6920 <context-group purpose="location">
6918 <target state="new">Domains entered are invalid.</target> 6921 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6919 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6922 <context context-type="linenumber">93</context>
6920 </trans-unit> 6923 </context-group>
6921 <trans-unit id="5886492514458202177" datatype="html"> 6924 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6922 <source>Domains entered contain duplicates.</source> 6925 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6923 <target state="new">Domains entered contain duplicates.</target> 6926 <context-group purpose="location">
6924 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6927 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6928 <context context-type="linenumber">94</context>
6929 </context-group>
6930 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6931 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6932 <context-group purpose="location">
6933 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6934 <context context-type="linenumber">102</context>
6935 </context-group>
6936 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6937 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6938 <context-group purpose="location">
6939 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6940 <context context-type="linenumber">103</context>
6941 </context-group>
6925 </trans-unit> 6942 </trans-unit>
6943
6944
6926 <trans-unit id="240806681889331244"> 6945 <trans-unit id="240806681889331244">
6927 <source>Unlimited</source> 6946 <source>Unlimited</source>
6928 <target>Απεριόριστα</target> 6947 <target>Απεριόριστα</target>
@@ -7082,26 +7101,52 @@ channel with the same name (<x id="PH_2"/>)!</target>
7082 <x id="PH"/> διαγράφηκε από ακόλουθος του κόμβου 7101 <x id="PH"/> διαγράφηκε από ακόλουθος του κόμβου
7083 </target> 7102 </target>
7084 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 7103 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
7104 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
7105 <source>Follow</source><target state="new">Follow</target>
7106 <context-group purpose="location">
7107 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7108 <context context-type="linenumber">3</context>
7109 </context-group>
7110 <context-group purpose="location">
7111 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7112 <context context-type="linenumber">37</context>
7113 </context-group>
7114 <context-group purpose="location">
7115 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7116 <context context-type="linenumber">18</context>
7117 </context-group>
7118 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
7119 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
7120 <context-group purpose="location">
7121 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7122 <context context-type="linenumber">11</context>
7123 </context-group>
7085 </trans-unit> 7124 </trans-unit>
7086 <trans-unit id="2740793005745065895"> 7125 <trans-unit id="2740793005745065895">
7087 <source><x id="PH"/> is not valid </source> 7126 <source><x id="PH"/> is not valid </source>
7088 <target> 7127 <target>
7089 <x id="PH"/> δεν είναι έγκυρο 7128 <x id="PH"/> δεν είναι έγκυρο
7090 </target> 7129 </target>
7091 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 7130
7092 </trans-unit> 7131 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
7093 <trans-unit id="2355066641781598196"> 7132 <trans-unit id="2355066641781598196">
7094 <source>Follow request(s) sent!</source> 7133 <source>Follow request(s) sent!</source>
7095 <target>Τα αιτήματα ακολούθησης στάλθηκαν!</target> 7134 <target>Τα αιτήματα ακολούθησης στάλθηκαν!</target>
7096 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 7135
7136 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
7137 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
7138 <context-group purpose="location">
7139 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7140 <context context-type="linenumber">3</context>
7141 </context-group>
7097 </trans-unit> 7142 </trans-unit>
7098 <trans-unit id="4245720728052819482"> 7143 <trans-unit id="4245720728052819482">
7099 <source>Do you really want to unfollow <x id="PH"/>?</source> 7144 <source>Do you really want to unfollow <x id="PH"/>?</source>
7100 <target>Θέλετε πραγματικά να σταματήσετε να ακολουθείτε το 7145 <target>Θέλετε πραγματικά να σταματήσετε να ακολουθείτε το
7101 <x id="PH"/>; 7146 <x id="PH"/>;
7102 </target> 7147 </target>
7103 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 7148
7104 </trans-unit> 7149 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
7105 <trans-unit id="9160510009013134726"> 7150 <trans-unit id="9160510009013134726">
7106 <source>Unfollow</source> 7151 <source>Unfollow</source>
7107 <target>Αφαίρεση ακολούθησης</target> 7152 <target>Αφαίρεση ακολούθησης</target>
@@ -7112,8 +7157,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7112 <target>Δεν ακολουθείτε το 7157 <target>Δεν ακολουθείτε το
7113 <x id="PH"/> πια. 7158 <x id="PH"/> πια.
7114 </target> 7159 </target>
7115 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 7160
7116 </trans-unit> 7161 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
7117 <trans-unit id="2593763089859685916"> 7162 <trans-unit id="2593763089859685916">
7118 <source>enabled</source> 7163 <source>enabled</source>
7119 <target>ενεργοποιήθηκε</target> 7164 <target>ενεργοποιήθηκε</target>
@@ -7596,9 +7641,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7596 <trans-unit id="1519954996184640001"> 7641 <trans-unit id="1519954996184640001">
7597 <source>Error</source> 7642 <source>Error</source>
7598 <target>Σφάλμα</target> 7643 <target>Σφάλμα</target>
7599 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7644
7600 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7645
7601 </trans-unit> 7646 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7602 <trans-unit id="5076187961693950167" datatype="html"> 7647 <trans-unit id="5076187961693950167" datatype="html">
7603 <source>Standard logs</source> 7648 <source>Standard logs</source>
7604 <target state="new">Standard logs</target> 7649 <target state="new">Standard logs</target>
@@ -7643,16 +7688,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7643 <target>Ενημέρωση κωδικού χρήστη</target> 7688 <target>Ενημέρωση κωδικού χρήστη</target>
7644 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7689 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7645 </trans-unit> 7690 </trans-unit>
7646 <trans-unit id="177544274549739411" datatype="html"> 7691
7647 <source>Following list</source> 7692
7648 <target state="new">Following list</target>
7649 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7650 </trans-unit>
7651 <trans-unit id="8092429110007204784" datatype="html">
7652 <source>Followers list</source>
7653 <target state="new">Followers list</target>
7654 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7655 </trans-unit>
7656 <trans-unit id="780323526182667308" datatype="html"> 7693 <trans-unit id="780323526182667308" datatype="html">
7657 <source>User <x id="PH"/> updated.</source> 7694 <source>User <x id="PH"/> updated.</source>
7658 <target state="new">User 7695 <target state="new">User
@@ -7692,16 +7729,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7692 <target state="new">Federation</target> 7729 <target state="new">Federation</target>
7693 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7730 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7694 </trans-unit> 7731 </trans-unit>
7695 <trans-unit id="4682675125751819107" datatype="html"> 7732
7696 <source>Instances you follow</source> 7733
7697 <target state="new">Instances you follow</target>
7698 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7699 </trans-unit>
7700 <trans-unit id="8899833753704589712" datatype="html">
7701 <source>Instances following you</source>
7702 <target state="new">Instances following you</target>
7703 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7704 </trans-unit>
7705 <trans-unit id="3767259920053407667" datatype="html"> 7734 <trans-unit id="3767259920053407667" datatype="html">
7706 <source>Videos will be deleted, comments will be tombstoned.</source> 7735 <source>Videos will be deleted, comments will be tombstoned.</source>
7707 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7736 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7732,7 +7761,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7732 <target>Το e-mail έχει επιβεβαιωθεί</target> 7761 <target>Το e-mail έχει επιβεβαιωθεί</target>
7733 7762
7734 7763
7735 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7764 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7765 <source>Created</source><target state="new">Created</target>
7766 <context-group purpose="location">
7767 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7768 <context context-type="linenumber">115</context>
7769 </context-group>
7770 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7771 <source>Daily quota</source><target state="new">Daily quota</target>
7772 <context-group purpose="location">
7773 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7774 <context context-type="linenumber">120</context>
7775 </context-group>
7776 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7777 <source>Last login</source><target state="new">Last login</target>
7778 <context-group purpose="location">
7779 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7780 <context context-type="linenumber">122</context>
7781 </context-group>
7782 </trans-unit>
7736 <trans-unit id="3403978719736970622"> 7783 <trans-unit id="3403978719736970622">
7737 <source>You cannot ban root.</source> 7784 <source>You cannot ban root.</source>
7738 <target>Δεν μπορείτε να αποκλείστε τον root.</target> 7785 <target>Δεν μπορείτε να αποκλείστε τον root.</target>
@@ -8047,13 +8094,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
8047 <target>Δημιουργήθηκε το κανάλι 8094 <target>Δημιουργήθηκε το κανάλι
8048 <x id="PH"/>. 8095 <x id="PH"/>.
8049 </target> 8096 </target>
8050 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 8097
8051 </trans-unit> 8098 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
8052 <trans-unit id="8723777130353305761"> 8099 <trans-unit id="8723777130353305761">
8053 <source>This name already exists on this instance.</source> 8100 <source>This name already exists on this instance.</source>
8054 <target>Το όνομα υπάρχει ήδη σ' αυτόν τον κόμβο</target> 8101 <target>Το όνομα υπάρχει ήδη σ' αυτόν τον κόμβο</target>
8055 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 8102
8056 </trans-unit> 8103 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
8057 <trans-unit id="7589345916094713536"> 8104 <trans-unit id="7589345916094713536">
8058 <source>Video channel <x id="PH"/> updated.</source> 8105 <source>Video channel <x id="PH"/> updated.</source>
8059 <target>Το κανάλι 8106 <target>Το κανάλι
@@ -8076,13 +8123,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8076 <target state="new">Banner deleted.</target> 8123 <target state="new">Banner deleted.</target>
8077 8124
8078 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 8125 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
8079 <trans-unit id="2575302837003821736"> 8126
8080 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
8081 <target>Παρακαλούμε πληκτρολογήστε το όνομα καναλιού του (
8082 <x id="PH"/>) για επιβεβαίωση
8083 </target>
8084
8085 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
8086 <trans-unit id="624066830180032195"> 8127 <trans-unit id="624066830180032195">
8087 <source>Video channel <x id="PH"/> deleted.</source> 8128 <source>Video channel <x id="PH"/> deleted.</source>
8088 <target>Το κανάλι 8129 <target>Το κανάλι
@@ -8241,6 +8282,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8241 <source>Ownership change request sent.</source> 8282 <source>Ownership change request sent.</source>
8242 <target>Το αίτημα αλλαγής κατόχου έχει σταλεί.</target> 8283 <target>Το αίτημα αλλαγής κατόχου έχει σταλεί.</target>
8243 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8284 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8285 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8286 <source>Sort by</source><target state="new">Sort by</target>
8287 <context-group purpose="location">
8288 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8289 <context context-type="linenumber">26</context>
8290 </context-group>
8244 </trans-unit> 8291 </trans-unit>
8245 <trans-unit id="3245220240937722814"> 8292 <trans-unit id="3245220240937722814">
8246 <source>My channels</source> 8293 <source>My channels</source>
@@ -8345,7 +8392,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8345 <target>Συνδρομή στον λογαριασμό</target> 8392 <target>Συνδρομή στον λογαριασμό</target>
8346 8393
8347 8394
8348 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8395 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8349 <trans-unit id="3131904093925601441" datatype="html"> 8396 <trans-unit id="3131904093925601441" datatype="html">
8350 <source>PLAYLISTS</source> 8397 <source>PLAYLISTS</source>
8351 <target state="new">PLAYLISTS</target> 8398 <target state="new">PLAYLISTS</target>
@@ -8392,35 +8439,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8392 <trans-unit id="3779524668013120370"> 8439 <trans-unit id="3779524668013120370">
8393 <source>Go to my subscriptions</source> 8440 <source>Go to my subscriptions</source>
8394 <target>Μετάβαση στις συνδρομές μου</target> 8441 <target>Μετάβαση στις συνδρομές μου</target>
8395 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8442
8396 </trans-unit> 8443 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8397 <trans-unit id="1136469849928650779"> 8444 <trans-unit id="1136469849928650779">
8398 <source>Go to my videos</source> 8445 <source>Go to my videos</source>
8399 <target>Μετάβαση στα βίντεό μου</target> 8446 <target>Μετάβαση στα βίντεό μου</target>
8400 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8447
8401 </trans-unit> 8448 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8402 <trans-unit id="7836683738999600376"> 8449 <trans-unit id="7836683738999600376">
8403 <source>Go to my imports</source> 8450 <source>Go to my imports</source>
8404 <target>Μετάβαση στις εισαγωγές μου</target> 8451 <target>Μετάβαση στις εισαγωγές μου</target>
8405 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8452
8406 </trans-unit> 8453 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8407 <trans-unit id="7511292153332773503"> 8454 <trans-unit id="7511292153332773503">
8408 <source>Go to my channels</source> 8455 <source>Go to my channels</source>
8409 <target>Μετάβαση στα κανάλια μου</target> 8456 <target>Μετάβαση στα κανάλια μου</target>
8410 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8457
8411 </trans-unit> 8458 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8412 <trans-unit id="2013324644839511073" datatype="html"> 8459 <trans-unit id="2013324644839511073" datatype="html">
8413 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8460 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8414Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8461Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8415 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8462 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8416Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8463Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8417 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8464
8418 </trans-unit> 8465 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8419 <trans-unit id="375263728166936544"> 8466 <trans-unit id="375263728166936544">
8420 <source>You need to reconnect.</source> 8467 <source>You need to reconnect.</source>
8421 <target>Πρέπει να ξανασυνδεθείτε.</target> 8468 <target>Πρέπει να ξανασυνδεθείτε.</target>
8422 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8469
8423 </trans-unit> 8470 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8424 <trans-unit id="2206638022166154361"> 8471 <trans-unit id="2206638022166154361">
8425 <source>Keyboard Shortcuts:</source> 8472 <source>Keyboard Shortcuts:</source>
8426 <target>Συντομεύσεις πληκτρολογίου:</target> 8473 <target>Συντομεύσεις πληκτρολογίου:</target>
@@ -8431,6 +8478,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8431 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8478 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8432 <context context-type="linenumber">98</context> 8479 <context context-type="linenumber">98</context>
8433 </context-group> 8480 </context-group>
8481 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8482 <source>In my library</source><target state="new">In my library</target>
8483 <context-group purpose="location">
8484 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8485 <context context-type="linenumber">104</context>
8486 </context-group>
8434 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 8487 </trans-unit><trans-unit id="232050922346936574" datatype="html">
8435 <source>Trending</source><target state="new">Trending</target> 8488 <source>Trending</source><target state="new">Trending</target>
8436 8489
@@ -8453,39 +8506,39 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8453 <trans-unit id="1266887509445371246"> 8506 <trans-unit id="1266887509445371246">
8454 <source>Incorrect username or password.</source> 8507 <source>Incorrect username or password.</source>
8455 <target>Λάθος όνομα χρήστη ή κωδικός.</target> 8508 <target>Λάθος όνομα χρήστη ή κωδικός.</target>
8456 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8509
8457 </trans-unit> 8510 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8458 <trans-unit id="6974874606619467663" datatype="html"> 8511 <trans-unit id="6974874606619467663" datatype="html">
8459 <source>Your account is blocked.</source> 8512 <source>Your account is blocked.</source>
8460 <target state="new">Your account is blocked.</target> 8513 <target state="new">Your account is blocked.</target>
8461 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8514
8462 </trans-unit> 8515 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8463 <trans-unit id="7939914198003891823" datatype="html"> 8516 <trans-unit id="7939914198003891823" datatype="html">
8464 <source>any language</source> 8517 <source>any language</source>
8465 <target state="new">any language</target> 8518 <target state="new">any language</target>
8466 8519
8467 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8520 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8468 8521
8469 <trans-unit id="5633144232269377096" datatype="html"> 8522 <trans-unit id="5633144232269377096" datatype="html">
8470 <source>hide</source> 8523 <source>hide</source>
8471 <target state="new">hide</target> 8524 <target state="new">hide</target>
8472 8525
8473 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8526 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8474 <trans-unit id="8603861867909474404" datatype="html"> 8527 <trans-unit id="8603861867909474404" datatype="html">
8475 <source>blur</source> 8528 <source>blur</source>
8476 <target state="new">blur</target> 8529 <target state="new">blur</target>
8477 8530
8478 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8531 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8479 <trans-unit id="4534458451100881847" datatype="html"> 8532 <trans-unit id="4534458451100881847" datatype="html">
8480 <source>display</source> 8533 <source>display</source>
8481 <target state="new">display</target> 8534 <target state="new">display</target>
8482 8535
8483 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8536 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8484 <trans-unit id="4467323362722952678" datatype="html"> 8537 <trans-unit id="4467323362722952678" datatype="html">
8485 <source>Unknown</source> 8538 <source>Unknown</source>
8486 <target state="translated">Άγνωστο</target> 8539 <target state="translated">Άγνωστο</target>
8487 8540
8488 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8541 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8489 <trans-unit id="8781423666414310853"> 8542 <trans-unit id="8781423666414310853">
8490 <source>Your password has been successfully reset!</source> 8543 <source>Your password has been successfully reset!</source>
8491 <target>Ο κωδικός σας έχει ανανεωθεί με επιτυχία!</target> 8544 <target>Ο κωδικός σας έχει ανανεωθεί με επιτυχία!</target>
@@ -10079,18 +10132,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10079 <target>Πάρα πολλές προσπάθειες, δοκιμάστε ξανά μετά από 10132 <target>Πάρα πολλές προσπάθειες, δοκιμάστε ξανά μετά από
10080 <x id="PH"/> λεπτά. 10133 <x id="PH"/> λεπτά.
10081 </target> 10134 </target>
10082 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 10135
10083 </trans-unit> 10136 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
10084 <trans-unit id="4965472196059235310"> 10137 <trans-unit id="4965472196059235310">
10085 <source>Too many attempts, please try again later.</source> 10138 <source>Too many attempts, please try again later.</source>
10086 <target>Πάρα πολλές προσπάθειες, δοκιμάστε ξανά αργότερα.</target> 10139 <target>Πάρα πολλές προσπάθειες, δοκιμάστε ξανά αργότερα.</target>
10087 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 10140
10088 </trans-unit> 10141 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
10089 <trans-unit id="1693549688987384699"> 10142 <trans-unit id="1693549688987384699">
10090 <source>Server error. Please retry later.</source> 10143 <source>Server error. Please retry later.</source>
10091 <target>Σφάλμα κόμβου. Δοκιμάστε ξανά αργότερα.</target> 10144 <target>Σφάλμα κόμβου. Δοκιμάστε ξανά αργότερα.</target>
10092 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 10145
10093 </trans-unit> 10146 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
10094 <trans-unit id="5927402622550505067" datatype="html"> 10147 <trans-unit id="5927402622550505067" datatype="html">
10095 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 10148 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
10096 <target state="new">Subscribed to all current channels of 10149 <target state="new">Subscribed to all current channels of
@@ -10686,35 +10739,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10686 <source>Your video was uploaded to your account and is private.</source> 10739 <source>Your video was uploaded to your account and is private.</source>
10687 <target>Το βίντεο ανέβηκε στον λογαριασμό σας και είναι ιδιωτικό.</target> 10740 <target>Το βίντεο ανέβηκε στον λογαριασμό σας και είναι ιδιωτικό.</target>
10688 10741
10689 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10742 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10690 <trans-unit id="5699822024600815733"> 10743 <trans-unit id="5699822024600815733">
10691 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10744 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10692 <target>Όμως οι σχετικές πληροφορίες (ετικέτες, περιγραφή...) θα χαθούν, σίγουρα θέλετε να φύγετε από τη σελίδα;</target> 10745 <target>Όμως οι σχετικές πληροφορίες (ετικέτες, περιγραφή...) θα χαθούν, σίγουρα θέλετε να φύγετε από τη σελίδα;</target>
10693 10746
10694 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10747 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10695 <trans-unit id="1219739004043110649"> 10748 <trans-unit id="1219739004043110649">
10696 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10749 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10697 <target>Το βίντεο δεν έχει ανέβει ακόμα, θέλετε σίγουρα να φύγετε από τη σελίδα;</target> 10750 <target>Το βίντεο δεν έχει ανέβει ακόμα, θέλετε σίγουρα να φύγετε από τη σελίδα;</target>
10698 10751
10699 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10752 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10700 <trans-unit id="6932865105766151309" datatype="html"> 10753 <trans-unit id="6932865105766151309" datatype="html">
10701 <source>Upload</source> 10754 <source>Upload</source>
10702 <target state="translated">Αποστολή</target> 10755 <target state="translated">Αποστολή</target>
10703 10756
10704 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10757 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10705 <trans-unit id="8278735427925094503"> 10758 <trans-unit id="8278735427925094503">
10706 <source>Upload <x id="PH"/> </source> 10759 <source>Upload <x id="PH"/> </source>
10707 <target>Ανεβάστε 10760 <target>Ανεβάστε
10708 <x id="PH"/> 10761 <x id="PH"/>
10709 </target> 10762 </target>
10710 10763
10711 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10764 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10712 10765
10713 <trans-unit id="5981816353437801748"> 10766 <trans-unit id="5981816353437801748">
10714 <source>Video published.</source> 10767 <source>Video published.</source>
10715 <target>Το βίνεο δημοσιεύτηκε.</target> 10768 <target>Το βίνεο δημοσιεύτηκε.</target>
10716 10769
10717 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10770 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10718 10771
10719 10772
10720 <trans-unit id="764164089183618119"> 10773 <trans-unit id="764164089183618119">
@@ -10764,27 +10817,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10764 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10817 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10765 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10818 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10766 10819
10767 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10820 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10768 <trans-unit id="5761611056224181752" datatype="html"> 10821 <trans-unit id="5761611056224181752" datatype="html">
10769 <source>Redirection</source> 10822 <source>Redirection</source>
10770 <target state="new">Redirection</target> 10823 <target state="new">Redirection</target>
10771 10824
10772 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10825 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10773 <trans-unit id="8858527736400081688"> 10826 <trans-unit id="8858527736400081688">
10774 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10827 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10775 <target>Το βίντεο έχει σκληρό περιεχόμενο ή μόνο για ενήλικες. Σίγουρα θέλετε να το δείτε;</target> 10828 <target>Το βίντεο έχει σκληρό περιεχόμενο ή μόνο για ενήλικες. Σίγουρα θέλετε να το δείτε;</target>
10776 10829
10777 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10830 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10778 <trans-unit id="3937119019020041049"> 10831 <trans-unit id="3937119019020041049">
10779 <source>Mature or explicit content</source> 10832 <source>Mature or explicit content</source>
10780 <target>Σκληρό περιεχόμενο ή για ενήλικες</target> 10833 <target>Σκληρό περιεχόμενο ή για ενήλικες</target>
10781 10834
10782 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10835 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10783 <trans-unit id="1755474755114288376" datatype="html"> 10836 <trans-unit id="1755474755114288376" datatype="html">
10784 <source>Up Next</source> 10837 <source>Up Next</source>
10785 <target state="translated">Επόμενο</target> 10838 <target state="translated">Επόμενο</target>
10786 10839
10787 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10840 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10788 <trans-unit id="2159130950882492111" datatype="html"> 10841 <trans-unit id="2159130950882492111" datatype="html">
10789 <source>Cancel</source> 10842 <source>Cancel</source>
10790 <target state="translated">Ακύρωση</target> 10843 <target state="translated">Ακύρωση</target>
@@ -10794,62 +10847,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10794 <source>Autoplay is suspended</source> 10847 <source>Autoplay is suspended</source>
10795 <target state="new">Autoplay is suspended</target> 10848 <target state="new">Autoplay is suspended</target>
10796 10849
10797 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10850 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10798 <trans-unit id="7895294730547405228" datatype="html"> 10851 <trans-unit id="7895294730547405228" datatype="html">
10799 <source>Enter/exit fullscreen (requires player focus)</source> 10852 <source>Enter/exit fullscreen (requires player focus)</source>
10800 <target state="new">Enter/exit fullscreen (requires player focus)</target> 10853 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10801 10854
10802 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10855 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10803 <trans-unit id="7618388257165864759" datatype="html"> 10856 <trans-unit id="7618388257165864759" datatype="html">
10804 <source>Play/Pause the video (requires player focus)</source> 10857 <source>Play/Pause the video (requires player focus)</source>
10805 <target state="new">Play/Pause the video (requires player focus)</target> 10858 <target state="new">Play/Pause the video (requires player focus)</target>
10806 10859
10807 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10860 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10808 <trans-unit id="7761890399634216630" datatype="html"> 10861 <trans-unit id="7761890399634216630" datatype="html">
10809 <source>Mute/unmute the video (requires player focus)</source> 10862 <source>Mute/unmute the video (requires player focus)</source>
10810 <target state="new">Mute/unmute the video (requires player focus)</target> 10863 <target state="new">Mute/unmute the video (requires player focus)</target>
10811 10864
10812 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10865 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10813 <trans-unit id="5996585232248234904" datatype="html"> 10866 <trans-unit id="5996585232248234904" datatype="html">
10814 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10867 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10815 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10868 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10816 10869
10817 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10870 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10818 <trans-unit id="3748765405903319998" datatype="html"> 10871 <trans-unit id="3748765405903319998" datatype="html">
10819 <source>Increase the volume (requires player focus)</source> 10872 <source>Increase the volume (requires player focus)</source>
10820 <target state="new">Increase the volume (requires player focus)</target> 10873 <target state="new">Increase the volume (requires player focus)</target>
10821 10874
10822 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10875 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10823 <trans-unit id="5810704036407159982" datatype="html"> 10876 <trans-unit id="5810704036407159982" datatype="html">
10824 <source>Decrease the volume (requires player focus)</source> 10877 <source>Decrease the volume (requires player focus)</source>
10825 <target state="new">Decrease the volume (requires player focus)</target> 10878 <target state="new">Decrease the volume (requires player focus)</target>
10826 10879
10827 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10880 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10828 <trans-unit id="2622048822548065691" datatype="html"> 10881 <trans-unit id="2622048822548065691" datatype="html">
10829 <source>Seek the video forward (requires player focus)</source> 10882 <source>Seek the video forward (requires player focus)</source>
10830 <target state="new">Seek the video forward (requires player focus)</target> 10883 <target state="new">Seek the video forward (requires player focus)</target>
10831 10884
10832 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10885 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10833 <trans-unit id="6540078205109221153" datatype="html"> 10886 <trans-unit id="6540078205109221153" datatype="html">
10834 <source>Seek the video backward (requires player focus)</source> 10887 <source>Seek the video backward (requires player focus)</source>
10835 <target state="new">Seek the video backward (requires player focus)</target> 10888 <target state="new">Seek the video backward (requires player focus)</target>
10836 10889
10837 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10890 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10838 <trans-unit id="1956491957766210808" datatype="html"> 10891 <trans-unit id="1956491957766210808" datatype="html">
10839 <source>Increase playback rate (requires player focus)</source> 10892 <source>Increase playback rate (requires player focus)</source>
10840 <target state="new">Increase playback rate (requires player focus)</target> 10893 <target state="new">Increase playback rate (requires player focus)</target>
10841 10894
10842 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10895 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10843 <trans-unit id="5495529997674803186" datatype="html"> 10896 <trans-unit id="5495529997674803186" datatype="html">
10844 <source>Decrease playback rate (requires player focus)</source> 10897 <source>Decrease playback rate (requires player focus)</source>
10845 <target state="translated">Μειώστε το ποσοστό αναπαραγωγής (απαιτεί εστίαση του προγράμματος αναπαραγωγής))</target> 10898 <target state="translated">Μειώστε το ποσοστό αναπαραγωγής (απαιτεί εστίαση του προγράμματος αναπαραγωγής))</target>
10846 10899
10847 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10900 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10848 <trans-unit id="3178343147230721210" datatype="html"> 10901 <trans-unit id="3178343147230721210" datatype="html">
10849 <source>Navigate in the video frame by frame (requires player focus)</source> 10902 <source>Navigate in the video frame by frame (requires player focus)</source>
10850 <target state="translated">Περιήγηση στο βίντεο καρέ ανά καρέ (απαιτεί εστίαση του προγράμματος αναπαραγωγής)</target> 10903 <target state="translated">Περιήγηση στο βίντεο καρέ ανά καρέ (απαιτεί εστίαση του προγράμματος αναπαραγωγής)</target>
10851 10904
10852 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10905 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10853 <trans-unit id="8025996572234182184"> 10906 <trans-unit id="8025996572234182184">
10854 <source>Like the video</source> 10907 <source>Like the video</source>
10855 <target>Σας αρέσει το βίντεο</target> 10908 <target>Σας αρέσει το βίντεο</target>
diff --git a/client/src/locale/angular.en-GB.xlf b/client/src/locale/angular.en-GB.xlf
index 034488b82..b7b8cdb7a 100644
--- a/client/src/locale/angular.en-GB.xlf
+++ b/client/src/locale/angular.en-GB.xlf
@@ -301,7 +301,7 @@
301 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 301 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
302 </target> 302 </target>
303 303
304 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html"> 304 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html">
305 <source>My watch history</source><target state="new">My watch history</target> 305 <source>My watch history</source><target state="new">My watch history</target>
306 306
307 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 307 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
@@ -382,12 +382,12 @@
382 382
383 383
384 384
385 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 385 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
386 <trans-unit id="1006562256968398209" datatype="html"> 386 <trans-unit id="1006562256968398209" datatype="html">
387 <source>video</source> 387 <source>video</source>
388 <target state="new">video</target> 388 <target state="new">video</target>
389 389
390 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 390 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
391 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 391 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
392 392
393 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 393 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -449,10 +449,10 @@
449 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 449 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
450 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 450 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
451 451
452 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 452 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
453 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 453 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
454 454
455 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html"> 455 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html">
456 <source>subtitles</source><target state="new">subtitles</target> 456 <source>subtitles</source><target state="new">subtitles</target>
457 457
458 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 458 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
@@ -835,7 +835,7 @@
835 835
836 836
837 837
838 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group></trans-unit> 838 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
839 <trans-unit id="1502595455339510144"> 839 <trans-unit id="1502595455339510144">
840 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 840 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
841 <target> 841 <target>
@@ -918,7 +918,31 @@
918 <source>Federation</source> 918 <source>Federation</source>
919 <target state="new">Federation</target> 919 <target state="new">Federation</target>
920 920
921 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 921 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
922 <source>Following</source><target state="new">Following</target>
923 <context-group purpose="location">
924 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
925 <context context-type="linenumber">29</context>
926 </context-group>
927 <context-group purpose="location">
928 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
929 <context context-type="linenumber">31</context>
930 </context-group>
931 <context-group purpose="location">
932 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
933 <context context-type="linenumber">28</context>
934 </context-group>
935 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
936 <source>Followers</source><target state="new">Followers</target>
937 <context-group purpose="location">
938 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
939 <context context-type="linenumber">34</context>
940 </context-group>
941 <context-group purpose="location">
942 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
943 <context context-type="linenumber">37</context>
944 </context-group>
945 </trans-unit>
922 <trans-unit id="3541687134897970106" datatype="html"> 946 <trans-unit id="3541687134897970106" datatype="html">
923 <source>followers</source> 947 <source>followers</source>
924 <target state="new">followers</target> 948 <target state="new">followers</target>
@@ -980,7 +1004,7 @@
980 1004
981 1005
982 1006
983 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 1007 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
984 <trans-unit id="3616223838716839702"> 1008 <trans-unit id="3616223838716839702">
985 <source>Ban this user</source> 1009 <source>Ban this user</source>
986 <target>Ban this user</target> 1010 <target>Ban this user</target>
@@ -1046,17 +1070,11 @@
1046 1070
1047 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html"> 1071 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html">
1048 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1072 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1049 <context-group purpose="location"> 1073
1050 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1074 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7215649348148521605" datatype="html">
1051 <context context-type="linenumber">60,62</context>
1052 </context-group>
1053 </trans-unit><trans-unit id="7215649348148521605" datatype="html">
1054 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1075 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1055 <context-group purpose="location"> 1076
1056 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1077 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1057 <context context-type="linenumber">65,67</context>
1058 </context-group>
1059 </trans-unit>
1060 <trans-unit id="2392488717875840729"> 1078 <trans-unit id="2392488717875840729">
1061 <source>User</source> 1079 <source>User</source>
1062 <target>User</target> 1080 <target>User</target>
@@ -1067,7 +1085,13 @@
1067 <source>Username or email address</source> 1085 <source>Username or email address</source>
1068 <target>Username or email address</target> 1086 <target>Username or email address</target>
1069 1087
1070 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit> 1088 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="1758058452376026925" datatype="html">
1089 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1090 <context-group purpose="location">
1091 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1092 <context context-type="linenumber">33,34</context>
1093 </context-group>
1094 </trans-unit>
1071 1095
1072 <trans-unit id="1431416938026210429"> 1096 <trans-unit id="1431416938026210429">
1073 <source>Password</source> 1097 <source>Password</source>
@@ -1080,50 +1104,44 @@
1080 1104
1081 1105
1082 1106
1083 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group></trans-unit> 1107 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1084 <trans-unit id="8715156686857791956" datatype="html"> 1108 <trans-unit id="8715156686857791956" datatype="html">
1085 <source>Click here to reset your password</source> 1109 <source>Click here to reset your password</source>
1086 <target state="new">Click here to reset your password</target> 1110 <target state="new">Click here to reset your password</target>
1087 1111
1088 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html"> 1112 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html">
1089 <source>I forgot my password</source><target state="new">I forgot my password</target> 1113 <source>I forgot my password</source><target state="new">I forgot my password</target>
1090 <context-group purpose="location"> 1114
1091 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1115 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="2101170466365500913" datatype="html">
1092 <context context-type="linenumber">47</context>
1093 </context-group>
1094 </trans-unit><trans-unit id="2101170466365500913" datatype="html">
1095 <source> Logging into an account lets you publish content </source><target state="new"> Logging into an account lets you publish content </target> 1116 <source> Logging into an account lets you publish content </source><target state="new"> Logging into an account lets you publish content </target>
1096 <context-group purpose="location"> 1117
1097 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1118 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1098 <context context-type="linenumber">56,57</context>
1099 </context-group>
1100 </trans-unit>
1101 <trans-unit id="2454050363478003966"> 1119 <trans-unit id="2454050363478003966">
1102 <source>Login</source> 1120 <source>Login</source>
1103 <target>Login</target> 1121 <target>Login</target>
1104 1122
1105 1123
1106 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1124 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1107 <trans-unit id="3183213940445113677" datatype="html"> 1125 <trans-unit id="3183213940445113677" datatype="html">
1108 <source>Or sign in with</source> 1126 <source>Or sign in with</source>
1109 <target state="new">Or sign in with</target> 1127 <target state="new">Or sign in with</target>
1110 1128
1111 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit> 1129 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1112 <trans-unit id="3238209155172574367"> 1130 <trans-unit id="3238209155172574367">
1113 <source>Forgot your password</source> 1131 <source>Forgot your password</source>
1114 <target>Forgot your password</target> 1132 <target>Forgot your password</target>
1115 1133
1116 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group></trans-unit> 1134 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1117 <trans-unit id="87327320394367488" datatype="html"> 1135 <trans-unit id="87327320394367488" datatype="html">
1118 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1136 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1119 <target state="new"> 1137 <target state="new">
1120 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1138 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
1121 </target> 1139 </target>
1122 1140
1123 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html"> 1141 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html">
1124 <source> Enter your email address and we will send you a link to reset your password. </source><target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1142 <source> Enter your email address and we will send you a link to reset your password. </source><target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1125 1143
1126 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html"> 1144 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html">
1127 <source>An email with the reset password instructions will be sent to <x id="PH"/>. 1145 <source>An email with the reset password instructions will be sent to <x id="PH"/>.
1128The link will expire within 1 hour.</source><target state="new">An email with the reset password instructions will be sent to <x id="PH"/>. 1146The link will expire within 1 hour.</source><target state="new">An email with the reset password instructions will be sent to <x id="PH"/>.
1129The link will expire within 1 hour.</target> 1147The link will expire within 1 hour.</target>
@@ -1139,17 +1157,17 @@ The link will expire within 1 hour.</target>
1139 1157
1140 1158
1141 1159
1142 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group></trans-unit> 1160 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1143 <trans-unit id="3967269098753656610"> 1161 <trans-unit id="3967269098753656610">
1144 <source>Email address</source> 1162 <source>Email address</source>
1145 <target>Email address</target> 1163 <target>Email address</target>
1146 1164
1147 1165
1148 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html"> 1166 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html">
1149 <source>Reset</source><target state="new">Reset</target> 1167 <source>Reset</source><target state="new">Reset</target>
1150 1168
1151 <note priority="1" from="description">Password reset button</note> 1169 <note priority="1" from="description">Password reset button</note>
1152 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group></trans-unit> 1170 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1153 1171
1154 1172
1155 <trans-unit id="4319634264526091601" datatype="html"> 1173 <trans-unit id="4319634264526091601" datatype="html">
@@ -1511,7 +1529,7 @@ The link will expire within 1 hour.</target>
1511 <source>Create an account</source> 1529 <source>Create an account</source>
1512 <target>Create an account</target> 1530 <target>Create an account</target>
1513 1531
1514 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1532 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1515 1533
1516 <trans-unit id="3058024914967508975" datatype="html"> 1534 <trans-unit id="3058024914967508975" datatype="html">
1517 <source>My videos</source><target state="new">My videos</target> 1535 <source>My videos</source><target state="new">My videos</target>
@@ -1557,7 +1575,7 @@ The link will expire within 1 hour.</target>
1557 <target state="new">VIDEOS</target> 1575 <target state="new">VIDEOS</target>
1558 1576
1559 1577
1560 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html"> 1578 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html">
1561 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target> 1579 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target>
1562 1580
1563 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html"> 1581 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html">
@@ -1625,7 +1643,7 @@ The link will expire within 1 hour.</target>
1625 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html"> 1643 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html">
1626 <source>I'm a teapot</source><target state="new">I'm a teapot</target> 1644 <source>I'm a teapot</source><target state="new">I'm a teapot</target>
1627 1645
1628 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html"> 1646 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html">
1629 <source>That's an error.</source><target state="new">That's an error.</target> 1647 <source>That's an error.</source><target state="new">That's an error.</target>
1630 <context-group purpose="location"> 1648 <context-group purpose="location">
1631 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context> 1649 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context>
@@ -1691,7 +1709,7 @@ The link will expire within 1 hour.</target>
1691 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html"> 1709 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html">
1692 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1710 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1693 1711
1694 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group></trans-unit> 1712 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1695 1713
1696 <trans-unit id="5131854469652959713" datatype="html"> 1714 <trans-unit id="5131854469652959713" datatype="html">
1697 <source>GLOBAL SEARCH</source> 1715 <source>GLOBAL SEARCH</source>
@@ -2385,7 +2403,7 @@ The link will expire within 1 hour.</target>
2385 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2403 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2386 <source>Upload on hold</source><target state="new">Upload on hold</target> 2404 <source>Upload on hold</source><target state="new">Upload on hold</target>
2387 2405
2388 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2406 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2389 <trans-unit id="285180972645018518" datatype="html"> 2407 <trans-unit id="285180972645018518" datatype="html">
2390 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2408 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2391 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2409 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3045,11 +3063,7 @@ The link will expire within 1 hour.</target>
3045 <target>ID</target> 3063 <target>ID</target>
3046 3064
3047 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit> 3065 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit>
3048 <trans-unit id="2265605798180116441" datatype="html"> 3066
3049 <source>Follower handle</source>
3050 <target state="new">Follower handle</target>
3051
3052 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
3053 <trans-unit id="5911214550882917183"> 3067 <trans-unit id="5911214550882917183">
3054 <source>State</source> 3068 <source>State</source>
3055 <target>State</target> 3069 <target>State</target>
@@ -3131,11 +3145,7 @@ The link will expire within 1 hour.</target>
3131 </target> 3145 </target>
3132 3146
3133 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit> 3147 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
3134 <trans-unit id="6641024648411549335"> 3148
3135 <source>Host</source>
3136 <target>Host</target>
3137
3138 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3139 <trans-unit id="6571718060636962350" datatype="html"> 3149 <trans-unit id="6571718060636962350" datatype="html">
3140 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3150 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3141 <target state="new">Redundancy allowed 3151 <target state="new">Redundancy allowed
@@ -3146,7 +3156,7 @@ The link will expire within 1 hour.</target>
3146 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html"> 3156 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html">
3147 <source>Unfollow</source><target state="new">Unfollow</target> 3157 <source>Unfollow</source><target state="new">Unfollow</target>
3148 3158
3149 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3159 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3150 <trans-unit id="8246779176913476983" datatype="html"> 3160 <trans-unit id="8246779176913476983" datatype="html">
3151 <source>Open instance in a new tab</source> 3161 <source>Open instance in a new tab</source>
3152 <target state="new">Open instance in a new tab</target> 3162 <target state="new">Open instance in a new tab</target>
@@ -3158,12 +3168,12 @@ The link will expire within 1 hour.</target>
3158 <source>No host found matching current filters.</source> 3168 <source>No host found matching current filters.</source>
3159 <target state="new">No host found matching current filters.</target> 3169 <target state="new">No host found matching current filters.</target>
3160 3170
3161 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3171 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3162 <trans-unit id="7274241885665071790" datatype="html"> 3172 <trans-unit id="7274241885665071790" datatype="html">
3163 <source>Your instance is not following anyone.</source> 3173 <source>Your instance is not following anyone.</source>
3164 <target state="new">Your instance is not following anyone.</target> 3174 <target state="new">Your instance is not following anyone.</target>
3165 3175
3166 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3176 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3167 <trans-unit id="4774348799569692380" datatype="html"> 3177 <trans-unit id="4774348799569692380" datatype="html">
3168 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3178 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3169 <target state="new">Showing 3179 <target state="new">Showing
@@ -3173,14 +3183,7 @@ The link will expire within 1 hour.</target>
3173 </target> 3183 </target>
3174 3184
3175 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3185 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3176 <trans-unit id="6275803119759621687" datatype="html"> 3186 <trans-unit id="9216117865911519658" datatype="html">
3177 <source>Follow domains</source>
3178 <target state="new">Follow domains</target>
3179
3180 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit><trans-unit id="1268699198448750610" datatype="html">
3181 <source>Follow instances</source><target state="new">Follow instances</target>
3182
3183 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3184 <source>Action</source><target state="new">Action</target> 3187 <source>Action</source><target state="new">Action</target>
3185 3188
3186 3189
@@ -3230,7 +3233,7 @@ The link will expire within 1 hour.</target>
3230 3233
3231 3234
3232 3235
3233 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html"> 3236 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html">
3234 <source>e.g. jane_doe</source><target state="new">e.g. jane_doe</target> 3237 <source>e.g. jane_doe</source><target state="new">e.g. jane_doe</target>
3235 3238
3236 <note priority="1" from="description">Username choice placeholder in the registration form</note> 3239 <note priority="1" from="description">Username choice placeholder in the registration form</note>
@@ -3262,7 +3265,7 @@ The link will expire within 1 hour.</target>
3262 <target>Role</target> 3265 <target>Role</target>
3263 3266
3264 3267
3265 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group></trans-unit> 3268 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3266 <trans-unit id="7046347992315328430" datatype="html"> 3269 <trans-unit id="7046347992315328430" datatype="html">
3267 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3270 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3268 <target state="new"> 3271 <target state="new">
@@ -3285,15 +3288,9 @@ The link will expire within 1 hour.</target>
3285 3288
3286 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html"> 3289 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html">
3287 <source>Auth plugin</source><target state="new">Auth plugin</target> 3290 <source>Auth plugin</source><target state="new">Auth plugin</target>
3288 <context-group purpose="location"> 3291
3289 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3292
3290 <context context-type="linenumber">188</context> 3293 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit><trans-unit id="588099657508661970" datatype="html">
3291 </context-group>
3292 <context-group purpose="location">
3293 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3294 <context context-type="linenumber">188</context>
3295 </context-group>
3296 </trans-unit><trans-unit id="588099657508661970" datatype="html">
3297 <source>None (local authentication)</source><target state="new">None (local authentication)</target> 3294 <source>None (local authentication)</source><target state="new">None (local authentication)</target>
3298 <context-group purpose="location"> 3295 <context-group purpose="location">
3299 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3296 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
@@ -3520,7 +3517,13 @@ The link will expire within 1 hour.</target>
3520 3517
3521 3518
3522 3519
3523 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="4691552465058437520" datatype="html"> 3520 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3521 <source>Follower</source><target state="new">Follower</target>
3522 <context-group purpose="location">
3523 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3524 <context context-type="linenumber">24</context>
3525 </context-group>
3526 </trans-unit><trans-unit id="4691552465058437520" datatype="html">
3524 <source>Commented video</source><target state="new">Commented video</target> 3527 <source>Commented video</source><target state="new">Commented video</target>
3525 3528
3526 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html"> 3529 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html">
@@ -3842,7 +3845,7 @@ The link will expire within 1 hour.</target>
3842 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 3845 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
3843 </target> 3846 </target>
3844 3847
3845 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3848 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3846 <trans-unit id="4058814854824495833" datatype="html"> 3849 <trans-unit id="4058814854824495833" datatype="html">
3847 <source>Mute domains</source> 3850 <source>Mute domains</source>
3848 <target state="new">Mute domains</target> 3851 <target state="new">Mute domains</target>
@@ -5647,11 +5650,8 @@ color: red;
5647 5650
5648 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5651 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5649 <source>CHANNELS</source><target state="new">CHANNELS</target> 5652 <source>CHANNELS</source><target state="new">CHANNELS</target>
5650 <context-group purpose="location"> 5653
5651 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5654 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5652 <context context-type="linenumber">82</context>
5653 </context-group>
5654 </trans-unit>
5655 5655
5656 <trans-unit id="3666829335406793239" datatype="html"> 5656 <trans-unit id="3666829335406793239" datatype="html">
5657 <source>This account does not have channels.</source> 5657 <source>This account does not have channels.</source>
@@ -5689,7 +5689,13 @@ channel with the same name (<x id="PH_2"/>)!</source><target state="new">Do you
5689It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 5689It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
5690channel with the same name (<x id="PH_2"/>)!</target> 5690channel with the same name (<x id="PH_2"/>)!</target>
5691 5691
5692 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5692 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5693 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5694 <context-group purpose="location">
5695 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5696 <context context-type="linenumber">48</context>
5697 </context-group>
5698 </trans-unit>
5693 <trans-unit id="5387007581996837469" datatype="html"> 5699 <trans-unit id="5387007581996837469" datatype="html">
5694 <source>My Channels</source> 5700 <source>My Channels</source>
5695 <target state="new">My Channels</target> 5701 <target state="new">My Channels</target>
@@ -6283,12 +6289,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6283 <source>Your message has been sent.</source> 6289 <source>Your message has been sent.</source>
6284 <target>Your message has been sent.</target> 6290 <target>Your message has been sent.</target>
6285 6291
6286 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6292 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6287 <trans-unit id="2072135752262464360"> 6293 <trans-unit id="2072135752262464360">
6288 <source>You already sent this form recently</source> 6294 <source>You already sent this form recently</source>
6289 <target>You already sent this form recently</target> 6295 <target>You already sent this form recently</target>
6290 6296
6291 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6297 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6292 <trans-unit id="819067926858619041" datatype="html"> 6298 <trans-unit id="819067926858619041" datatype="html">
6293 <source>Account videos</source><target state="new">Account videos</target> 6299 <source>Account videos</source><target state="new">Account videos</target>
6294 6300
@@ -6323,10 +6329,10 @@ channel with the same name (<x id="PH_2"/>)!</target>
6323 <x id="PH"/> direct account followers 6329 <x id="PH"/> direct account followers
6324 </target> 6330 </target>
6325 6331
6326 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html"> 6332 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html">
6327 <source>Report this account</source><target state="new">Report this account</target> 6333 <source>Report this account</source><target state="new">Report this account</target>
6328 6334
6329 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6335 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6330 6336
6331 <trans-unit id="1504521795586863905" datatype="html"> 6337 <trans-unit id="1504521795586863905" datatype="html">
6332 <source>VIDEOS</source><target state="new">VIDEOS</target> 6338 <source>VIDEOS</source><target state="new">VIDEOS</target>
@@ -6338,24 +6344,16 @@ channel with the same name (<x id="PH_2"/>)!</target>
6338 <target state="new">Username copied</target> 6344 <target state="new">Username copied</target>
6339 6345
6340 6346
6341 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html"> 6347 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html">
6342 <source>1 subscriber</source><target state="new">1 subscriber</target> 6348 <source>1 subscriber</source><target state="new">1 subscriber</target>
6343 6349
6344 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html"> 6350 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html">
6345 <source><x id="PH"/> subscribers</source><target state="new"><x id="PH"/> subscribers</target> 6351 <source><x id="PH"/> subscribers</source><target state="new"><x id="PH"/> subscribers</target>
6346 6352
6347 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6353 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6354
6355
6348 6356
6349 <trans-unit id="4682675125751819107" datatype="html">
6350 <source>Instances you follow</source>
6351 <target state="new">Instances you follow</target>
6352
6353 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6354 <trans-unit id="8899833753704589712" datatype="html">
6355 <source>Instances following you</source>
6356 <target state="new">Instances following you</target>
6357
6358 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6359 <trans-unit id="1035838766454786107" datatype="html"> 6357 <trans-unit id="1035838766454786107" datatype="html">
6360 <source>Audio-only</source> 6358 <source>Audio-only</source>
6361 <target state="new">Audio-only</target> 6359 <target state="new">Audio-only</target>
@@ -6403,7 +6401,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6403 <source>Auto (via ffmpeg)</source> 6401 <source>Auto (via ffmpeg)</source>
6404 <target>Auto (via ffmpeg)</target> 6402 <target>Auto (via ffmpeg)</target>
6405 6403
6406 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="931255636742351800" datatype="html"> 6404 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="3642770981085338761" datatype="html">
6405 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6406 <context-group purpose="location">
6407 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6408 <context context-type="linenumber">3</context>
6409 </context-group>
6410 </trans-unit><trans-unit id="931255636742351800" datatype="html">
6407 <source>No limit</source><target state="new">No limit</target> 6411 <source>No limit</source><target state="new">No limit</target>
6408 6412
6409 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html"> 6413 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html">
@@ -6522,17 +6526,33 @@ channel with the same name (<x id="PH_2"/>)!</target>
6522 <source>Domain is required.</source> 6526 <source>Domain is required.</source>
6523 <target state="new">Domain is required.</target> 6527 <target state="new">Domain is required.</target>
6524 6528
6525 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group></trans-unit> 6529 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6526 <trans-unit id="6780793142903080663" datatype="html"> 6530 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6527 <source>Domains entered are invalid.</source> 6531 <context-group purpose="location">
6528 <target state="new">Domains entered are invalid.</target> 6532 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6529 6533 <context context-type="linenumber">93</context>
6530 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6534 </context-group>
6531 <trans-unit id="5886492514458202177" datatype="html"> 6535 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6532 <source>Domains entered contain duplicates.</source> 6536 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6533 <target state="new">Domains entered contain duplicates.</target> 6537 <context-group purpose="location">
6534 6538 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6535 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 6539 <context context-type="linenumber">94</context>
6540 </context-group>
6541 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6542 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6543 <context-group purpose="location">
6544 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6545 <context context-type="linenumber">102</context>
6546 </context-group>
6547 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6548 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6549 <context-group purpose="location">
6550 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6551 <context context-type="linenumber">103</context>
6552 </context-group>
6553 </trans-unit>
6554
6555
6536 <trans-unit id="240806681889331244"> 6556 <trans-unit id="240806681889331244">
6537 <source>Unlimited</source> 6557 <source>Unlimited</source>
6538 <target>Unlimited</target> 6558 <target>Unlimited</target>
@@ -6663,7 +6683,27 @@ channel with the same name (<x id="PH_2"/>)!</target>
6663 <x id="PH"/> removed from instance followers 6683 <x id="PH"/> removed from instance followers
6664 </target> 6684 </target>
6665 6685
6666 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit> 6686 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit><trans-unit id="6018246591673612412" datatype="html">
6687 <source>Follow</source><target state="new">Follow</target>
6688 <context-group purpose="location">
6689 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6690 <context context-type="linenumber">3</context>
6691 </context-group>
6692 <context-group purpose="location">
6693 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6694 <context context-type="linenumber">37</context>
6695 </context-group>
6696 <context-group purpose="location">
6697 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6698 <context context-type="linenumber">18</context>
6699 </context-group>
6700 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6701 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6702 <context-group purpose="location">
6703 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6704 <context context-type="linenumber">11</context>
6705 </context-group>
6706 </trans-unit>
6667 <trans-unit id="2740793005745065895"> 6707 <trans-unit id="2740793005745065895">
6668 <source> 6708 <source>
6669 <x id="PH"/> is not valid 6709 <x id="PH"/> is not valid
@@ -6672,19 +6712,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
6672 <x id="PH"/> is not valid 6712 <x id="PH"/> is not valid
6673 </target> 6713 </target>
6674 6714
6675 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group></trans-unit> 6715 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6676 <trans-unit id="2355066641781598196"> 6716 <trans-unit id="2355066641781598196">
6677 <source>Follow request(s) sent!</source> 6717 <source>Follow request(s) sent!</source>
6678 <target>Follow request(s) sent!</target> 6718 <target>Follow request(s) sent!</target>
6679 6719
6680 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit> 6720 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6721 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6722 <context-group purpose="location">
6723 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6724 <context context-type="linenumber">3</context>
6725 </context-group>
6726 </trans-unit>
6681 <trans-unit id="4245720728052819482"> 6727 <trans-unit id="4245720728052819482">
6682 <source>Do you really want to unfollow <x id="PH"/>?</source> 6728 <source>Do you really want to unfollow <x id="PH"/>?</source>
6683 <target>Do you really want to unfollow 6729 <target>Do you really want to unfollow
6684 <x id="PH"/>? 6730 <x id="PH"/>?
6685 </target> 6731 </target>
6686 6732
6687 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6733 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6688 <trans-unit id="9160510009013134726"> 6734 <trans-unit id="9160510009013134726">
6689 <source>Unfollow</source> 6735 <source>Unfollow</source>
6690 <target>Unfollow</target> 6736 <target>Unfollow</target>
@@ -6696,7 +6742,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
6696 <x id="PH"/> anymore. 6742 <x id="PH"/> anymore.
6697 </target> 6743 </target>
6698 6744
6699 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 6745 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6700 <trans-unit id="2593763089859685916"> 6746 <trans-unit id="2593763089859685916">
6701 <source>enabled</source> 6747 <source>enabled</source>
6702 <target>enabled</target> 6748 <target>enabled</target>
@@ -7154,7 +7200,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7154 7200
7155 7201
7156 7202
7157 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit> 7203 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7158 <trans-unit id="5076187961693950167" datatype="html"> 7204 <trans-unit id="5076187961693950167" datatype="html">
7159 <source>Standard logs</source> 7205 <source>Standard logs</source>
7160 <target state="new">Standard logs</target> 7206 <target state="new">Standard logs</target>
@@ -7192,13 +7238,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7192 <source>Update user password</source> 7238 <source>Update user password</source>
7193 <target>Update the user password</target> 7239 <target>Update the user password</target>
7194 7240
7195 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit><trans-unit id="177544274549739411" datatype="html"> 7241 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit>
7196 <source>Following list</source><target state="new">Following list</target>
7197
7198 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group></trans-unit><trans-unit id="8092429110007204784" datatype="html">
7199 <source>Followers list</source><target state="new">Followers list</target>
7200
7201 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group></trans-unit>
7202 <trans-unit id="780323526182667308" datatype="html"> 7242 <trans-unit id="780323526182667308" datatype="html">
7203 <source>User <x id="PH"/> updated.</source> 7243 <source>User <x id="PH"/> updated.</source>
7204 <target state="new">User 7244 <target state="new">User
@@ -7229,13 +7269,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7229 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html"> 7269 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html">
7230 <source>Federation</source><target state="new">Federation</target> 7270 <source>Federation</source><target state="new">Federation</target>
7231 7271
7232 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="4682675125751819107" datatype="html"> 7272 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit>
7233 <source>Instances you follow</source><target state="new">Instances you follow</target>
7234
7235 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group></trans-unit><trans-unit id="8899833753704589712" datatype="html">
7236 <source>Instances following you</source><target state="new">Instances following you</target>
7237
7238 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit>
7239 <trans-unit id="3767259920053407667" datatype="html"> 7273 <trans-unit id="3767259920053407667" datatype="html">
7240 <source>Videos will be deleted, comments will be tombstoned.</source> 7274 <source>Videos will be deleted, comments will be tombstoned.</source>
7241 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7275 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7263,7 +7297,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7263 <target>Set Email as Verified</target> 7297 <target>Set Email as Verified</target>
7264 7298
7265 7299
7266 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7300 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7301 <source>Created</source><target state="new">Created</target>
7302 <context-group purpose="location">
7303 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7304 <context context-type="linenumber">115</context>
7305 </context-group>
7306 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7307 <source>Daily quota</source><target state="new">Daily quota</target>
7308 <context-group purpose="location">
7309 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7310 <context context-type="linenumber">120</context>
7311 </context-group>
7312 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7313 <source>Last login</source><target state="new">Last login</target>
7314 <context-group purpose="location">
7315 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7316 <context context-type="linenumber">122</context>
7317 </context-group>
7318 </trans-unit>
7267 <trans-unit id="3403978719736970622"> 7319 <trans-unit id="3403978719736970622">
7268 <source>You cannot ban root.</source> 7320 <source>You cannot ban root.</source>
7269 <target>You cannot ban root.</target> 7321 <target>You cannot ban root.</target>
@@ -7568,12 +7620,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
7568 <x id="PH"/> created. 7620 <x id="PH"/> created.
7569 </target> 7621 </target>
7570 7622
7571 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7623 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7572 <trans-unit id="8723777130353305761"> 7624 <trans-unit id="8723777130353305761">
7573 <source>This name already exists on this instance.</source> 7625 <source>This name already exists on this instance.</source>
7574 <target>This name already exists on this instance.</target> 7626 <target>This name already exists on this instance.</target>
7575 7627
7576 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7628 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7577 <trans-unit id="7589345916094713536"> 7629 <trans-unit id="7589345916094713536">
7578 <source>Video channel <x id="PH"/> updated.</source> 7630 <source>Video channel <x id="PH"/> updated.</source>
7579 <target>Video channel 7631 <target>Video channel
@@ -7590,13 +7642,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7590 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 7642 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7591 7643
7592 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7644 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7593 <trans-unit id="2575302837003821736"> 7645
7594 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7595 <target>Please type the display name of the video channel (
7596 <x id="PH"/>) to confirm
7597 </target>
7598
7599 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7600 <trans-unit id="624066830180032195"> 7646 <trans-unit id="624066830180032195">
7601 <source>Video channel <x id="PH"/> deleted.</source> 7647 <source>Video channel <x id="PH"/> deleted.</source>
7602 <target>Video channel 7648 <target>Video channel
@@ -7739,7 +7785,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
7739 <source>Ownership change request sent.</source> 7785 <source>Ownership change request sent.</source>
7740 <target>Ownership change request sent.</target> 7786 <target>Ownership change request sent.</target>
7741 7787
7742 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 7788 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7699622144571229146" datatype="html">
7789 <source>Sort by</source><target state="new">Sort by</target>
7790 <context-group purpose="location">
7791 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7792 <context context-type="linenumber">26</context>
7793 </context-group>
7794 </trans-unit>
7743 <trans-unit id="3245220240937722814"> 7795 <trans-unit id="3245220240937722814">
7744 <source>My channels</source> 7796 <source>My channels</source>
7745 <target>My channels</target> 7797 <target>My channels</target>
@@ -7834,7 +7886,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7834 <target>Subscribe to the account</target> 7886 <target>Subscribe to the account</target>
7835 7887
7836 7888
7837 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 7889 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
7838 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 7890 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
7839 <context-group purpose="location"> 7891 <context-group purpose="location">
7840 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 7892 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -7881,34 +7933,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
7881 <source>Go to my subscriptions</source> 7933 <source>Go to my subscriptions</source>
7882 <target>Go to my subscriptions</target> 7934 <target>Go to my subscriptions</target>
7883 7935
7884 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 7936 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
7885 <trans-unit id="1136469849928650779"> 7937 <trans-unit id="1136469849928650779">
7886 <source>Go to my videos</source> 7938 <source>Go to my videos</source>
7887 <target>Go to my videos</target> 7939 <target>Go to my videos</target>
7888 7940
7889 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit> 7941 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
7890 <trans-unit id="7836683738999600376"> 7942 <trans-unit id="7836683738999600376">
7891 <source>Go to my imports</source> 7943 <source>Go to my imports</source>
7892 <target>Go to my imports</target> 7944 <target>Go to my imports</target>
7893 7945
7894 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 7946 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
7895 <trans-unit id="7511292153332773503"> 7947 <trans-unit id="7511292153332773503">
7896 <source>Go to my channels</source> 7948 <source>Go to my channels</source>
7897 <target>Go to my channels</target> 7949 <target>Go to my channels</target>
7898 7950
7899 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html"> 7951 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html">
7900 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>. 7952 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7901Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 7953Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7902Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 7954Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
7903 7955
7904 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group></trans-unit> 7956 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
7905 7957
7906 7958
7907 <trans-unit id="375263728166936544"> 7959 <trans-unit id="375263728166936544">
7908 <source>You need to reconnect.</source> 7960 <source>You need to reconnect.</source>
7909 <target>You need to reconnect.</target> 7961 <target>You need to reconnect.</target>
7910 7962
7911 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group></trans-unit> 7963 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
7912 <trans-unit id="2206638022166154361"> 7964 <trans-unit id="2206638022166154361">
7913 <source>Keyboard Shortcuts:</source> 7965 <source>Keyboard Shortcuts:</source>
7914 <target>Keyboard Shortcuts:</target> 7966 <target>Keyboard Shortcuts:</target>
@@ -7919,6 +7971,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7919 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 7971 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7920 <context context-type="linenumber">98</context> 7972 <context context-type="linenumber">98</context>
7921 </context-group> 7973 </context-group>
7974 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
7975 <source>In my library</source><target state="new">In my library</target>
7976 <context-group purpose="location">
7977 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7978 <context context-type="linenumber">104</context>
7979 </context-group>
7922 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 7980 </trans-unit><trans-unit id="232050922346936574" datatype="html">
7923 <source>Trending</source><target state="new">Trending</target> 7981 <source>Trending</source><target state="new">Trending</target>
7924 7982
@@ -7942,38 +8000,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7942 <source>Incorrect username or password.</source> 8000 <source>Incorrect username or password.</source>
7943 <target>Incorrect username or password.</target> 8001 <target>Incorrect username or password.</target>
7944 8002
7945 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 8003 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
7946 <trans-unit id="6974874606619467663" datatype="html"> 8004 <trans-unit id="6974874606619467663" datatype="html">
7947 <source>Your account is blocked.</source> 8005 <source>Your account is blocked.</source>
7948 <target state="new">Your account is blocked.</target> 8006 <target state="new">Your account is blocked.</target>
7949 8007
7950 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 8008 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
7951 8009
7952 <trans-unit id="7939914198003891823" datatype="html"> 8010 <trans-unit id="7939914198003891823" datatype="html">
7953 <source>any language</source> 8011 <source>any language</source>
7954 <target state="new">any language</target> 8012 <target state="new">any language</target>
7955 8013
7956 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8014 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
7957 <trans-unit id="5633144232269377096" datatype="html"> 8015 <trans-unit id="5633144232269377096" datatype="html">
7958 <source>hide</source> 8016 <source>hide</source>
7959 <target state="new">hide</target> 8017 <target state="new">hide</target>
7960 8018
7961 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8019 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
7962 <trans-unit id="8603861867909474404" datatype="html"> 8020 <trans-unit id="8603861867909474404" datatype="html">
7963 <source>blur</source> 8021 <source>blur</source>
7964 <target state="new">blur</target> 8022 <target state="new">blur</target>
7965 8023
7966 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8024 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
7967 <trans-unit id="4534458451100881847" datatype="html"> 8025 <trans-unit id="4534458451100881847" datatype="html">
7968 <source>display</source> 8026 <source>display</source>
7969 <target state="new">display</target> 8027 <target state="new">display</target>
7970 8028
7971 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8029 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
7972 <trans-unit id="4467323362722952678" datatype="html"> 8030 <trans-unit id="4467323362722952678" datatype="html">
7973 <source>Unknown</source> 8031 <source>Unknown</source>
7974 <target state="new">Unknown</target> 8032 <target state="new">Unknown</target>
7975 8033
7976 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8034 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
7977 <trans-unit id="8781423666414310853"> 8035 <trans-unit id="8781423666414310853">
7978 <source>Your password has been successfully reset!</source> 8036 <source>Your password has been successfully reset!</source>
7979 <target>Your password has been successfully reset!</target> 8037 <target>Your password has been successfully reset!</target>
@@ -9517,17 +9575,17 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9517 <x id="PH"/> minutes. 9575 <x id="PH"/> minutes.
9518 </target> 9576 </target>
9519 9577
9520 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 9578 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9521 <trans-unit id="4965472196059235310"> 9579 <trans-unit id="4965472196059235310">
9522 <source>Too many attempts, please try again later.</source> 9580 <source>Too many attempts, please try again later.</source>
9523 <target>Too many attempts, please try again later.</target> 9581 <target>Too many attempts, please try again later.</target>
9524 9582
9525 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group></trans-unit> 9583 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9526 <trans-unit id="1693549688987384699"> 9584 <trans-unit id="1693549688987384699">
9527 <source>Server error. Please retry later.</source> 9585 <source>Server error. Please retry later.</source>
9528 <target>Server error. Please retry later.</target> 9586 <target>Server error. Please retry later.</target>
9529 9587
9530 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 9588 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9531 <trans-unit id="5927402622550505067" datatype="html"> 9589 <trans-unit id="5927402622550505067" datatype="html">
9532 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9590 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9533 <target state="new">Subscribed to all current channels of 9591 <target state="new">Subscribed to all current channels of
@@ -10025,20 +10083,20 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10025 <source>Your video was uploaded to your account and is private.</source> 10083 <source>Your video was uploaded to your account and is private.</source>
10026 <target>Your video was uploaded to your account and is private.</target> 10084 <target>Your video was uploaded to your account and is private.</target>
10027 10085
10028 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10086 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10029 <trans-unit id="5699822024600815733"> 10087 <trans-unit id="5699822024600815733">
10030 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10088 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10031 <target>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target> 10089 <target>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target>
10032 10090
10033 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10091 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10034 <trans-unit id="1219739004043110649"> 10092 <trans-unit id="1219739004043110649">
10035 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10093 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10036 <target>Your video is not uploaded yet, are you sure you want to leave this page?</target> 10094 <target>Your video is not uploaded yet, are you sure you want to leave this page?</target>
10037 10095
10038 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html"> 10096 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html">
10039 <source>Upload</source><target state="new">Upload</target> 10097 <source>Upload</source><target state="new">Upload</target>
10040 10098
10041 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10099 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10042 <trans-unit id="8278735427925094503" datatype="html"> 10100 <trans-unit id="8278735427925094503" datatype="html">
10043 <source>Upload 10101 <source>Upload
10044 <x id="PH"/> 10102 <x id="PH"/>
@@ -10047,13 +10105,13 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10047 <x id="PH"/> 10105 <x id="PH"/>
10048 </target> 10106 </target>
10049 10107
10050 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10108 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10051 10109
10052 <trans-unit id="5981816353437801748"> 10110 <trans-unit id="5981816353437801748">
10053 <source>Video published.</source> 10111 <source>Video published.</source>
10054 <target>Video published.</target> 10112 <target>Video published.</target>
10055 10113
10056 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10114 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10057 10115
10058 10116
10059 <trans-unit id="764164089183618119"> 10117 <trans-unit id="764164089183618119">
@@ -10116,26 +10174,26 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10116 <trans-unit id="961774488937452220" datatype="html"> 10174 <trans-unit id="961774488937452220" datatype="html">
10117 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10175 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10118 10176
10119 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html"> 10177 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html">
10120 <source>Redirection</source><target state="new">Redirection</target> 10178 <source>Redirection</source><target state="new">Redirection</target>
10121 10179
10122 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10180 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10123 10181
10124 <trans-unit id="8858527736400081688"> 10182 <trans-unit id="8858527736400081688">
10125 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10183 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10126 <target>This video contains mature or explicit content. Are you sure you want to watch it?</target> 10184 <target>This video contains mature or explicit content. Are you sure you want to watch it?</target>
10127 10185
10128 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10186 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10129 <trans-unit id="3937119019020041049"> 10187 <trans-unit id="3937119019020041049">
10130 <source>Mature or explicit content</source> 10188 <source>Mature or explicit content</source>
10131 <target>Mature or explicit content</target> 10189 <target>Mature or explicit content</target>
10132 10190
10133 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10191 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10134 <trans-unit id="1755474755114288376" datatype="html"> 10192 <trans-unit id="1755474755114288376" datatype="html">
10135 <source>Up Next</source> 10193 <source>Up Next</source>
10136 <target state="new">Up Next</target> 10194 <target state="new">Up Next</target>
10137 10195
10138 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html"> 10196 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html">
10139 <source>Cancel</source><target state="new">Cancel</target> 10197 <source>Cancel</source><target state="new">Cancel</target>
10140 10198
10141 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit> 10199 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit>
@@ -10143,62 +10201,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10143 <source>Autoplay is suspended</source> 10201 <source>Autoplay is suspended</source>
10144 <target state="new">Autoplay is suspended</target> 10202 <target state="new">Autoplay is suspended</target>
10145 10203
10146 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10204 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10147 <trans-unit id="7895294730547405228" datatype="html"> 10205 <trans-unit id="7895294730547405228" datatype="html">
10148 <source>Enter/exit fullscreen (requires player focus)</source> 10206 <source>Enter/exit fullscreen (requires player focus)</source>
10149 <target state="new">Enter/exit fullscreen (requires player focus)</target> 10207 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10150 10208
10151 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10209 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10152 <trans-unit id="7618388257165864759" datatype="html"> 10210 <trans-unit id="7618388257165864759" datatype="html">
10153 <source>Play/Pause the video (requires player focus)</source> 10211 <source>Play/Pause the video (requires player focus)</source>
10154 <target state="new">Play/Pause the video (requires player focus)</target> 10212 <target state="new">Play/Pause the video (requires player focus)</target>
10155 10213
10156 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10214 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10157 <trans-unit id="7761890399634216630" datatype="html"> 10215 <trans-unit id="7761890399634216630" datatype="html">
10158 <source>Mute/unmute the video (requires player focus)</source> 10216 <source>Mute/unmute the video (requires player focus)</source>
10159 <target state="new">Mute/unmute the video (requires player focus)</target> 10217 <target state="new">Mute/unmute the video (requires player focus)</target>
10160 10218
10161 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10219 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10162 <trans-unit id="5996585232248234904" datatype="html"> 10220 <trans-unit id="5996585232248234904" datatype="html">
10163 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10221 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10164 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10222 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10165 10223
10166 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10224 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10167 <trans-unit id="3748765405903319998" datatype="html"> 10225 <trans-unit id="3748765405903319998" datatype="html">
10168 <source>Increase the volume (requires player focus)</source> 10226 <source>Increase the volume (requires player focus)</source>
10169 <target state="new">Increase the volume (requires player focus)</target> 10227 <target state="new">Increase the volume (requires player focus)</target>
10170 10228
10171 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10229 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10172 <trans-unit id="5810704036407159982" datatype="html"> 10230 <trans-unit id="5810704036407159982" datatype="html">
10173 <source>Decrease the volume (requires player focus)</source> 10231 <source>Decrease the volume (requires player focus)</source>
10174 <target state="new">Decrease the volume (requires player focus)</target> 10232 <target state="new">Decrease the volume (requires player focus)</target>
10175 10233
10176 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10234 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10177 <trans-unit id="2622048822548065691" datatype="html"> 10235 <trans-unit id="2622048822548065691" datatype="html">
10178 <source>Seek the video forward (requires player focus)</source> 10236 <source>Seek the video forward (requires player focus)</source>
10179 <target state="new">Seek the video forward (requires player focus)</target> 10237 <target state="new">Seek the video forward (requires player focus)</target>
10180 10238
10181 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10239 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10182 <trans-unit id="6540078205109221153" datatype="html"> 10240 <trans-unit id="6540078205109221153" datatype="html">
10183 <source>Seek the video backward (requires player focus)</source> 10241 <source>Seek the video backward (requires player focus)</source>
10184 <target state="new">Seek the video backward (requires player focus)</target> 10242 <target state="new">Seek the video backward (requires player focus)</target>
10185 10243
10186 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10244 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10187 <trans-unit id="1956491957766210808" datatype="html"> 10245 <trans-unit id="1956491957766210808" datatype="html">
10188 <source>Increase playback rate (requires player focus)</source> 10246 <source>Increase playback rate (requires player focus)</source>
10189 <target state="new">Increase playback rate (requires player focus)</target> 10247 <target state="new">Increase playback rate (requires player focus)</target>
10190 10248
10191 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10249 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10192 <trans-unit id="5495529997674803186" datatype="html"> 10250 <trans-unit id="5495529997674803186" datatype="html">
10193 <source>Decrease playback rate (requires player focus)</source> 10251 <source>Decrease playback rate (requires player focus)</source>
10194 <target state="new">Decrease playback rate (requires player focus)</target> 10252 <target state="new">Decrease playback rate (requires player focus)</target>
10195 10253
10196 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10254 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10197 <trans-unit id="3178343147230721210" datatype="html"> 10255 <trans-unit id="3178343147230721210" datatype="html">
10198 <source>Navigate in the video frame by frame (requires player focus)</source> 10256 <source>Navigate in the video frame by frame (requires player focus)</source>
10199 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 10257 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10200 10258
10201 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10259 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10202 <trans-unit id="8025996572234182184"> 10260 <trans-unit id="8025996572234182184">
10203 <source>Like the video</source> 10261 <source>Like the video</source>
10204 <target>Like the video</target> 10262 <target>Like the video</target>
diff --git a/client/src/locale/angular.en-US.xlf b/client/src/locale/angular.en-US.xlf
index 707ab9e3b..33c475f96 100644
--- a/client/src/locale/angular.en-US.xlf
+++ b/client/src/locale/angular.en-US.xlf
@@ -194,7 +194,7 @@
194 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 194 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
195 </target> 195 </target>
196 196
197 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html"> 197 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html">
198 <source>My watch history</source><target state="final">My watch history</target> 198 <source>My watch history</source><target state="final">My watch history</target>
199 199
200 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 200 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
@@ -268,12 +268,12 @@
268 268
269 269
270 270
271 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 271 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
272 <trans-unit id="1006562256968398209" datatype="html"> 272 <trans-unit id="1006562256968398209" datatype="html">
273 <source>video</source> 273 <source>video</source>
274 <target state="final">video</target> 274 <target state="final">video</target>
275 275
276 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 276 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
277 <source> The following link contains a private token and should not be shared with anyone. </source><target state="final"> The following link contains a private token and should not be shared with anyone. </target> 277 <source> The following link contains a private token and should not be shared with anyone. </source><target state="final"> The following link contains a private token and should not be shared with anyone. </target>
278 278
279 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 279 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -335,10 +335,10 @@
335 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 335 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
336 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="final">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 336 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="final">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
337 337
338 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 338 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
339 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="final">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 339 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="final">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
340 340
341 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html"> 341 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html">
342 <source>subtitles</source><target state="final">subtitles</target> 342 <source>subtitles</source><target state="final">subtitles</target>
343 343
344 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 344 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
@@ -699,7 +699,7 @@
699 699
700 700
701 701
702 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group></trans-unit> 702 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
703 <trans-unit id="1502595455339510144" datatype="html"> 703 <trans-unit id="1502595455339510144" datatype="html">
704 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 704 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
705 <target state="final"> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></target> 705 <target state="final"> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -777,7 +777,31 @@
777 <source>Federation</source> 777 <source>Federation</source>
778 <target state="final">Federation</target> 778 <target state="final">Federation</target>
779 779
780 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 780 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
781 <source>Following</source><target state="final">Following</target>
782 <context-group purpose="location">
783 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
784 <context context-type="linenumber">29</context>
785 </context-group>
786 <context-group purpose="location">
787 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
788 <context context-type="linenumber">31</context>
789 </context-group>
790 <context-group purpose="location">
791 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
792 <context context-type="linenumber">28</context>
793 </context-group>
794 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
795 <source>Followers</source><target state="final">Followers</target>
796 <context-group purpose="location">
797 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
798 <context context-type="linenumber">34</context>
799 </context-group>
800 <context-group purpose="location">
801 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
802 <context context-type="linenumber">37</context>
803 </context-group>
804 </trans-unit>
781 <trans-unit id="3541687134897970106" datatype="html"> 805 <trans-unit id="3541687134897970106" datatype="html">
782 <source>followers</source> 806 <source>followers</source>
783 <target state="final">followers</target> 807 <target state="final">followers</target>
@@ -839,7 +863,7 @@
839 863
840 864
841 865
842 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 866 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
843 <trans-unit id="3616223838716839702" datatype="html"> 867 <trans-unit id="3616223838716839702" datatype="html">
844 <source>Ban this user</source> 868 <source>Ban this user</source>
845 <target state="final">Ban this user</target> 869 <target state="final">Ban this user</target>
@@ -898,17 +922,11 @@
898 922
899 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html"> 923 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html">
900 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="final"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 924 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="final"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
901 <context-group purpose="location"> 925
902 <context context-type="sourcefile">src/app/+login/login.component.html</context> 926 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7215649348148521605" datatype="html">
903 <context context-type="linenumber">60,62</context>
904 </context-group>
905 </trans-unit><trans-unit id="7215649348148521605" datatype="html">
906 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="final"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 927 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="final"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
907 <context-group purpose="location"> 928
908 <context context-type="sourcefile">src/app/+login/login.component.html</context> 929 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
909 <context context-type="linenumber">65,67</context>
910 </context-group>
911 </trans-unit>
912 <trans-unit id="2392488717875840729" datatype="html"> 930 <trans-unit id="2392488717875840729" datatype="html">
913 <source>User</source> 931 <source>User</source>
914 <target state="final">User</target> 932 <target state="final">User</target>
@@ -919,7 +937,13 @@
919 <source>Username or email address</source> 937 <source>Username or email address</source>
920 <target state="final">Username or email address</target> 938 <target state="final">Username or email address</target>
921 939
922 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit> 940 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="1758058452376026925" datatype="html">
941 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="final"> ⚠️ Most email addresses do not include capital letters. </target>
942 <context-group purpose="location">
943 <context context-type="sourcefile">src/app/+login/login.component.html</context>
944 <context context-type="linenumber">33,34</context>
945 </context-group>
946 </trans-unit>
923 947
924 <trans-unit id="1431416938026210429" datatype="html"> 948 <trans-unit id="1431416938026210429" datatype="html">
925 <source>Password</source> 949 <source>Password</source>
@@ -932,50 +956,44 @@
932 956
933 957
934 958
935 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group></trans-unit> 959 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
936 <trans-unit id="8715156686857791956" datatype="html"> 960 <trans-unit id="8715156686857791956" datatype="html">
937 <source>Click here to reset your password</source> 961 <source>Click here to reset your password</source>
938 <target state="final">Click here to reset your password</target> 962 <target state="final">Click here to reset your password</target>
939 963
940 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html"> 964 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html">
941 <source>I forgot my password</source><target state="final">I forgot my password</target> 965 <source>I forgot my password</source><target state="final">I forgot my password</target>
942 <context-group purpose="location"> 966
943 <context context-type="sourcefile">src/app/+login/login.component.html</context> 967 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="2101170466365500913" datatype="html">
944 <context context-type="linenumber">47</context>
945 </context-group>
946 </trans-unit><trans-unit id="2101170466365500913" datatype="html">
947 <source> Logging into an account lets you publish content </source><target state="final"> Logging into an account lets you publish content </target> 968 <source> Logging into an account lets you publish content </source><target state="final"> Logging into an account lets you publish content </target>
948 <context-group purpose="location"> 969
949 <context context-type="sourcefile">src/app/+login/login.component.html</context> 970 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
950 <context context-type="linenumber">56,57</context>
951 </context-group>
952 </trans-unit>
953 <trans-unit id="2454050363478003966" datatype="html"> 971 <trans-unit id="2454050363478003966" datatype="html">
954 <source>Login</source> 972 <source>Login</source>
955 <target state="final">Login</target> 973 <target state="final">Login</target>
956 974
957 975
958 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 976 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
959 <trans-unit id="3183213940445113677" datatype="html"> 977 <trans-unit id="3183213940445113677" datatype="html">
960 <source>Or sign in with</source> 978 <source>Or sign in with</source>
961 <target state="final">Or sign in with</target> 979 <target state="final">Or sign in with</target>
962 980
963 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit> 981 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
964 <trans-unit id="3238209155172574367" datatype="html"> 982 <trans-unit id="3238209155172574367" datatype="html">
965 <source>Forgot your password</source> 983 <source>Forgot your password</source>
966 <target state="final">Forgot your password</target> 984 <target state="final">Forgot your password</target>
967 985
968 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group></trans-unit> 986 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
969 <trans-unit id="87327320394367488" datatype="html"> 987 <trans-unit id="87327320394367488" datatype="html">
970 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 988 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
971 <target state="final"> 989 <target state="final">
972 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 990 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
973 </target> 991 </target>
974 992
975 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html"> 993 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html">
976 <source> Enter your email address and we will send you a link to reset your password. </source><target state="final"> Enter your email address and we will send you a link to reset your password. </target> 994 <source> Enter your email address and we will send you a link to reset your password. </source><target state="final"> Enter your email address and we will send you a link to reset your password. </target>
977 995
978 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html"> 996 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html">
979 <source>An email with the reset password instructions will be sent to <x id="PH"/>. 997 <source>An email with the reset password instructions will be sent to <x id="PH"/>.
980The link will expire within 1 hour.</source><target state="final">An email with the reset password instructions will be sent to <x id="PH"/>. 998The link will expire within 1 hour.</source><target state="final">An email with the reset password instructions will be sent to <x id="PH"/>.
981The link will expire within 1 hour.</target> 999The link will expire within 1 hour.</target>
@@ -991,17 +1009,17 @@ The link will expire within 1 hour.</target>
991 1009
992 1010
993 1011
994 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group></trans-unit> 1012 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
995 <trans-unit id="3967269098753656610" datatype="html"> 1013 <trans-unit id="3967269098753656610" datatype="html">
996 <source>Email address</source> 1014 <source>Email address</source>
997 <target state="final">Email address</target> 1015 <target state="final">Email address</target>
998 1016
999 1017
1000 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html"> 1018 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html">
1001 <source>Reset</source><target state="final">Reset</target> 1019 <source>Reset</source><target state="final">Reset</target>
1002 1020
1003 <note priority="1" from="description">Password reset button</note> 1021 <note priority="1" from="description">Password reset button</note>
1004 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group></trans-unit> 1022 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1005 1023
1006 1024
1007 <trans-unit id="4319634264526091601" datatype="html"> 1025 <trans-unit id="4319634264526091601" datatype="html">
@@ -1323,7 +1341,7 @@ The link will expire within 1 hour.</target>
1323 <source>Create an account</source> 1341 <source>Create an account</source>
1324 <target state="final">Create an account</target> 1342 <target state="final">Create an account</target>
1325 1343
1326 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1344 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1327 1345
1328 <trans-unit id="3058024914967508975" datatype="html"> 1346 <trans-unit id="3058024914967508975" datatype="html">
1329 <source>My videos</source><target state="final">My videos</target> 1347 <source>My videos</source><target state="final">My videos</target>
@@ -1369,7 +1387,7 @@ The link will expire within 1 hour.</target>
1369 <target state="final">VIDEOS</target> 1387 <target state="final">VIDEOS</target>
1370 1388
1371 1389
1372 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html"> 1390 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html">
1373 <source>Import jobs concurrency</source><target state="final">Import jobs concurrency</target> 1391 <source>Import jobs concurrency</source><target state="final">Import jobs concurrency</target>
1374 1392
1375 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html"> 1393 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html">
@@ -1437,7 +1455,7 @@ The link will expire within 1 hour.</target>
1437 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html"> 1455 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html">
1438 <source>I'm a teapot</source><target state="final">I'm a teapot</target> 1456 <source>I'm a teapot</source><target state="final">I'm a teapot</target>
1439 1457
1440 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html"> 1458 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html">
1441 <source>That's an error.</source><target state="final">That's an error.</target> 1459 <source>That's an error.</source><target state="final">That's an error.</target>
1442 <context-group purpose="location"> 1460 <context-group purpose="location">
1443 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context> 1461 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context>
@@ -1503,7 +1521,7 @@ The link will expire within 1 hour.</target>
1503 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html"> 1521 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html">
1504 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="final">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1522 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="final">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1505 1523
1506 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group></trans-unit> 1524 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1507 1525
1508 <trans-unit id="5131854469652959713" datatype="html"> 1526 <trans-unit id="5131854469652959713" datatype="html">
1509 <source>GLOBAL SEARCH</source> 1527 <source>GLOBAL SEARCH</source>
@@ -2181,7 +2199,7 @@ The link will expire within 1 hour.</target>
2181 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2199 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2182 <source>Upload on hold</source><target state="final">Upload on hold</target> 2200 <source>Upload on hold</source><target state="final">Upload on hold</target>
2183 2201
2184 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2202 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2185 <trans-unit id="285180972645018518" datatype="html"> 2203 <trans-unit id="285180972645018518" datatype="html">
2186 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2204 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2187 <target state="final">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2205 <target state="final">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -2826,11 +2844,7 @@ The link will expire within 1 hour.</target>
2826 <target state="final">ID</target> 2844 <target state="final">ID</target>
2827 2845
2828 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit> 2846 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit>
2829 <trans-unit id="2265605798180116441" datatype="html"> 2847
2830 <source>Follower handle</source>
2831 <target state="final">Follower handle</target>
2832
2833 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
2834 <trans-unit id="5911214550882917183" datatype="html"> 2848 <trans-unit id="5911214550882917183" datatype="html">
2835 <source>State</source> 2849 <source>State</source>
2836 <target state="final">State</target> 2850 <target state="final">State</target>
@@ -2905,11 +2919,7 @@ The link will expire within 1 hour.</target>
2905 </target> 2919 </target>
2906 2920
2907 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit> 2921 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
2908 <trans-unit id="6641024648411549335" datatype="html"> 2922
2909 <source>Host</source>
2910 <target state="final">Host</target>
2911
2912 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
2913 <trans-unit id="6571718060636962350" datatype="html"> 2923 <trans-unit id="6571718060636962350" datatype="html">
2914 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 2924 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
2915 <target state="final">Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 2925 <target state="final">Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -2917,7 +2927,7 @@ The link will expire within 1 hour.</target>
2917 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html"> 2927 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html">
2918 <source>Unfollow</source><target state="final">Unfollow</target> 2928 <source>Unfollow</source><target state="final">Unfollow</target>
2919 2929
2920 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 2930 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
2921 <trans-unit id="8246779176913476983" datatype="html"> 2931 <trans-unit id="8246779176913476983" datatype="html">
2922 <source>Open instance in a new tab</source> 2932 <source>Open instance in a new tab</source>
2923 <target state="final">Open instance in a new tab</target> 2933 <target state="final">Open instance in a new tab</target>
@@ -2929,25 +2939,18 @@ The link will expire within 1 hour.</target>
2929 <source>No host found matching current filters.</source> 2939 <source>No host found matching current filters.</source>
2930 <target state="final">No host found matching current filters.</target> 2940 <target state="final">No host found matching current filters.</target>
2931 2941
2932 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 2942 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
2933 <trans-unit id="7274241885665071790" datatype="html"> 2943 <trans-unit id="7274241885665071790" datatype="html">
2934 <source>Your instance is not following anyone.</source> 2944 <source>Your instance is not following anyone.</source>
2935 <target state="final">Your instance is not following anyone.</target> 2945 <target state="final">Your instance is not following anyone.</target>
2936 2946
2937 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 2947 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
2938 <trans-unit id="4774348799569692380" datatype="html"> 2948 <trans-unit id="4774348799569692380" datatype="html">
2939 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 2949 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
2940 <target state="final">Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</target> 2950 <target state="final">Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</target>
2941 2951
2942 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 2952 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
2943 <trans-unit id="6275803119759621687" datatype="html"> 2953 <trans-unit id="9216117865911519658" datatype="html">
2944 <source>Follow domains</source>
2945 <target state="final">Follow domains</target>
2946
2947 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit><trans-unit id="1268699198448750610" datatype="html">
2948 <source>Follow instances</source><target state="final">Follow instances</target>
2949
2950 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
2951 <source>Action</source><target state="final">Action</target> 2954 <source>Action</source><target state="final">Action</target>
2952 2955
2953 2956
@@ -2997,7 +3000,7 @@ The link will expire within 1 hour.</target>
2997 3000
2998 3001
2999 3002
3000 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html"> 3003 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html">
3001 <source>e.g. jane_doe</source><target state="final">e.g. jane_doe</target> 3004 <source>e.g. jane_doe</source><target state="final">e.g. jane_doe</target>
3002 3005
3003 <note priority="1" from="description">Username choice placeholder in the registration form</note> 3006 <note priority="1" from="description">Username choice placeholder in the registration form</note>
@@ -3029,7 +3032,7 @@ The link will expire within 1 hour.</target>
3029 <target state="final">Role</target> 3032 <target state="final">Role</target>
3030 3033
3031 3034
3032 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group></trans-unit> 3035 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3033 <trans-unit id="7046347992315328430" datatype="html"> 3036 <trans-unit id="7046347992315328430" datatype="html">
3034 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3037 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3035 <target state="final"> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </target> 3038 <target state="final"> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </target>
@@ -3044,15 +3047,9 @@ The link will expire within 1 hour.</target>
3044 3047
3045 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html"> 3048 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html">
3046 <source>Auth plugin</source><target state="final">Auth plugin</target> 3049 <source>Auth plugin</source><target state="final">Auth plugin</target>
3047 <context-group purpose="location"> 3050
3048 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3051
3049 <context context-type="linenumber">188</context> 3052 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit><trans-unit id="588099657508661970" datatype="html">
3050 </context-group>
3051 <context-group purpose="location">
3052 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3053 <context context-type="linenumber">188</context>
3054 </context-group>
3055 </trans-unit><trans-unit id="588099657508661970" datatype="html">
3056 <source>None (local authentication)</source><target state="final">None (local authentication)</target> 3053 <source>None (local authentication)</source><target state="final">None (local authentication)</target>
3057 <context-group purpose="location"> 3054 <context-group purpose="location">
3058 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3055 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
@@ -3269,7 +3266,13 @@ The link will expire within 1 hour.</target>
3269 3266
3270 3267
3271 3268
3272 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="4691552465058437520" datatype="html"> 3269 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3270 <source>Follower</source><target state="final">Follower</target>
3271 <context-group purpose="location">
3272 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3273 <context context-type="linenumber">24</context>
3274 </context-group>
3275 </trans-unit><trans-unit id="4691552465058437520" datatype="html">
3273 <source>Commented video</source><target state="final">Commented video</target> 3276 <source>Commented video</source><target state="final">Commented video</target>
3274 3277
3275 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html"> 3278 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html">
@@ -3555,7 +3558,7 @@ The link will expire within 1 hour.</target>
3555 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 3558 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
3556 </target> 3559 </target>
3557 3560
3558 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3561 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3559 <trans-unit id="4058814854824495833" datatype="html"> 3562 <trans-unit id="4058814854824495833" datatype="html">
3560 <source>Mute domains</source> 3563 <source>Mute domains</source>
3561 <target state="final">Mute domains</target> 3564 <target state="final">Mute domains</target>
@@ -5312,11 +5315,8 @@ color: red;
5312 5315
5313 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5316 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5314 <source>CHANNELS</source><target state="final">CHANNELS</target> 5317 <source>CHANNELS</source><target state="final">CHANNELS</target>
5315 <context-group purpose="location"> 5318
5316 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5319 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5317 <context context-type="linenumber">82</context>
5318 </context-group>
5319 </trans-unit>
5320 5320
5321 <trans-unit id="3666829335406793239" datatype="html"> 5321 <trans-unit id="3666829335406793239" datatype="html">
5322 <source>This account does not have channels.</source> 5322 <source>This account does not have channels.</source>
@@ -5350,7 +5350,13 @@ channel with the same name (<x id="PH_2"/>)!</source><target state="final">Do yo
5350It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 5350It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
5351channel with the same name (<x id="PH_2"/>)!</target> 5351channel with the same name (<x id="PH_2"/>)!</target>
5352 5352
5353 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5353 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5354 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="final">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5355 <context-group purpose="location">
5356 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5357 <context context-type="linenumber">48</context>
5358 </context-group>
5359 </trans-unit>
5354 <trans-unit id="5387007581996837469" datatype="html"> 5360 <trans-unit id="5387007581996837469" datatype="html">
5355 <source>My Channels</source> 5361 <source>My Channels</source>
5356 <target state="final">My Channels</target> 5362 <target state="final">My Channels</target>
@@ -5901,12 +5907,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
5901 <source>Your message has been sent.</source> 5907 <source>Your message has been sent.</source>
5902 <target state="final">Your message has been sent.</target> 5908 <target state="final">Your message has been sent.</target>
5903 5909
5904 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 5910 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
5905 <trans-unit id="2072135752262464360" datatype="html"> 5911 <trans-unit id="2072135752262464360" datatype="html">
5906 <source>You already sent this form recently</source> 5912 <source>You already sent this form recently</source>
5907 <target state="final">You already sent this form recently</target> 5913 <target state="final">You already sent this form recently</target>
5908 5914
5909 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 5915 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
5910 <trans-unit id="819067926858619041" datatype="html"> 5916 <trans-unit id="819067926858619041" datatype="html">
5911 <source>Account videos</source><target state="final">Account videos</target> 5917 <source>Account videos</source><target state="final">Account videos</target>
5912 5918
@@ -5939,10 +5945,10 @@ channel with the same name (<x id="PH_2"/>)!</target>
5939 <x id="PH"/> direct account followers 5945 <x id="PH"/> direct account followers
5940 </target> 5946 </target>
5941 5947
5942 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html"> 5948 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html">
5943 <source>Report this account</source><target state="final">Report this account</target> 5949 <source>Report this account</source><target state="final">Report this account</target>
5944 5950
5945 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 5951 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
5946 5952
5947 <trans-unit id="1504521795586863905" datatype="html"> 5953 <trans-unit id="1504521795586863905" datatype="html">
5948 <source>VIDEOS</source><target state="final">VIDEOS</target> 5954 <source>VIDEOS</source><target state="final">VIDEOS</target>
@@ -5954,24 +5960,16 @@ channel with the same name (<x id="PH_2"/>)!</target>
5954 <target state="final">Username copied</target> 5960 <target state="final">Username copied</target>
5955 5961
5956 5962
5957 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html"> 5963 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html">
5958 <source>1 subscriber</source><target state="final">1 subscriber</target> 5964 <source>1 subscriber</source><target state="final">1 subscriber</target>
5959 5965
5960 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html"> 5966 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html">
5961 <source><x id="PH"/> subscribers</source><target state="final"><x id="PH"/> subscribers</target> 5967 <source><x id="PH"/> subscribers</source><target state="final"><x id="PH"/> subscribers</target>
5962 5968
5963 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 5969 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
5970
5971
5964 5972
5965 <trans-unit id="4682675125751819107" datatype="html">
5966 <source>Instances you follow</source>
5967 <target state="final">Instances you follow</target>
5968
5969 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
5970 <trans-unit id="8899833753704589712" datatype="html">
5971 <source>Instances following you</source>
5972 <target state="final">Instances following you</target>
5973
5974 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
5975 <trans-unit id="1035838766454786107" datatype="html"> 5973 <trans-unit id="1035838766454786107" datatype="html">
5976 <source>Audio-only</source> 5974 <source>Audio-only</source>
5977 <target state="final">Audio-only</target> 5975 <target state="final">Audio-only</target>
@@ -6019,7 +6017,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6019 <source>Auto (via ffmpeg)</source> 6017 <source>Auto (via ffmpeg)</source>
6020 <target state="final">Auto (via ffmpeg)</target> 6018 <target state="final">Auto (via ffmpeg)</target>
6021 6019
6022 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="931255636742351800" datatype="html"> 6020 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="3642770981085338761" datatype="html">
6021 <source>Followers of your instance</source><target state="final">Followers of your instance</target>
6022 <context-group purpose="location">
6023 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6024 <context context-type="linenumber">3</context>
6025 </context-group>
6026 </trans-unit><trans-unit id="931255636742351800" datatype="html">
6023 <source>No limit</source><target state="final">No limit</target> 6027 <source>No limit</source><target state="final">No limit</target>
6024 6028
6025 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html"> 6029 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html">
@@ -6138,17 +6142,33 @@ channel with the same name (<x id="PH_2"/>)!</target>
6138 <source>Domain is required.</source> 6142 <source>Domain is required.</source>
6139 <target state="final">Domain is required.</target> 6143 <target state="final">Domain is required.</target>
6140 6144
6141 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group></trans-unit> 6145 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6142 <trans-unit id="6780793142903080663" datatype="html"> 6146 <source>Hosts entered are invalid.</source><target state="final">Hosts entered are invalid.</target>
6143 <source>Domains entered are invalid.</source> 6147 <context-group purpose="location">
6144 <target state="final">Domains entered are invalid.</target> 6148 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6145 6149 <context context-type="linenumber">93</context>
6146 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6150 </context-group>
6147 <trans-unit id="5886492514458202177" datatype="html"> 6151 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6148 <source>Domains entered contain duplicates.</source> 6152 <source>Hosts entered contain duplicates.</source><target state="final">Hosts entered contain duplicates.</target>
6149 <target state="final">Domains entered contain duplicates.</target> 6153 <context-group purpose="location">
6150 6154 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6151 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 6155 <context context-type="linenumber">94</context>
6156 </context-group>
6157 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6158 <source>Hosts or handles are invalid.</source><target state="final">Hosts or handles are invalid.</target>
6159 <context-group purpose="location">
6160 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6161 <context context-type="linenumber">102</context>
6162 </context-group>
6163 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6164 <source>Hosts or handles contain duplicates.</source><target state="final">Hosts or handles contain duplicates.</target>
6165 <context-group purpose="location">
6166 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6167 <context context-type="linenumber">103</context>
6168 </context-group>
6169 </trans-unit>
6170
6171
6152 <trans-unit id="240806681889331244" datatype="html"> 6172 <trans-unit id="240806681889331244" datatype="html">
6153 <source>Unlimited</source> 6173 <source>Unlimited</source>
6154 <target state="final">Unlimited</target> 6174 <target state="final">Unlimited</target>
@@ -6279,7 +6299,27 @@ channel with the same name (<x id="PH_2"/>)!</target>
6279 <x id="PH"/> removed from instance followers 6299 <x id="PH"/> removed from instance followers
6280 </target> 6300 </target>
6281 6301
6282 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit> 6302 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit><trans-unit id="6018246591673612412" datatype="html">
6303 <source>Follow</source><target state="final">Follow</target>
6304 <context-group purpose="location">
6305 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6306 <context context-type="linenumber">3</context>
6307 </context-group>
6308 <context-group purpose="location">
6309 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6310 <context context-type="linenumber">37</context>
6311 </context-group>
6312 <context-group purpose="location">
6313 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6314 <context context-type="linenumber">18</context>
6315 </context-group>
6316 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6317 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="final">1 host (without "http://"), account handle or channel handle per line</target>
6318 <context-group purpose="location">
6319 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6320 <context context-type="linenumber">11</context>
6321 </context-group>
6322 </trans-unit>
6283 <trans-unit id="2740793005745065895" datatype="html"> 6323 <trans-unit id="2740793005745065895" datatype="html">
6284 <source> 6324 <source>
6285 <x id="PH"/> is not valid 6325 <x id="PH"/> is not valid
@@ -6288,17 +6328,23 @@ channel with the same name (<x id="PH_2"/>)!</target>
6288 <x id="PH"/> is not valid 6328 <x id="PH"/> is not valid
6289 </target> 6329 </target>
6290 6330
6291 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group></trans-unit> 6331 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6292 <trans-unit id="2355066641781598196" datatype="html"> 6332 <trans-unit id="2355066641781598196" datatype="html">
6293 <source>Follow request(s) sent!</source> 6333 <source>Follow request(s) sent!</source>
6294 <target state="final">Follow request(s) sent!</target> 6334 <target state="final">Follow request(s) sent!</target>
6295 6335
6296 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit> 6336 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6337 <source>Your instance subscriptions</source><target state="final">Your instance subscriptions</target>
6338 <context-group purpose="location">
6339 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6340 <context context-type="linenumber">3</context>
6341 </context-group>
6342 </trans-unit>
6297 <trans-unit id="4245720728052819482" datatype="html"> 6343 <trans-unit id="4245720728052819482" datatype="html">
6298 <source>Do you really want to unfollow <x id="PH"/>?</source> 6344 <source>Do you really want to unfollow <x id="PH"/>?</source>
6299 <target state="final">Do you really want to unfollow <x id="PH"/>?</target> 6345 <target state="final">Do you really want to unfollow <x id="PH"/>?</target>
6300 6346
6301 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6347 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6302 <trans-unit id="9160510009013134726" datatype="html"> 6348 <trans-unit id="9160510009013134726" datatype="html">
6303 <source>Unfollow</source> 6349 <source>Unfollow</source>
6304 <target state="final">Unfollow</target> 6350 <target state="final">Unfollow</target>
@@ -6308,7 +6354,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
6308 <source>You are not following <x id="PH"/> anymore.</source> 6354 <source>You are not following <x id="PH"/> anymore.</source>
6309 <target state="final">You are not following <x id="PH"/> anymore.</target> 6355 <target state="final">You are not following <x id="PH"/> anymore.</target>
6310 6356
6311 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 6357 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6312 <trans-unit id="2593763089859685916" datatype="html"> 6358 <trans-unit id="2593763089859685916" datatype="html">
6313 <source>enabled</source> 6359 <source>enabled</source>
6314 <target state="final">enabled</target> 6360 <target state="final">enabled</target>
@@ -6747,7 +6793,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
6747 6793
6748 6794
6749 6795
6750 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit> 6796 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
6751 <trans-unit id="5076187961693950167" datatype="html"> 6797 <trans-unit id="5076187961693950167" datatype="html">
6752 <source>Standard logs</source> 6798 <source>Standard logs</source>
6753 <target state="final">Standard logs</target> 6799 <target state="final">Standard logs</target>
@@ -6781,13 +6827,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
6781 <source>Update user password</source> 6827 <source>Update user password</source>
6782 <target state="final">Update user password</target> 6828 <target state="final">Update user password</target>
6783 6829
6784 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit><trans-unit id="177544274549739411" datatype="html"> 6830 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit>
6785 <source>Following list</source><target state="final">Following list</target>
6786
6787 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group></trans-unit><trans-unit id="8092429110007204784" datatype="html">
6788 <source>Followers list</source><target state="final">Followers list</target>
6789
6790 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group></trans-unit>
6791 <trans-unit id="780323526182667308" datatype="html"> 6831 <trans-unit id="780323526182667308" datatype="html">
6792 <source>User <x id="PH"/> updated.</source> 6832 <source>User <x id="PH"/> updated.</source>
6793 <target state="final">User <x id="PH"/> updated.</target> 6833 <target state="final">User <x id="PH"/> updated.</target>
@@ -6814,13 +6854,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
6814 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html"> 6854 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html">
6815 <source>Federation</source><target state="final">Federation</target> 6855 <source>Federation</source><target state="final">Federation</target>
6816 6856
6817 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="4682675125751819107" datatype="html"> 6857 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit>
6818 <source>Instances you follow</source><target state="final">Instances you follow</target>
6819
6820 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group></trans-unit><trans-unit id="8899833753704589712" datatype="html">
6821 <source>Instances following you</source><target state="final">Instances following you</target>
6822
6823 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit>
6824 <trans-unit id="3767259920053407667" datatype="html"> 6858 <trans-unit id="3767259920053407667" datatype="html">
6825 <source>Videos will be deleted, comments will be tombstoned.</source> 6859 <source>Videos will be deleted, comments will be tombstoned.</source>
6826 <target state="final">Videos will be deleted, comments will be tombstoned.</target> 6860 <target state="final">Videos will be deleted, comments will be tombstoned.</target>
@@ -6848,7 +6882,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
6848 <target state="final">Set Email as Verified</target> 6882 <target state="final">Set Email as Verified</target>
6849 6883
6850 6884
6851 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 6885 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
6886 <source>Created</source><target state="final">Created</target>
6887 <context-group purpose="location">
6888 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
6889 <context context-type="linenumber">115</context>
6890 </context-group>
6891 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
6892 <source>Daily quota</source><target state="final">Daily quota</target>
6893 <context-group purpose="location">
6894 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
6895 <context context-type="linenumber">120</context>
6896 </context-group>
6897 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
6898 <source>Last login</source><target state="final">Last login</target>
6899 <context-group purpose="location">
6900 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
6901 <context context-type="linenumber">122</context>
6902 </context-group>
6903 </trans-unit>
6852 <trans-unit id="3403978719736970622" datatype="html"> 6904 <trans-unit id="3403978719736970622" datatype="html">
6853 <source>You cannot ban root.</source> 6905 <source>You cannot ban root.</source>
6854 <target state="final">You cannot ban root.</target> 6906 <target state="final">You cannot ban root.</target>
@@ -7145,12 +7197,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
7145 <source>Video channel <x id="PH"/> created.</source> 7197 <source>Video channel <x id="PH"/> created.</source>
7146 <target state="final">Video channel <x id="PH"/> created.</target> 7198 <target state="final">Video channel <x id="PH"/> created.</target>
7147 7199
7148 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7200 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7149 <trans-unit id="8723777130353305761" datatype="html"> 7201 <trans-unit id="8723777130353305761" datatype="html">
7150 <source>This name already exists on this instance.</source> 7202 <source>This name already exists on this instance.</source>
7151 <target state="final">This name already exists on this instance.</target> 7203 <target state="final">This name already exists on this instance.</target>
7152 7204
7153 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7205 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7154 <trans-unit id="7589345916094713536" datatype="html"> 7206 <trans-unit id="7589345916094713536" datatype="html">
7155 <source>Video channel <x id="PH"/> updated.</source> 7207 <source>Video channel <x id="PH"/> updated.</source>
7156 <target state="final">Video channel <x id="PH"/> updated.</target> 7208 <target state="final">Video channel <x id="PH"/> updated.</target>
@@ -7165,11 +7217,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7165 <source>Banner deleted.</source><target state="final">Banner deleted.</target> 7217 <source>Banner deleted.</source><target state="final">Banner deleted.</target>
7166 7218
7167 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7219 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7168 <trans-unit id="2575302837003821736" datatype="html"> 7220
7169 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7170 <target state="final">Please type the display name of the video channel (<x id="PH"/>) to confirm</target>
7171
7172 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7173 <trans-unit id="624066830180032195" datatype="html"> 7221 <trans-unit id="624066830180032195" datatype="html">
7174 <source>Video channel <x id="PH"/> deleted.</source> 7222 <source>Video channel <x id="PH"/> deleted.</source>
7175 <target state="final">Video channel <x id="PH"/> deleted.</target> 7223 <target state="final">Video channel <x id="PH"/> deleted.</target>
@@ -7298,7 +7346,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
7298 <source>Ownership change request sent.</source> 7346 <source>Ownership change request sent.</source>
7299 <target state="final">Ownership change request sent.</target> 7347 <target state="final">Ownership change request sent.</target>
7300 7348
7301 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 7349 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7699622144571229146" datatype="html">
7350 <source>Sort by</source><target state="final">Sort by</target>
7351 <context-group purpose="location">
7352 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7353 <context context-type="linenumber">26</context>
7354 </context-group>
7355 </trans-unit>
7302 <trans-unit id="3245220240937722814" datatype="html"> 7356 <trans-unit id="3245220240937722814" datatype="html">
7303 <source>My channels</source> 7357 <source>My channels</source>
7304 <target state="final">My channels</target> 7358 <target state="final">My channels</target>
@@ -7389,7 +7443,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7389 <target state="final">Subscribe to the account</target> 7443 <target state="final">Subscribe to the account</target>
7390 7444
7391 7445
7392 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 7446 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
7393 <source>PLAYLISTS</source><target state="final">PLAYLISTS</target> 7447 <source>PLAYLISTS</source><target state="final">PLAYLISTS</target>
7394 <context-group purpose="location"> 7448 <context-group purpose="location">
7395 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 7449 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -7436,34 +7490,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
7436 <source>Go to my subscriptions</source> 7490 <source>Go to my subscriptions</source>
7437 <target state="final">Go to my subscriptions</target> 7491 <target state="final">Go to my subscriptions</target>
7438 7492
7439 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 7493 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
7440 <trans-unit id="1136469849928650779" datatype="html"> 7494 <trans-unit id="1136469849928650779" datatype="html">
7441 <source>Go to my videos</source> 7495 <source>Go to my videos</source>
7442 <target state="final">Go to my videos</target> 7496 <target state="final">Go to my videos</target>
7443 7497
7444 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit> 7498 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
7445 <trans-unit id="7836683738999600376" datatype="html"> 7499 <trans-unit id="7836683738999600376" datatype="html">
7446 <source>Go to my imports</source> 7500 <source>Go to my imports</source>
7447 <target state="final">Go to my imports</target> 7501 <target state="final">Go to my imports</target>
7448 7502
7449 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 7503 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
7450 <trans-unit id="7511292153332773503" datatype="html"> 7504 <trans-unit id="7511292153332773503" datatype="html">
7451 <source>Go to my channels</source> 7505 <source>Go to my channels</source>
7452 <target state="final">Go to my channels</target> 7506 <target state="final">Go to my channels</target>
7453 7507
7454 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html"> 7508 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html">
7455 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>. 7509 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7456Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="final">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 7510Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="final">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7457Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 7511Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
7458 7512
7459 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group></trans-unit> 7513 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
7460 7514
7461 7515
7462 <trans-unit id="375263728166936544" datatype="html"> 7516 <trans-unit id="375263728166936544" datatype="html">
7463 <source>You need to reconnect.</source> 7517 <source>You need to reconnect.</source>
7464 <target state="final">You need to reconnect.</target> 7518 <target state="final">You need to reconnect.</target>
7465 7519
7466 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group></trans-unit> 7520 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
7467 <trans-unit id="2206638022166154361" datatype="html"> 7521 <trans-unit id="2206638022166154361" datatype="html">
7468 <source>Keyboard Shortcuts:</source> 7522 <source>Keyboard Shortcuts:</source>
7469 <target state="final">Keyboard Shortcuts:</target> 7523 <target state="final">Keyboard Shortcuts:</target>
@@ -7474,6 +7528,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7474 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 7528 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7475 <context context-type="linenumber">98</context> 7529 <context context-type="linenumber">98</context>
7476 </context-group> 7530 </context-group>
7531 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
7532 <source>In my library</source><target state="final">In my library</target>
7533 <context-group purpose="location">
7534 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7535 <context context-type="linenumber">104</context>
7536 </context-group>
7477 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 7537 </trans-unit><trans-unit id="232050922346936574" datatype="html">
7478 <source>Trending</source><target state="final">Trending</target> 7538 <source>Trending</source><target state="final">Trending</target>
7479 7539
@@ -7497,38 +7557,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7497 <source>Incorrect username or password.</source> 7557 <source>Incorrect username or password.</source>
7498 <target state="final">Incorrect username or password.</target> 7558 <target state="final">Incorrect username or password.</target>
7499 7559
7500 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 7560 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
7501 <trans-unit id="6974874606619467663" datatype="html"> 7561 <trans-unit id="6974874606619467663" datatype="html">
7502 <source>Your account is blocked.</source> 7562 <source>Your account is blocked.</source>
7503 <target state="final">Your account is blocked.</target> 7563 <target state="final">Your account is blocked.</target>
7504 7564
7505 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 7565 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
7506 7566
7507 <trans-unit id="7939914198003891823" datatype="html"> 7567 <trans-unit id="7939914198003891823" datatype="html">
7508 <source>any language</source> 7568 <source>any language</source>
7509 <target state="final">any language</target> 7569 <target state="final">any language</target>
7510 7570
7511 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 7571 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
7512 <trans-unit id="5633144232269377096" datatype="html"> 7572 <trans-unit id="5633144232269377096" datatype="html">
7513 <source>hide</source> 7573 <source>hide</source>
7514 <target state="final">hide</target> 7574 <target state="final">hide</target>
7515 7575
7516 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 7576 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
7517 <trans-unit id="8603861867909474404" datatype="html"> 7577 <trans-unit id="8603861867909474404" datatype="html">
7518 <source>blur</source> 7578 <source>blur</source>
7519 <target state="final">blur</target> 7579 <target state="final">blur</target>
7520 7580
7521 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 7581 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
7522 <trans-unit id="4534458451100881847" datatype="html"> 7582 <trans-unit id="4534458451100881847" datatype="html">
7523 <source>display</source> 7583 <source>display</source>
7524 <target state="final">display</target> 7584 <target state="final">display</target>
7525 7585
7526 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 7586 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
7527 <trans-unit id="4467323362722952678" datatype="html"> 7587 <trans-unit id="4467323362722952678" datatype="html">
7528 <source>Unknown</source> 7588 <source>Unknown</source>
7529 <target state="final">Unknown</target> 7589 <target state="final">Unknown</target>
7530 7590
7531 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 7591 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
7532 <trans-unit id="8781423666414310853" datatype="html"> 7592 <trans-unit id="8781423666414310853" datatype="html">
7533 <source>Your password has been successfully reset!</source> 7593 <source>Your password has been successfully reset!</source>
7534 <target state="final">Your password has been successfully reset!</target> 7594 <target state="final">Your password has been successfully reset!</target>
@@ -9048,17 +9108,17 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9048 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9108 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9049 <target state="final">Too many attempts, please try again after <x id="PH"/> minutes.</target> 9109 <target state="final">Too many attempts, please try again after <x id="PH"/> minutes.</target>
9050 9110
9051 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 9111 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9052 <trans-unit id="4965472196059235310" datatype="html"> 9112 <trans-unit id="4965472196059235310" datatype="html">
9053 <source>Too many attempts, please try again later.</source> 9113 <source>Too many attempts, please try again later.</source>
9054 <target state="final">Too many attempts, please try again later.</target> 9114 <target state="final">Too many attempts, please try again later.</target>
9055 9115
9056 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group></trans-unit> 9116 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9057 <trans-unit id="1693549688987384699" datatype="html"> 9117 <trans-unit id="1693549688987384699" datatype="html">
9058 <source>Server error. Please retry later.</source> 9118 <source>Server error. Please retry later.</source>
9059 <target state="final">Server error. Please retry later.</target> 9119 <target state="final">Server error. Please retry later.</target>
9060 9120
9061 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 9121 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9062 <trans-unit id="5927402622550505067" datatype="html"> 9122 <trans-unit id="5927402622550505067" datatype="html">
9063 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9123 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9064 <target state="final">Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</target> 9124 <target state="final">Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</target>
@@ -9549,20 +9609,20 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9549 <source>Your video was uploaded to your account and is private.</source> 9609 <source>Your video was uploaded to your account and is private.</source>
9550 <target state="final">Your video was uploaded to your account and is private.</target> 9610 <target state="final">Your video was uploaded to your account and is private.</target>
9551 9611
9552 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 9612 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
9553 <trans-unit id="5699822024600815733" datatype="html"> 9613 <trans-unit id="5699822024600815733" datatype="html">
9554 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 9614 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
9555 <target state="final">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target> 9615 <target state="final">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target>
9556 9616
9557 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 9617 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
9558 <trans-unit id="1219739004043110649" datatype="html"> 9618 <trans-unit id="1219739004043110649" datatype="html">
9559 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 9619 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
9560 <target state="final">Your video is not uploaded yet, are you sure you want to leave this page?</target> 9620 <target state="final">Your video is not uploaded yet, are you sure you want to leave this page?</target>
9561 9621
9562 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html"> 9622 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html">
9563 <source>Upload</source><target state="final">Upload</target> 9623 <source>Upload</source><target state="final">Upload</target>
9564 9624
9565 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 9625 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
9566 <trans-unit id="8278735427925094503" datatype="html"> 9626 <trans-unit id="8278735427925094503" datatype="html">
9567 <source>Upload 9627 <source>Upload
9568 <x id="PH"/> 9628 <x id="PH"/>
@@ -9571,13 +9631,13 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9571 <x id="PH"/> 9631 <x id="PH"/>
9572 </target> 9632 </target>
9573 9633
9574 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 9634 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
9575 9635
9576 <trans-unit id="5981816353437801748" datatype="html"> 9636 <trans-unit id="5981816353437801748" datatype="html">
9577 <source>Video published.</source> 9637 <source>Video published.</source>
9578 <target state="final">Video published.</target> 9638 <target state="final">Video published.</target>
9579 9639
9580 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 9640 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
9581 9641
9582 9642
9583 <trans-unit id="764164089183618119" datatype="html"> 9643 <trans-unit id="764164089183618119" datatype="html">
@@ -9640,26 +9700,26 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9640 <trans-unit id="961774488937452220" datatype="html"> 9700 <trans-unit id="961774488937452220" datatype="html">
9641 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="final">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 9701 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="final">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
9642 9702
9643 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html"> 9703 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html">
9644 <source>Redirection</source><target state="final">Redirection</target> 9704 <source>Redirection</source><target state="final">Redirection</target>
9645 9705
9646 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 9706 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
9647 9707
9648 <trans-unit id="8858527736400081688" datatype="html"> 9708 <trans-unit id="8858527736400081688" datatype="html">
9649 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 9709 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
9650 <target state="final">This video contains mature or explicit content. Are you sure you want to watch it?</target> 9710 <target state="final">This video contains mature or explicit content. Are you sure you want to watch it?</target>
9651 9711
9652 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 9712 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
9653 <trans-unit id="3937119019020041049" datatype="html"> 9713 <trans-unit id="3937119019020041049" datatype="html">
9654 <source>Mature or explicit content</source> 9714 <source>Mature or explicit content</source>
9655 <target state="final">Mature or explicit content</target> 9715 <target state="final">Mature or explicit content</target>
9656 9716
9657 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 9717 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
9658 <trans-unit id="1755474755114288376" datatype="html"> 9718 <trans-unit id="1755474755114288376" datatype="html">
9659 <source>Up Next</source> 9719 <source>Up Next</source>
9660 <target state="final">Up Next</target> 9720 <target state="final">Up Next</target>
9661 9721
9662 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html"> 9722 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html">
9663 <source>Cancel</source><target state="final">Cancel</target> 9723 <source>Cancel</source><target state="final">Cancel</target>
9664 9724
9665 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit> 9725 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit>
@@ -9667,62 +9727,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9667 <source>Autoplay is suspended</source> 9727 <source>Autoplay is suspended</source>
9668 <target state="final">Autoplay is suspended</target> 9728 <target state="final">Autoplay is suspended</target>
9669 9729
9670 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 9730 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
9671 <trans-unit id="7895294730547405228" datatype="html"> 9731 <trans-unit id="7895294730547405228" datatype="html">
9672 <source>Enter/exit fullscreen (requires player focus)</source> 9732 <source>Enter/exit fullscreen (requires player focus)</source>
9673 <target state="final">Enter/exit fullscreen (requires player focus)</target> 9733 <target state="final">Enter/exit fullscreen (requires player focus)</target>
9674 9734
9675 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 9735 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
9676 <trans-unit id="7618388257165864759" datatype="html"> 9736 <trans-unit id="7618388257165864759" datatype="html">
9677 <source>Play/Pause the video (requires player focus)</source> 9737 <source>Play/Pause the video (requires player focus)</source>
9678 <target state="final">Play/Pause the video (requires player focus)</target> 9738 <target state="final">Play/Pause the video (requires player focus)</target>
9679 9739
9680 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 9740 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
9681 <trans-unit id="7761890399634216630" datatype="html"> 9741 <trans-unit id="7761890399634216630" datatype="html">
9682 <source>Mute/unmute the video (requires player focus)</source> 9742 <source>Mute/unmute the video (requires player focus)</source>
9683 <target state="final">Mute/unmute the video (requires player focus)</target> 9743 <target state="final">Mute/unmute the video (requires player focus)</target>
9684 9744
9685 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 9745 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
9686 <trans-unit id="5996585232248234904" datatype="html"> 9746 <trans-unit id="5996585232248234904" datatype="html">
9687 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 9747 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
9688 <target state="final">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 9748 <target state="final">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
9689 9749
9690 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 9750 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
9691 <trans-unit id="3748765405903319998" datatype="html"> 9751 <trans-unit id="3748765405903319998" datatype="html">
9692 <source>Increase the volume (requires player focus)</source> 9752 <source>Increase the volume (requires player focus)</source>
9693 <target state="final">Increase the volume (requires player focus)</target> 9753 <target state="final">Increase the volume (requires player focus)</target>
9694 9754
9695 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 9755 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
9696 <trans-unit id="5810704036407159982" datatype="html"> 9756 <trans-unit id="5810704036407159982" datatype="html">
9697 <source>Decrease the volume (requires player focus)</source> 9757 <source>Decrease the volume (requires player focus)</source>
9698 <target state="final">Decrease the volume (requires player focus)</target> 9758 <target state="final">Decrease the volume (requires player focus)</target>
9699 9759
9700 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 9760 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
9701 <trans-unit id="2622048822548065691" datatype="html"> 9761 <trans-unit id="2622048822548065691" datatype="html">
9702 <source>Seek the video forward (requires player focus)</source> 9762 <source>Seek the video forward (requires player focus)</source>
9703 <target state="final">Seek the video forward (requires player focus)</target> 9763 <target state="final">Seek the video forward (requires player focus)</target>
9704 9764
9705 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 9765 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
9706 <trans-unit id="6540078205109221153" datatype="html"> 9766 <trans-unit id="6540078205109221153" datatype="html">
9707 <source>Seek the video backward (requires player focus)</source> 9767 <source>Seek the video backward (requires player focus)</source>
9708 <target state="final">Seek the video backward (requires player focus)</target> 9768 <target state="final">Seek the video backward (requires player focus)</target>
9709 9769
9710 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 9770 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
9711 <trans-unit id="1956491957766210808" datatype="html"> 9771 <trans-unit id="1956491957766210808" datatype="html">
9712 <source>Increase playback rate (requires player focus)</source> 9772 <source>Increase playback rate (requires player focus)</source>
9713 <target state="final">Increase playback rate (requires player focus)</target> 9773 <target state="final">Increase playback rate (requires player focus)</target>
9714 9774
9715 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 9775 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
9716 <trans-unit id="5495529997674803186" datatype="html"> 9776 <trans-unit id="5495529997674803186" datatype="html">
9717 <source>Decrease playback rate (requires player focus)</source> 9777 <source>Decrease playback rate (requires player focus)</source>
9718 <target state="final">Decrease playback rate (requires player focus)</target> 9778 <target state="final">Decrease playback rate (requires player focus)</target>
9719 9779
9720 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 9780 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
9721 <trans-unit id="3178343147230721210" datatype="html"> 9781 <trans-unit id="3178343147230721210" datatype="html">
9722 <source>Navigate in the video frame by frame (requires player focus)</source> 9782 <source>Navigate in the video frame by frame (requires player focus)</source>
9723 <target state="final">Navigate in the video frame by frame (requires player focus)</target> 9783 <target state="final">Navigate in the video frame by frame (requires player focus)</target>
9724 9784
9725 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 9785 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
9726 <trans-unit id="8025996572234182184" datatype="html"> 9786 <trans-unit id="8025996572234182184" datatype="html">
9727 <source>Like the video</source> 9787 <source>Like the video</source>
9728 <target state="final">Like the video</target> 9788 <target state="final">Like the video</target>
diff --git a/client/src/locale/angular.eo.xlf b/client/src/locale/angular.eo.xlf
index 884779094..e18655e10 100644
--- a/client/src/locale/angular.eo.xlf
+++ b/client/src/locale/angular.eo.xlf
@@ -160,7 +160,7 @@
160 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 160 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
161 </target> 161 </target>
162 162
163 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 163 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
164 <trans-unit id="1486537403020619891" datatype="html"> 164 <trans-unit id="1486537403020619891" datatype="html">
165 <source>My watch history</source> 165 <source>My watch history</source>
166 <target state="translated">Historio de mia spektado</target> 166 <target state="translated">Historio de mia spektado</target>
@@ -235,12 +235,12 @@
235 235
236 236
237 237
238 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 238 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
239 <trans-unit id="1006562256968398209" datatype="html"> 239 <trans-unit id="1006562256968398209" datatype="html">
240 <source>video</source> 240 <source>video</source>
241 <target state="translated">filmo</target> 241 <target state="translated">filmo</target>
242 242
243 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 243 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
244 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 244 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
245 245
246 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 246 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -304,10 +304,10 @@
304 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 304 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
305 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 305 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
306 306
307 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 307 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
308 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 308 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
309 309
310 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 310 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
311 311
312 <trans-unit id="5235042777215655908" datatype="html"> 312 <trans-unit id="5235042777215655908" datatype="html">
313 <source>subtitles</source> 313 <source>subtitles</source>
@@ -700,10 +700,10 @@
700 <trans-unit id="2602586221576511475"> 700 <trans-unit id="2602586221576511475">
701 <source>Video quota</source> 701 <source>Video quota</source>
702 <target>Datumlimo por filmoj</target> 702 <target>Datumlimo por filmoj</target>
703 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 703
704 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 704
705 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 705
706 </trans-unit> 706 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
707 <trans-unit id="1502595455339510144"> 707 <trans-unit id="1502595455339510144">
708 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 708 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
709 <target>Senlima <x id="START_TAG_NG_CONTAINER"/>(po <x id="INTERPOLATION"/> tage) <x id="CLOSE_TAG_NG_CONTAINER"/></target> 709 <target>Senlima <x id="START_TAG_NG_CONTAINER"/>(po <x id="INTERPOLATION"/> tage) <x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -782,7 +782,31 @@
782 <source>Federation</source> 782 <source>Federation</source>
783 <target>Federaĵo</target> 783 <target>Federaĵo</target>
784 784
785 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 785 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
786 <source>Following</source><target state="new">Following</target>
787 <context-group purpose="location">
788 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
789 <context context-type="linenumber">29</context>
790 </context-group>
791 <context-group purpose="location">
792 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
793 <context context-type="linenumber">31</context>
794 </context-group>
795 <context-group purpose="location">
796 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
797 <context context-type="linenumber">28</context>
798 </context-group>
799 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
800 <source>Followers</source><target state="new">Followers</target>
801 <context-group purpose="location">
802 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
803 <context context-type="linenumber">34</context>
804 </context-group>
805 <context-group purpose="location">
806 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
807 <context context-type="linenumber">37</context>
808 </context-group>
809 </trans-unit>
786 <trans-unit id="3541687134897970106"> 810 <trans-unit id="3541687134897970106">
787 <source>followers</source> 811 <source>followers</source>
788 <target>abonantoj</target> 812 <target>abonantoj</target>
@@ -857,7 +881,7 @@
857 881
858 882
859 883
860 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 884 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
861 <trans-unit id="3616223838716839702"> 885 <trans-unit id="3616223838716839702">
862 <source>Ban this user</source> 886 <source>Ban this user</source>
863 <target>Forbari ĉi tiun uzanton</target> 887 <target>Forbari ĉi tiun uzanton</target>
@@ -923,19 +947,13 @@
923 <trans-unit id="7252854992688790751" datatype="html"> 947 <trans-unit id="7252854992688790751" datatype="html">
924 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 948 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
925 <target state="translated">Ĉi tiu nodo permesas registriĝojn. Tamen zorge tralegu <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>la uzokondiĉojn<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>uzokondiĉojn<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> antaŭ ol vi kreos konton. Eble vi ankaŭ povus trovi nodon, kiu precize konvenos viajn bezonojn, per: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 949 <target state="translated">Ĉi tiu nodo permesas registriĝojn. Tamen zorge tralegu <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>la uzokondiĉojn<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>uzokondiĉojn<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> antaŭ ol vi kreos konton. Eble vi ankaŭ povus trovi nodon, kiu precize konvenos viajn bezonojn, per: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
926 <context-group purpose="location"> 950
927 <context context-type="sourcefile">src/app/+login/login.component.html</context> 951 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
928 <context context-type="linenumber">60,62</context>
929 </context-group>
930 </trans-unit>
931 <trans-unit id="7215649348148521605" datatype="html"> 952 <trans-unit id="7215649348148521605" datatype="html">
932 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 953 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
933 <target state="translated">Ĉi tiu nodo nun ne permesas registriĝon de uzantoj. Vi povas malkovri pliajn detalojn per <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>la uzokondiĉoj<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> aŭ trovi nodon, kiu ebligas registradon de kontoj kaj alŝutadon de filmoj. Trovu la vian inter pluraj nodoj per: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 954 <target state="translated">Ĉi tiu nodo nun ne permesas registriĝon de uzantoj. Vi povas malkovri pliajn detalojn per <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>la uzokondiĉoj<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> aŭ trovi nodon, kiu ebligas registradon de kontoj kaj alŝutadon de filmoj. Trovu la vian inter pluraj nodoj per: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
934 <context-group purpose="location"> 955
935 <context context-type="sourcefile">src/app/+login/login.component.html</context> 956 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
936 <context context-type="linenumber">65,67</context>
937 </context-group>
938 </trans-unit>
939 <trans-unit id="2392488717875840729"> 957 <trans-unit id="2392488717875840729">
940 <source>User</source> 958 <source>User</source>
941 <target>Uzanto</target> 959 <target>Uzanto</target>
@@ -946,68 +964,68 @@
946 <source>Username or email address</source> 964 <source>Username or email address</source>
947 <target>Salutnomo aŭ retpoŝtadreso</target> 965 <target>Salutnomo aŭ retpoŝtadreso</target>
948 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 966 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
967 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
968 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
969 <context-group purpose="location">
970 <context context-type="sourcefile">src/app/+login/login.component.html</context>
971 <context context-type="linenumber">33,34</context>
972 </context-group>
949 </trans-unit> 973 </trans-unit>
950 <trans-unit id="1431416938026210429"> 974 <trans-unit id="1431416938026210429">
951 <source>Password</source> 975 <source>Password</source>
952 <target>Pasvorto</target> 976 <target>Pasvorto</target>
953 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 977
954 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 978
955 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 979
956 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 980
957 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 981
958 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 982
959 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 983
960 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 984
961 </trans-unit> 985 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
962 <trans-unit id="8715156686857791956" datatype="html"> 986 <trans-unit id="8715156686857791956" datatype="html">
963 <source>Click here to reset your password</source> 987 <source>Click here to reset your password</source>
964 <target state="translated">Klaku ĉi tien por restarigi vian pasvorton</target> 988 <target state="translated">Klaku ĉi tien por restarigi vian pasvorton</target>
965 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 989
966 </trans-unit> 990 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
967 <trans-unit id="892063502898494584" datatype="html"> 991 <trans-unit id="892063502898494584" datatype="html">
968 <source>I forgot my password</source> 992 <source>I forgot my password</source>
969 <target state="translated">Mi forgesis pasvorton</target> 993 <target state="translated">Mi forgesis pasvorton</target>
970 <context-group purpose="location"> 994
971 <context context-type="sourcefile">src/app/+login/login.component.html</context> 995 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
972 <context context-type="linenumber">47</context>
973 </context-group>
974 </trans-unit>
975 <trans-unit id="2101170466365500913" datatype="html"> 996 <trans-unit id="2101170466365500913" datatype="html">
976 <source>Logging into an account lets you publish content</source> 997 <source>Logging into an account lets you publish content</source>
977 <target state="translated">Salutinte vian konton, vi povas publikigi enhavon</target> 998 <target state="translated">Salutinte vian konton, vi povas publikigi enhavon</target>
978 <context-group purpose="location"> 999
979 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1000 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
980 <context context-type="linenumber">56,57</context>
981 </context-group>
982 </trans-unit>
983 <trans-unit id="2454050363478003966"> 1001 <trans-unit id="2454050363478003966">
984 <source>Login</source> 1002 <source>Login</source>
985 <target>Saluti</target> 1003 <target>Saluti</target>
986 1004
987 1005
988 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1006 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
989 <trans-unit id="3183213940445113677" datatype="html"> 1007 <trans-unit id="3183213940445113677" datatype="html">
990 <source>Or sign in with</source> 1008 <source>Or sign in with</source>
991 <target state="translated">Aŭ saluti per</target> 1009 <target state="translated">Aŭ saluti per</target>
992 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1010
993 </trans-unit> 1011 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
994 <trans-unit id="3238209155172574367"> 1012 <trans-unit id="3238209155172574367">
995 <source>Forgot your password</source> 1013 <source>Forgot your password</source>
996 <target>Forgesita pasvorto</target> 1014 <target>Forgesita pasvorto</target>
997 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1015
998 </trans-unit> 1016 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
999 <trans-unit id="87327320394367488"> 1017 <trans-unit id="87327320394367488">
1000 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1018 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1001 <target> 1019 <target>
1002 Ni pardonpetas; vi ne povas rehavi vian pasvorton, ĉar la administranto de via nodo ne agordis la retpoŝtan sistemon de PeerTube. 1020 Ni pardonpetas; vi ne povas rehavi vian pasvorton, ĉar la administranto de via nodo ne agordis la retpoŝtan sistemon de PeerTube.
1003 </target> 1021 </target>
1004 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1022
1005 </trans-unit> 1023 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1006 <trans-unit id="3188014010833256853" datatype="html"> 1024 <trans-unit id="3188014010833256853" datatype="html">
1007 <source>Enter your email address and we will send you a link to reset your password.</source> 1025 <source>Enter your email address and we will send you a link to reset your password.</source>
1008 <target state="translated">Enigu vian retpoŝtadreson kaj ni sendos al vi retleteron por restarigi vian pasvorton.</target> 1026 <target state="translated">Enigu vian retpoŝtadreson kaj ni sendos al vi retleteron por restarigi vian pasvorton.</target>
1009 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1027
1010 </trans-unit> 1028 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1011 <trans-unit id="1190256911880544559" datatype="html"> 1029 <trans-unit id="1190256911880544559" datatype="html">
1012 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1030 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1013The link will expire within 1 hour.</source> 1031The link will expire within 1 hour.</source>
@@ -1017,26 +1035,26 @@ The link will expire within 1 hour.</source>
1017 <trans-unit id="4768749765465246664"> 1035 <trans-unit id="4768749765465246664">
1018 <source>Email</source> 1036 <source>Email</source>
1019 <target>Retpoŝtadreso</target> 1037 <target>Retpoŝtadreso</target>
1020 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1038
1021 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1039
1022 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1040
1023 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1041
1024 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1042
1025 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1043
1026 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1044
1027 </trans-unit> 1045 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1028 <trans-unit id="3967269098753656610"> 1046 <trans-unit id="3967269098753656610">
1029 <source>Email address</source> 1047 <source>Email address</source>
1030 <target>Retpoŝtadreso</target> 1048 <target>Retpoŝtadreso</target>
1031 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1049
1032 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1050
1033 </trans-unit> 1051 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1034 <trans-unit id="7808756054397155068" datatype="html"> 1052 <trans-unit id="7808756054397155068" datatype="html">
1035 <source>Reset</source> 1053 <source>Reset</source>
1036 <target state="translated">Restarigi</target> 1054 <target state="translated">Restarigi</target>
1037 <note priority="1" from="description">Password reset button</note> 1055 <note priority="1" from="description">Password reset button</note>
1038 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1056
1039 </trans-unit> 1057 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1040 1058
1041 <trans-unit id="4319634264526091601" datatype="html"> 1059 <trans-unit id="4319634264526091601" datatype="html">
1042 <source>on this instance</source> 1060 <source>on this instance</source>
@@ -1365,7 +1383,7 @@ The link will expire within 1 hour.</source>
1365 <target>Krei konton</target> 1383 <target>Krei konton</target>
1366 1384
1367 1385
1368 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1386 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1369 1387
1370 <trans-unit id="3058024914967508975" datatype="html"> 1388 <trans-unit id="3058024914967508975" datatype="html">
1371 <source>My videos</source> 1389 <source>My videos</source>
@@ -1422,7 +1440,7 @@ The link will expire within 1 hour.</source>
1422 <source>VIDEOS</source> 1440 <source>VIDEOS</source>
1423 <target state="translated">FILMOJ</target> 1441 <target state="translated">FILMOJ</target>
1424 1442
1425 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 1443 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1426 <trans-unit id="667372110624203230" datatype="html"> 1444 <trans-unit id="667372110624203230" datatype="html">
1427 <source>Import jobs concurrency</source> 1445 <source>Import jobs concurrency</source>
1428 <target state="new">Import jobs concurrency</target> 1446 <target state="new">Import jobs concurrency</target>
@@ -1502,7 +1520,7 @@ The link will expire within 1 hour.</source>
1502 <source>I'm a teapot</source> 1520 <source>I'm a teapot</source>
1503 <target state="translated">Mi estas tekruĉo</target> 1521 <target state="translated">Mi estas tekruĉo</target>
1504 1522
1505 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit> 1523 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1506 <trans-unit id="1597262876035959248" datatype="html"> 1524 <trans-unit id="1597262876035959248" datatype="html">
1507 <source>That's an error.</source> 1525 <source>That's an error.</source>
1508 <target state="translated">Tio estas eraro.</target> 1526 <target state="translated">Tio estas eraro.</target>
@@ -1586,8 +1604,8 @@ The link will expire within 1 hour.</source>
1586 <trans-unit id="2971365540217107489" datatype="html"> 1604 <trans-unit id="2971365540217107489" datatype="html">
1587 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1605 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1588 <target state="translated">La vidaŭdaĵo estas tro granda por la servilo. Bonvolu kontakti vian administranton, se vi volas pligrandigi la datumlimon.</target> 1606 <target state="translated">La vidaŭdaĵo estas tro granda por la servilo. Bonvolu kontakti vian administranton, se vi volas pligrandigi la datumlimon.</target>
1589 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1607
1590 </trans-unit> 1608 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1591 1609
1592 <trans-unit id="5131854469652959713" datatype="html"> 1610 <trans-unit id="5131854469652959713" datatype="html">
1593 <source>GLOBAL SEARCH</source> 1611 <source>GLOBAL SEARCH</source>
@@ -2267,7 +2285,7 @@ The link will expire within 1 hour.</source>
2267 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2285 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2268 <source>Upload on hold</source><target state="new">Upload on hold</target> 2286 <source>Upload on hold</source><target state="new">Upload on hold</target>
2269 2287
2270 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2288 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2271 <trans-unit id="285180972645018518" datatype="html"> 2289 <trans-unit id="285180972645018518" datatype="html">
2272 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2290 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2273 <target state="translated">Pardonu, la alŝuta funkcio estas malŝaltita por via konto. Se vi volas aldoni filmojn, administranto devas malŝlosi vian datumlimon.</target> 2291 <target state="translated">Pardonu, la alŝuta funkcio estas malŝaltita por via konto. Se vi volas aldoni filmojn, administranto devas malŝlosi vian datumlimon.</target>
@@ -2977,11 +2995,7 @@ The link will expire within 1 hour.</source>
2977 <target>Identigilo</target> 2995 <target>Identigilo</target>
2978 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 2996 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
2979 </trans-unit> 2997 </trans-unit>
2980 <trans-unit id="2265605798180116441"> 2998
2981 <source>Follower handle</source>
2982 <target>Nomo de abonanto</target>
2983
2984 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
2985 <trans-unit id="5911214550882917183"> 2999 <trans-unit id="5911214550882917183">
2986 <source>State</source> 3000 <source>State</source>
2987 <target>Stato</target> 3001 <target>Stato</target>
@@ -3049,11 +3063,7 @@ The link will expire within 1 hour.</source>
3049 </target> 3063 </target>
3050 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3064 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3051 </trans-unit> 3065 </trans-unit>
3052 <trans-unit id="6641024648411549335"> 3066
3053 <source>Host</source>
3054 <target>Gastiganto</target>
3055
3056 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3057 <trans-unit id="6571718060636962350" datatype="html"> 3067 <trans-unit id="6571718060636962350" datatype="html">
3058 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3068 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3059 <target state="translated">Ripetaĵo permesita <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 3069 <target state="translated">Ripetaĵo permesita <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -3063,7 +3073,7 @@ The link will expire within 1 hour.</source>
3063 <source>Unfollow</source> 3073 <source>Unfollow</source>
3064 <target state="translated">Malaboni</target> 3074 <target state="translated">Malaboni</target>
3065 3075
3066 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3076 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3067 <trans-unit id="8246779176913476983" datatype="html"> 3077 <trans-unit id="8246779176913476983" datatype="html">
3068 <source>Open instance in a new tab</source> 3078 <source>Open instance in a new tab</source>
3069 <target state="translated">Malfermi nodon en nova langeto</target> 3079 <target state="translated">Malfermi nodon en nova langeto</target>
@@ -3075,27 +3085,19 @@ The link will expire within 1 hour.</source>
3075 <source>No host found matching current filters.</source> 3085 <source>No host found matching current filters.</source>
3076 <target state="translated">Neniu gastiganto troviĝis per la nunaj filtriloj.</target> 3086 <target state="translated">Neniu gastiganto troviĝis per la nunaj filtriloj.</target>
3077 3087
3078 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3088 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3079 <trans-unit id="7274241885665071790" datatype="html"> 3089 <trans-unit id="7274241885665071790" datatype="html">
3080 <source>Your instance is not following anyone.</source> 3090 <source>Your instance is not following anyone.</source>
3081 <target state="translated">Via nodo neniun abonas.</target> 3091 <target state="translated">Via nodo neniun abonas.</target>
3082 3092
3083 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3093 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3084 <trans-unit id="4774348799569692380" datatype="html"> 3094 <trans-unit id="4774348799569692380" datatype="html">
3085 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3095 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3086 <target state="translated">Montrante <x id="INTERPOLATION"/> ĝis <x id="INTERPOLATION_1"/> el <x id="INTERPOLATION_2"/> gastigantoj</target> 3096 <target state="translated">Montrante <x id="INTERPOLATION"/> ĝis <x id="INTERPOLATION_1"/> el <x id="INTERPOLATION_2"/> gastigantoj</target>
3087 3097
3088 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3098 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3089 <trans-unit id="6275803119759621687" datatype="html"> 3099
3090 <source>Follow domains</source> 3100 <trans-unit id="9216117865911519658" datatype="html">
3091 <target state="translated">Aboni domajnojn</target>
3092
3093 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit>
3094 <trans-unit id="1268699198448750610" datatype="html">
3095 <source>Follow instances</source>
3096 <target state="translated">Aboni nodojn</target>
3097
3098 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3099 <source>Action</source><target state="new">Action</target> 3101 <source>Action</source><target state="new">Action</target>
3100 3102
3101 3103
@@ -3142,11 +3144,11 @@ The link will expire within 1 hour.</source>
3142 <trans-unit id="5248717555542428023"> 3144 <trans-unit id="5248717555542428023">
3143 <source>Username</source> 3145 <source>Username</source>
3144 <target>Salutnomo</target> 3146 <target>Salutnomo</target>
3145 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3147
3146 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3148
3147 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3149
3148 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3150
3149 </trans-unit> 3151 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3150 <trans-unit id="5428411040014095392" datatype="html"> 3152 <trans-unit id="5428411040014095392" datatype="html">
3151 <source>e.g. jane_doe</source> 3153 <source>e.g. jane_doe</source>
3152 <target state="translated">ekz. ushio_shiromija</target> 3154 <target state="translated">ekz. ushio_shiromija</target>
@@ -3174,9 +3176,9 @@ The link will expire within 1 hour.</source>
3174 <trans-unit id="4145496584631696119"> 3176 <trans-unit id="4145496584631696119">
3175 <source>Role</source> 3177 <source>Role</source>
3176 <target>Rolo</target> 3178 <target>Rolo</target>
3177 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3179
3178 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3180
3179 </trans-unit> 3181 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3180 <trans-unit id="7046347992315328430" datatype="html"> 3182 <trans-unit id="7046347992315328430" datatype="html">
3181 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3183 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3182 <target state="translated">Transkodado estas ŝaltita. Ĉi tiu datumlimo konsideras <x id="START_TAG_STRONG"/>originalan<x id="CLOSE_TAG_STRONG"/> grandecon de la filmo. <x id="LINE_BREAK"/> Plej grande ĉi tiu uzanto povus alŝuti ~ <x id="INTERPOLATION"/>. </target> 3184 <target state="translated">Transkodado estas ŝaltita. Ĉi tiu datumlimo konsideras <x id="START_TAG_STRONG"/>originalan<x id="CLOSE_TAG_STRONG"/> grandecon de la filmo. <x id="LINE_BREAK"/> Plej grande ĉi tiu uzanto povus alŝuti ~ <x id="INTERPOLATION"/>. </target>
@@ -3193,15 +3195,9 @@ The link will expire within 1 hour.</source>
3193 <trans-unit id="2622255144026150901" datatype="html"> 3195 <trans-unit id="2622255144026150901" datatype="html">
3194 <source>Auth plugin</source> 3196 <source>Auth plugin</source>
3195 <target state="translated">Aŭtentikiga kromprogramo</target> 3197 <target state="translated">Aŭtentikiga kromprogramo</target>
3196 <context-group purpose="location"> 3198
3197 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3199
3198 <context context-type="linenumber">188</context> 3200 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3199 </context-group>
3200 <context-group purpose="location">
3201 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3202 <context context-type="linenumber">188</context>
3203 </context-group>
3204 </trans-unit>
3205 <trans-unit id="588099657508661970" datatype="html"> 3201 <trans-unit id="588099657508661970" datatype="html">
3206 <source>None (local authentication)</source> 3202 <source>None (local authentication)</source>
3207 <target state="translated">Neniu (loka aŭtentikigo)</target> 3203 <target state="translated">Neniu (loka aŭtentikigo)</target>
@@ -3446,7 +3442,13 @@ The link will expire within 1 hour.</source>
3446 3442
3447 3443
3448 3444
3449 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit> 3445 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3446 <source>Follower</source><target state="new">Follower</target>
3447 <context-group purpose="location">
3448 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3449 <context context-type="linenumber">24</context>
3450 </context-group>
3451 </trans-unit>
3450 <trans-unit id="4691552465058437520" datatype="html"> 3452 <trans-unit id="4691552465058437520" datatype="html">
3451 <source>Commented video</source> 3453 <source>Commented video</source>
3452 <target state="translated">Komentita filmo</target> 3454 <target state="translated">Komentita filmo</target>
@@ -3733,7 +3735,7 @@ The link will expire within 1 hour.</source>
3733 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3735 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3734 <target state="translated">Ŝajnas, ke vi ne uzas servilon kun HTTPS. Via retservilo bezonas aktivan protokolon TLS por aboni servilojn.</target> 3736 <target state="translated">Ŝajnas, ke vi ne uzas servilon kun HTTPS. Via retservilo bezonas aktivan protokolon TLS por aboni servilojn.</target>
3735 3737
3736 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3738 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3737 <trans-unit id="4058814854824495833" datatype="html"> 3739 <trans-unit id="4058814854824495833" datatype="html">
3738 <source>Mute domains</source> 3740 <source>Mute domains</source>
3739 <target state="translated">Silentigi domajnojn</target> 3741 <target state="translated">Silentigi domajnojn</target>
@@ -5632,11 +5634,8 @@ color: red;
5632 5634
5633 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5635 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5634 <source>CHANNELS</source><target state="new">CHANNELS</target> 5636 <source>CHANNELS</source><target state="new">CHANNELS</target>
5635 <context-group purpose="location"> 5637
5636 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5638 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5637 <context context-type="linenumber">82</context>
5638 </context-group>
5639 </trans-unit>
5640 5639
5641 <trans-unit id="3666829335406793239"> 5640 <trans-unit id="3666829335406793239">
5642 <source>This account does not have channels.</source> 5641 <source>This account does not have channels.</source>
@@ -5670,7 +5669,13 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
5670channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 5669channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
5671 <target state="translated">Ĉu vi certe volas forigi <x id="PH"/>? Tio forigos ĉiujn filmojn alŝutitajn de <x id="PH_1"/> al ĉi tiu kanalo, kaj vi ne povos krei alian kanalon kun la sama nomo (<x id="PH_2"/>)!</target> 5670 <target state="translated">Ĉu vi certe volas forigi <x id="PH"/>? Tio forigos ĉiujn filmojn alŝutitajn de <x id="PH_1"/> al ĉi tiu kanalo, kaj vi ne povos krei alian kanalon kun la sama nomo (<x id="PH_2"/>)!</target>
5672 5671
5673 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5672 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5673 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5674 <context-group purpose="location">
5675 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5676 <context context-type="linenumber">48</context>
5677 </context-group>
5678 </trans-unit>
5674 <trans-unit id="5387007581996837469" datatype="html"> 5679 <trans-unit id="5387007581996837469" datatype="html">
5675 <source>My Channels</source> 5680 <source>My Channels</source>
5676 <target state="translated">Miaj kanaloj</target> 5681 <target state="translated">Miaj kanaloj</target>
@@ -6191,12 +6196,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6191 <source>Your message has been sent.</source> 6196 <source>Your message has been sent.</source>
6192 <target>Via mesaĝo sendiĝis.</target> 6197 <target>Via mesaĝo sendiĝis.</target>
6193 6198
6194 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6199 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6195 <trans-unit id="2072135752262464360"> 6200 <trans-unit id="2072135752262464360">
6196 <source>You already sent this form recently</source> 6201 <source>You already sent this form recently</source>
6197 <target>Vi jam sendis ĉi tiun respondilon freŝdate</target> 6202 <target>Vi jam sendis ĉi tiun respondilon freŝdate</target>
6198 6203
6199 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6204 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6200 6205
6201 <trans-unit id="819067926858619041" datatype="html"> 6206 <trans-unit id="819067926858619041" datatype="html">
6202 <source>Account videos</source> 6207 <source>Account videos</source>
@@ -6244,12 +6249,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6244 <x id="PH"/> rektaj abonantoj de konto 6249 <x id="PH"/> rektaj abonantoj de konto
6245 </target> 6250 </target>
6246 6251
6247 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit> 6252 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6248 <trans-unit id="6250999352462648289" datatype="html"> 6253 <trans-unit id="6250999352462648289" datatype="html">
6249 <source>Report this account</source> 6254 <source>Report this account</source>
6250 <target state="translated">Raporti ĉi tiun konton</target> 6255 <target state="translated">Raporti ĉi tiun konton</target>
6251 6256
6252 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6257 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6253 6258
6254 6259
6255 <trans-unit id="1504521795586863905" datatype="html"> 6260 <trans-unit id="1504521795586863905" datatype="html">
@@ -6264,27 +6269,19 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6264 <target>Salutnomo kopiiĝis</target> 6269 <target>Salutnomo kopiiĝis</target>
6265 6270
6266 6271
6267 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit> 6272 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6268 <trans-unit id="9221735175659318025" datatype="html"> 6273 <trans-unit id="9221735175659318025" datatype="html">
6269 <source>1 subscriber</source> 6274 <source>1 subscriber</source>
6270 <target state="translated">1 abonanto</target> 6275 <target state="translated">1 abonanto</target>
6271 6276
6272 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit> 6277 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6273 <trans-unit id="4097331874769079975" datatype="html"> 6278 <trans-unit id="4097331874769079975" datatype="html">
6274 <source><x id="PH"/> subscribers</source> 6279 <source><x id="PH"/> subscribers</source>
6275 <target state="translated"><x id="PH"/> abonantoj</target> 6280 <target state="translated"><x id="PH"/> abonantoj</target>
6276 6281
6277 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6282 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6278 <trans-unit id="4682675125751819107" datatype="html"> 6283
6279 <source>Instances you follow</source> 6284
6280 <target state="translated">Nodoj, kiujn vi abonas</target>
6281
6282 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6283 <trans-unit id="8899833753704589712" datatype="html">
6284 <source>Instances following you</source>
6285 <target state="translated">Nodoj, kiuj vin abonas</target>
6286
6287 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6288 <trans-unit id="1035838766454786107" datatype="html"> 6285 <trans-unit id="1035838766454786107" datatype="html">
6289 <source>Audio-only</source> 6286 <source>Audio-only</source>
6290 <target state="translated">Nur sono</target> 6287 <target state="translated">Nur sono</target>
@@ -6334,6 +6331,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6334 <source>Auto (via ffmpeg)</source> 6331 <source>Auto (via ffmpeg)</source>
6335 <target>Memage (per ffmpeg)</target> 6332 <target>Memage (per ffmpeg)</target>
6336 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6333 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6334 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6335 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6336 <context-group purpose="location">
6337 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6338 <context context-type="linenumber">3</context>
6339 </context-group>
6337 </trans-unit> 6340 </trans-unit>
6338 <trans-unit id="931255636742351800" datatype="html"> 6341 <trans-unit id="931255636742351800" datatype="html">
6339 <source>No limit</source> 6342 <source>No limit</source>
@@ -6474,18 +6477,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6474 <trans-unit id="2127446333083057097" datatype="html"> 6477 <trans-unit id="2127446333083057097" datatype="html">
6475 <source>Domain is required.</source> 6478 <source>Domain is required.</source>
6476 <target state="translated">Domajno estas postulata.</target> 6479 <target state="translated">Domajno estas postulata.</target>
6477 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6480
6478 </trans-unit> 6481 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6479 <trans-unit id="6780793142903080663" datatype="html"> 6482 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6480 <source>Domains entered are invalid.</source> 6483 <context-group purpose="location">
6481 <target state="translated">Enigitaj domajnoj estas nevalidaj.</target> 6484 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6482 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6485 <context context-type="linenumber">93</context>
6483 </trans-unit> 6486 </context-group>
6484 <trans-unit id="5886492514458202177" datatype="html"> 6487 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6485 <source>Domains entered contain duplicates.</source> 6488 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6486 <target state="translated">Enigitaj domajnoj enhavas duoblaĵojn.</target> 6489 <context-group purpose="location">
6487 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6490 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6491 <context context-type="linenumber">94</context>
6492 </context-group>
6493 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6494 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6495 <context-group purpose="location">
6496 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6497 <context context-type="linenumber">102</context>
6498 </context-group>
6499 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6500 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6501 <context-group purpose="location">
6502 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6503 <context context-type="linenumber">103</context>
6504 </context-group>
6488 </trans-unit> 6505 </trans-unit>
6506
6507
6489 <trans-unit id="240806681889331244"> 6508 <trans-unit id="240806681889331244">
6490 <source>Unlimited</source> 6509 <source>Unlimited</source>
6491 <target>Senlima</target> 6510 <target>Senlima</target>
@@ -6645,24 +6664,50 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6645 <x id="PH"/> forigita el abonantoj de nodo 6664 <x id="PH"/> forigita el abonantoj de nodo
6646 </target> 6665 </target>
6647 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6666 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6667 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6668 <source>Follow</source><target state="new">Follow</target>
6669 <context-group purpose="location">
6670 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6671 <context context-type="linenumber">3</context>
6672 </context-group>
6673 <context-group purpose="location">
6674 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6675 <context context-type="linenumber">37</context>
6676 </context-group>
6677 <context-group purpose="location">
6678 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6679 <context context-type="linenumber">18</context>
6680 </context-group>
6681 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6682 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6683 <context-group purpose="location">
6684 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6685 <context context-type="linenumber">11</context>
6686 </context-group>
6648 </trans-unit> 6687 </trans-unit>
6649 <trans-unit id="2740793005745065895"> 6688 <trans-unit id="2740793005745065895">
6650 <source><x id="PH"/> is not valid </source> 6689 <source><x id="PH"/> is not valid </source>
6651 <target> 6690 <target>
6652 <x id="PH"/> ne validas 6691 <x id="PH"/> ne validas
6653 </target> 6692 </target>
6654 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6693
6655 </trans-unit> 6694 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6656 <trans-unit id="2355066641781598196"> 6695 <trans-unit id="2355066641781598196">
6657 <source>Follow request(s) sent!</source> 6696 <source>Follow request(s) sent!</source>
6658 <target>Abonpetoj senditaj!</target> 6697 <target>Abonpetoj senditaj!</target>
6659 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6698
6699 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6700 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6701 <context-group purpose="location">
6702 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6703 <context context-type="linenumber">3</context>
6704 </context-group>
6660 </trans-unit> 6705 </trans-unit>
6661 <trans-unit id="4245720728052819482"> 6706 <trans-unit id="4245720728052819482">
6662 <source>Do you really want to unfollow <x id="PH"/>?</source> 6707 <source>Do you really want to unfollow <x id="PH"/>?</source>
6663 <target>Ĉu vi certe volas malaboni <x id="PH"/>?</target> 6708 <target>Ĉu vi certe volas malaboni <x id="PH"/>?</target>
6664 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6709
6665 </trans-unit> 6710 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6666 <trans-unit id="9160510009013134726"> 6711 <trans-unit id="9160510009013134726">
6667 <source>Unfollow</source> 6712 <source>Unfollow</source>
6668 <target>Malaboni</target> 6713 <target>Malaboni</target>
@@ -6671,8 +6716,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6671 <trans-unit id="3935234189109112926"> 6716 <trans-unit id="3935234189109112926">
6672 <source>You are not following <x id="PH"/> anymore.</source> 6717 <source>You are not following <x id="PH"/> anymore.</source>
6673 <target>Vi ne plu abonas <x id="PH"/>.</target> 6718 <target>Vi ne plu abonas <x id="PH"/>.</target>
6674 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6719
6675 </trans-unit> 6720 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6676 <trans-unit id="2593763089859685916"> 6721 <trans-unit id="2593763089859685916">
6677 <source>enabled</source> 6722 <source>enabled</source>
6678 <target>ŝaltita</target> 6723 <target>ŝaltita</target>
@@ -7120,9 +7165,9 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7120 <trans-unit id="1519954996184640001"> 7165 <trans-unit id="1519954996184640001">
7121 <source>Error</source> 7166 <source>Error</source>
7122 <target>Eraro</target> 7167 <target>Eraro</target>
7123 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7168
7124 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7169
7125 </trans-unit> 7170 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7126 <trans-unit id="5076187961693950167" datatype="html"> 7171 <trans-unit id="5076187961693950167" datatype="html">
7127 <source>Standard logs</source> 7172 <source>Standard logs</source>
7128 <target state="translated">Normaj protokoloj</target> 7173 <target state="translated">Normaj protokoloj</target>
@@ -7163,16 +7208,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7163 <target>Ĝisdatigi pasvorton de uzanto</target> 7208 <target>Ĝisdatigi pasvorton de uzanto</target>
7164 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7209 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7165 </trans-unit> 7210 </trans-unit>
7166 <trans-unit id="177544274549739411" datatype="html"> 7211
7167 <source>Following list</source> 7212
7168 <target state="translated">Listo de abonatoj</target>
7169 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7170 </trans-unit>
7171 <trans-unit id="8092429110007204784" datatype="html">
7172 <source>Followers list</source>
7173 <target state="translated">Listo de abonantoj</target>
7174 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7175 </trans-unit>
7176 <trans-unit id="780323526182667308" datatype="html"> 7213 <trans-unit id="780323526182667308" datatype="html">
7177 <source>User <x id="PH"/> updated.</source> 7214 <source>User <x id="PH"/> updated.</source>
7178 <target state="translated">Uzanto <x id="PH"/> ĝisdatigita.</target> 7215 <target state="translated">Uzanto <x id="PH"/> ĝisdatigita.</target>
@@ -7208,16 +7245,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7208 <target state="translated">Federaĵo</target> 7245 <target state="translated">Federaĵo</target>
7209 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7246 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7210 </trans-unit> 7247 </trans-unit>
7211 <trans-unit id="4682675125751819107" datatype="html"> 7248
7212 <source>Instances you follow</source> 7249
7213 <target state="translated">Abonataj nodoj</target>
7214 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7215 </trans-unit>
7216 <trans-unit id="8899833753704589712" datatype="html">
7217 <source>Instances following you</source>
7218 <target state="translated">Abonantaj nodoj</target>
7219 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7220 </trans-unit>
7221 <trans-unit id="3767259920053407667" datatype="html"> 7250 <trans-unit id="3767259920053407667" datatype="html">
7222 <source>Videos will be deleted, comments will be tombstoned.</source> 7251 <source>Videos will be deleted, comments will be tombstoned.</source>
7223 <target state="translated">Filmoj foriĝos, komentoj tombiĝos.</target> 7252 <target state="translated">Filmoj foriĝos, komentoj tombiĝos.</target>
@@ -7248,7 +7277,25 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7248 <target>Agordi retpoŝtadreson konfirmita</target> 7277 <target>Agordi retpoŝtadreson konfirmita</target>
7249 7278
7250 7279
7251 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7280 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7281 <source>Created</source><target state="new">Created</target>
7282 <context-group purpose="location">
7283 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7284 <context context-type="linenumber">115</context>
7285 </context-group>
7286 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7287 <source>Daily quota</source><target state="new">Daily quota</target>
7288 <context-group purpose="location">
7289 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7290 <context context-type="linenumber">120</context>
7291 </context-group>
7292 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7293 <source>Last login</source><target state="new">Last login</target>
7294 <context-group purpose="location">
7295 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7296 <context context-type="linenumber">122</context>
7297 </context-group>
7298 </trans-unit>
7252 <trans-unit id="3403978719736970622"> 7299 <trans-unit id="3403978719736970622">
7253 <source>You cannot ban root.</source> 7300 <source>You cannot ban root.</source>
7254 <target>Vi ne povas forbari ĉefuzanton.</target> 7301 <target>Vi ne povas forbari ĉefuzanton.</target>
@@ -7551,12 +7598,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7551 <source>Video channel <x id="PH"/> created.</source> 7598 <source>Video channel <x id="PH"/> created.</source>
7552 <target>Filma kanalo <x id="PH"/> kreita.</target> 7599 <target>Filma kanalo <x id="PH"/> kreita.</target>
7553 7600
7554 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7601 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7555 <trans-unit id="8723777130353305761"> 7602 <trans-unit id="8723777130353305761">
7556 <source>This name already exists on this instance.</source> 7603 <source>This name already exists on this instance.</source>
7557 <target>Ĉi tiu nomo jam ekzistas ĉe ĉi tiu nodo.</target> 7604 <target>Ĉi tiu nomo jam ekzistas ĉe ĉi tiu nodo.</target>
7558 7605
7559 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7606 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7560 <trans-unit id="7589345916094713536"> 7607 <trans-unit id="7589345916094713536">
7561 <source>Video channel <x id="PH"/> updated.</source> 7608 <source>Video channel <x id="PH"/> updated.</source>
7562 <target>Filma kanalo <x id="PH"/> ĝisdatigita.</target> 7609 <target>Filma kanalo <x id="PH"/> ĝisdatigita.</target>
@@ -7571,11 +7618,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7571 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 7618 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7572 7619
7573 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7620 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7574 <trans-unit id="2575302837003821736"> 7621
7575 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7576 <target>Bonvolu entajpi la prezentan nomon de la filma kanalo (<x id="PH"/>) por konfirmi</target>
7577
7578 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7579 <trans-unit id="624066830180032195"> 7622 <trans-unit id="624066830180032195">
7580 <source>Video channel <x id="PH"/> deleted.</source> 7623 <source>Video channel <x id="PH"/> deleted.</source>
7581 <target>Filma kanalo <x id="PH"/> forigita.</target> 7624 <target>Filma kanalo <x id="PH"/> forigita.</target>
@@ -7724,6 +7767,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7724 <source>Ownership change request sent.</source> 7767 <source>Ownership change request sent.</source>
7725 <target>Peto de poseda ŝanĝo sendiĝis.</target> 7768 <target>Peto de poseda ŝanĝo sendiĝis.</target>
7726 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 7769 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
7770 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
7771 <source>Sort by</source><target state="new">Sort by</target>
7772 <context-group purpose="location">
7773 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7774 <context context-type="linenumber">26</context>
7775 </context-group>
7727 </trans-unit> 7776 </trans-unit>
7728 <trans-unit id="3245220240937722814"> 7777 <trans-unit id="3245220240937722814">
7729 <source>My channels</source> 7778 <source>My channels</source>
@@ -7825,7 +7874,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7825 <target>Aboni la konton</target> 7874 <target>Aboni la konton</target>
7826 7875
7827 7876
7828 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 7877 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
7829 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 7878 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
7830 <context-group purpose="location"> 7879 <context-group purpose="location">
7831 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 7880 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -7871,34 +7920,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7871 <trans-unit id="3779524668013120370"> 7920 <trans-unit id="3779524668013120370">
7872 <source>Go to my subscriptions</source> 7921 <source>Go to my subscriptions</source>
7873 <target>Iri al miaj abonoj</target> 7922 <target>Iri al miaj abonoj</target>
7874 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 7923
7875 </trans-unit> 7924 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
7876 <trans-unit id="1136469849928650779"> 7925 <trans-unit id="1136469849928650779">
7877 <source>Go to my videos</source> 7926 <source>Go to my videos</source>
7878 <target>Iri al miaj filmoj</target> 7927 <target>Iri al miaj filmoj</target>
7879 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 7928
7880 </trans-unit> 7929 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
7881 <trans-unit id="7836683738999600376"> 7930 <trans-unit id="7836683738999600376">
7882 <source>Go to my imports</source> 7931 <source>Go to my imports</source>
7883 <target>Iri al miaj enportoj</target> 7932 <target>Iri al miaj enportoj</target>
7884 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 7933
7885 </trans-unit> 7934 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
7886 <trans-unit id="7511292153332773503"> 7935 <trans-unit id="7511292153332773503">
7887 <source>Go to my channels</source> 7936 <source>Go to my channels</source>
7888 <target>Iri al miaj kanaloj</target> 7937 <target>Iri al miaj kanaloj</target>
7889 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 7938
7890 </trans-unit> 7939 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
7891 <trans-unit id="2013324644839511073" datatype="html"> 7940 <trans-unit id="2013324644839511073" datatype="html">
7892 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 7941 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
7893Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 7942Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
7894 <target state="translated">Ne povas ekhavi salutilojn de OAuth Client: <x id="PH"/>. Certigu, ke PeerTube estas ĝuste agordita (en la dosierujo config/), precipe la sekcio «webserver».</target> 7943 <target state="translated">Ne povas ekhavi salutilojn de OAuth Client: <x id="PH"/>. Certigu, ke PeerTube estas ĝuste agordita (en la dosierujo config/), precipe la sekcio «webserver».</target>
7895 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 7944
7896 </trans-unit> 7945 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
7897 <trans-unit id="375263728166936544"> 7946 <trans-unit id="375263728166936544">
7898 <source>You need to reconnect.</source> 7947 <source>You need to reconnect.</source>
7899 <target>Vi devas rekonektiĝi.</target> 7948 <target>Vi devas rekonektiĝi.</target>
7900 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 7949
7901 </trans-unit> 7950 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
7902 <trans-unit id="2206638022166154361"> 7951 <trans-unit id="2206638022166154361">
7903 <source>Keyboard Shortcuts:</source> 7952 <source>Keyboard Shortcuts:</source>
7904 <target>Fulmoklavoj:</target> 7953 <target>Fulmoklavoj:</target>
@@ -7909,6 +7958,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7909 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 7958 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7910 <context context-type="linenumber">98</context> 7959 <context context-type="linenumber">98</context>
7911 </context-group> 7960 </context-group>
7961 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
7962 <source>In my library</source><target state="new">In my library</target>
7963 <context-group purpose="location">
7964 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7965 <context context-type="linenumber">104</context>
7966 </context-group>
7912 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 7967 </trans-unit><trans-unit id="232050922346936574" datatype="html">
7913 <source>Trending</source><target state="new">Trending</target> 7968 <source>Trending</source><target state="new">Trending</target>
7914 7969
@@ -7932,38 +7987,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7932 <source>Incorrect username or password.</source> 7987 <source>Incorrect username or password.</source>
7933 <target>Malĝusta salutnomo aŭ pasvorto.</target> 7988 <target>Malĝusta salutnomo aŭ pasvorto.</target>
7934 7989
7935 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 7990 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
7936 <trans-unit id="6974874606619467663" datatype="html"> 7991 <trans-unit id="6974874606619467663" datatype="html">
7937 <source>Your account is blocked.</source> 7992 <source>Your account is blocked.</source>
7938 <target state="translated">Via konto estas blokita.</target> 7993 <target state="translated">Via konto estas blokita.</target>
7939 7994
7940 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 7995 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
7941 <trans-unit id="7939914198003891823" datatype="html"> 7996 <trans-unit id="7939914198003891823" datatype="html">
7942 <source>any language</source> 7997 <source>any language</source>
7943 <target state="translated">ajna lingvo</target> 7998 <target state="translated">ajna lingvo</target>
7944 7999
7945 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8000 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
7946 8001
7947 <trans-unit id="5633144232269377096" datatype="html"> 8002 <trans-unit id="5633144232269377096" datatype="html">
7948 <source>hide</source> 8003 <source>hide</source>
7949 <target state="translated">kaŝi</target> 8004 <target state="translated">kaŝi</target>
7950 8005
7951 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8006 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
7952 <trans-unit id="8603861867909474404" datatype="html"> 8007 <trans-unit id="8603861867909474404" datatype="html">
7953 <source>blur</source> 8008 <source>blur</source>
7954 <target state="translated">malklarigi</target> 8009 <target state="translated">malklarigi</target>
7955 8010
7956 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8011 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
7957 <trans-unit id="4534458451100881847" datatype="html"> 8012 <trans-unit id="4534458451100881847" datatype="html">
7958 <source>display</source> 8013 <source>display</source>
7959 <target state="translated">montri</target> 8014 <target state="translated">montri</target>
7960 8015
7961 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8016 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
7962 <trans-unit id="4467323362722952678" datatype="html"> 8017 <trans-unit id="4467323362722952678" datatype="html">
7963 <source>Unknown</source> 8018 <source>Unknown</source>
7964 <target state="translated">Nekonata</target> 8019 <target state="translated">Nekonata</target>
7965 8020
7966 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8021 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
7967 <trans-unit id="8781423666414310853"> 8022 <trans-unit id="8781423666414310853">
7968 <source>Your password has been successfully reset!</source> 8023 <source>Your password has been successfully reset!</source>
7969 <target>Via pasvorto estas sukcese restarigita!</target> 8024 <target>Via pasvorto estas sukcese restarigita!</target>
@@ -9524,18 +9579,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9524 <trans-unit id="968295009933361070"> 9579 <trans-unit id="968295009933361070">
9525 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9580 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9526 <target>Tro multaj petoj; bonvolu reprovi post <x id="PH"/> minutoj.</target> 9581 <target>Tro multaj petoj; bonvolu reprovi post <x id="PH"/> minutoj.</target>
9527 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9582
9528 </trans-unit> 9583 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9529 <trans-unit id="4965472196059235310"> 9584 <trans-unit id="4965472196059235310">
9530 <source>Too many attempts, please try again later.</source> 9585 <source>Too many attempts, please try again later.</source>
9531 <target>Tro multaj provoj; bonvolu reprovi poste.</target> 9586 <target>Tro multaj provoj; bonvolu reprovi poste.</target>
9532 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9587
9533 </trans-unit> 9588 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9534 <trans-unit id="1693549688987384699"> 9589 <trans-unit id="1693549688987384699">
9535 <source>Server error. Please retry later.</source> 9590 <source>Server error. Please retry later.</source>
9536 <target>Servila eraro. Bonvolu reprovi poste.</target> 9591 <target>Servila eraro. Bonvolu reprovi poste.</target>
9537 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9592
9538 </trans-unit> 9593 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9539 <trans-unit id="5927402622550505067" datatype="html"> 9594 <trans-unit id="5927402622550505067" datatype="html">
9540 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9595 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9541 <target state="translated">Abonanta ĉiujn nunajn kanalojn de <x id="PH"/>. Vi sciiĝos pri ĉiuj ĝiaj novaj filmoj.</target> 9596 <target state="translated">Abonanta ĉiujn nunajn kanalojn de <x id="PH"/>. Vi sciiĝos pri ĉiuj ĝiaj novaj filmoj.</target>
@@ -10120,35 +10175,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10120 <source>Your video was uploaded to your account and is private.</source> 10175 <source>Your video was uploaded to your account and is private.</source>
10121 <target>Via filmo alŝutiĝis al via konto kaj estas privata.</target> 10176 <target>Via filmo alŝutiĝis al via konto kaj estas privata.</target>
10122 10177
10123 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10178 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10124 <trans-unit id="5699822024600815733"> 10179 <trans-unit id="5699822024600815733">
10125 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10180 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10126 <target>Sed rilataj informoj (etikedoj, priskribo…) perdiĝos; ĉu vi certe volas folasi ĉi tiun paĝon?</target> 10181 <target>Sed rilataj informoj (etikedoj, priskribo…) perdiĝos; ĉu vi certe volas folasi ĉi tiun paĝon?</target>
10127 10182
10128 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10183 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10129 <trans-unit id="1219739004043110649"> 10184 <trans-unit id="1219739004043110649">
10130 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10185 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10131 <target>Via filmo ankoraŭ ne alŝutiĝis; ĉu vi certe volas forlasi ĉi tiun paĝon?</target> 10186 <target>Via filmo ankoraŭ ne alŝutiĝis; ĉu vi certe volas forlasi ĉi tiun paĝon?</target>
10132 10187
10133 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10188 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10134 <trans-unit id="6932865105766151309" datatype="html"> 10189 <trans-unit id="6932865105766151309" datatype="html">
10135 <source>Upload</source> 10190 <source>Upload</source>
10136 <target state="translated">Alŝuti</target> 10191 <target state="translated">Alŝuti</target>
10137 10192
10138 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10193 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10139 <trans-unit id="8278735427925094503"> 10194 <trans-unit id="8278735427925094503">
10140 <source>Upload <x id="PH"/> </source> 10195 <source>Upload <x id="PH"/> </source>
10141 <target>Alŝuti 10196 <target>Alŝuti
10142 <x id="PH"/> 10197 <x id="PH"/>
10143 </target> 10198 </target>
10144 10199
10145 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10200 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10146 10201
10147 <trans-unit id="5981816353437801748"> 10202 <trans-unit id="5981816353437801748">
10148 <source>Video published.</source> 10203 <source>Video published.</source>
10149 <target>Filmo publikigita.</target> 10204 <target>Filmo publikigita.</target>
10150 10205
10151 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10206 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10152 10207
10153 10208
10154 <trans-unit id="764164089183618119"> 10209 <trans-unit id="764164089183618119">
@@ -10196,27 +10251,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10196 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10251 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10197 <target state="translated">Ĉi tiu filmo ne estas disponebla per ĉi tiu nodo. Ĉu vi volas alidirektiĝi al la devena nodo: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10252 <target state="translated">Ĉi tiu filmo ne estas disponebla per ĉi tiu nodo. Ĉu vi volas alidirektiĝi al la devena nodo: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10198 10253
10199 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10254 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10200 <trans-unit id="5761611056224181752" datatype="html"> 10255 <trans-unit id="5761611056224181752" datatype="html">
10201 <source>Redirection</source> 10256 <source>Redirection</source>
10202 <target state="translated">Alidirektiĝo</target> 10257 <target state="translated">Alidirektiĝo</target>
10203 10258
10204 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10259 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10205 <trans-unit id="8858527736400081688"> 10260 <trans-unit id="8858527736400081688">
10206 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10261 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10207 <target>Tiu ĉi video povas esti konsterna aŭ maltaŭga por neplenaĝuloj. Ĉu vi certe volas spekti ĝin?</target> 10262 <target>Tiu ĉi video povas esti konsterna aŭ maltaŭga por neplenaĝuloj. Ĉu vi certe volas spekti ĝin?</target>
10208 10263
10209 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10264 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10210 <trans-unit id="3937119019020041049"> 10265 <trans-unit id="3937119019020041049">
10211 <source>Mature or explicit content</source> 10266 <source>Mature or explicit content</source>
10212 <target>Konsterna aŭ maltaŭga por neplenaĝaj</target> 10267 <target>Konsterna aŭ maltaŭga por neplenaĝaj</target>
10213 10268
10214 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10269 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10215 <trans-unit id="1755474755114288376" datatype="html"> 10270 <trans-unit id="1755474755114288376" datatype="html">
10216 <source>Up Next</source> 10271 <source>Up Next</source>
10217 <target state="translated">Sekve</target> 10272 <target state="translated">Sekve</target>
10218 10273
10219 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10274 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10220 <trans-unit id="2159130950882492111" datatype="html"> 10275 <trans-unit id="2159130950882492111" datatype="html">
10221 <source>Cancel</source> 10276 <source>Cancel</source>
10222 <target state="translated">Nuligi</target> 10277 <target state="translated">Nuligi</target>
@@ -10226,62 +10281,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10226 <source>Autoplay is suspended</source> 10281 <source>Autoplay is suspended</source>
10227 <target state="translated">Memludado estas haltigita</target> 10282 <target state="translated">Memludado estas haltigita</target>
10228 10283
10229 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10284 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10230 <trans-unit id="7895294730547405228" datatype="html"> 10285 <trans-unit id="7895294730547405228" datatype="html">
10231 <source>Enter/exit fullscreen (requires player focus)</source> 10286 <source>Enter/exit fullscreen (requires player focus)</source>
10232 <target state="translated">Eniĝi/eliĝi tutekranan reĝimon (bezonas fokusitan ludilon)</target> 10287 <target state="translated">Eniĝi/eliĝi tutekranan reĝimon (bezonas fokusitan ludilon)</target>
10233 10288
10234 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10289 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10235 <trans-unit id="7618388257165864759" datatype="html"> 10290 <trans-unit id="7618388257165864759" datatype="html">
10236 <source>Play/Pause the video (requires player focus)</source> 10291 <source>Play/Pause the video (requires player focus)</source>
10237 <target state="translated">Ludi/Paŭzgi la filmon (bezonas fokusitan ludilon)</target> 10292 <target state="translated">Ludi/Paŭzgi la filmon (bezonas fokusitan ludilon)</target>
10238 10293
10239 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10294 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10240 <trans-unit id="7761890399634216630" datatype="html"> 10295 <trans-unit id="7761890399634216630" datatype="html">
10241 <source>Mute/unmute the video (requires player focus)</source> 10296 <source>Mute/unmute the video (requires player focus)</source>
10242 <target state="translated">Silentigi/Malsilentigi la filmon (bezonas fokusitan ludilon)</target> 10297 <target state="translated">Silentigi/Malsilentigi la filmon (bezonas fokusitan ludilon)</target>
10243 10298
10244 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10299 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10245 <trans-unit id="5996585232248234904" datatype="html"> 10300 <trans-unit id="5996585232248234904" datatype="html">
10246 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10301 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10247 <target state="translated">Salti al elcenta progreso en la filmo: 0 estas 0% kaj 9 estas 90% (bezonas fokusitan ludilon)</target> 10302 <target state="translated">Salti al elcenta progreso en la filmo: 0 estas 0% kaj 9 estas 90% (bezonas fokusitan ludilon)</target>
10248 10303
10249 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10304 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10250 <trans-unit id="3748765405903319998" datatype="html"> 10305 <trans-unit id="3748765405903319998" datatype="html">
10251 <source>Increase the volume (requires player focus)</source> 10306 <source>Increase the volume (requires player focus)</source>
10252 <target state="translated">Plilaŭtigi (bezonas fokusitan ludilon)</target> 10307 <target state="translated">Plilaŭtigi (bezonas fokusitan ludilon)</target>
10253 10308
10254 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10309 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10255 <trans-unit id="5810704036407159982" datatype="html"> 10310 <trans-unit id="5810704036407159982" datatype="html">
10256 <source>Decrease the volume (requires player focus)</source> 10311 <source>Decrease the volume (requires player focus)</source>
10257 <target state="translated">Mallaŭtigi (bezonas fokusitan ludilon)</target> 10312 <target state="translated">Mallaŭtigi (bezonas fokusitan ludilon)</target>
10258 10313
10259 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10314 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10260 <trans-unit id="2622048822548065691" datatype="html"> 10315 <trans-unit id="2622048822548065691" datatype="html">
10261 <source>Seek the video forward (requires player focus)</source> 10316 <source>Seek the video forward (requires player focus)</source>
10262 <target state="translated">Pluigi la filmon (bezonas fokusitan ludilon)</target> 10317 <target state="translated">Pluigi la filmon (bezonas fokusitan ludilon)</target>
10263 10318
10264 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10319 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10265 <trans-unit id="6540078205109221153" datatype="html"> 10320 <trans-unit id="6540078205109221153" datatype="html">
10266 <source>Seek the video backward (requires player focus)</source> 10321 <source>Seek the video backward (requires player focus)</source>
10267 <target state="translated">Malpluigi la filmon (bezonas fokusitan ludilon)</target> 10322 <target state="translated">Malpluigi la filmon (bezonas fokusitan ludilon)</target>
10268 10323
10269 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10324 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10270 <trans-unit id="1956491957766210808" datatype="html"> 10325 <trans-unit id="1956491957766210808" datatype="html">
10271 <source>Increase playback rate (requires player focus)</source> 10326 <source>Increase playback rate (requires player focus)</source>
10272 <target state="translated">Plirapidigi la filmon (bezonas fokusitan ludilon)</target> 10327 <target state="translated">Plirapidigi la filmon (bezonas fokusitan ludilon)</target>
10273 10328
10274 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10329 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10275 <trans-unit id="5495529997674803186" datatype="html"> 10330 <trans-unit id="5495529997674803186" datatype="html">
10276 <source>Decrease playback rate (requires player focus)</source> 10331 <source>Decrease playback rate (requires player focus)</source>
10277 <target state="translated">Malrapidigi la filmon (bezonas fokusitan ludilon)</target> 10332 <target state="translated">Malrapidigi la filmon (bezonas fokusitan ludilon)</target>
10278 10333
10279 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10334 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10280 <trans-unit id="3178343147230721210" datatype="html"> 10335 <trans-unit id="3178343147230721210" datatype="html">
10281 <source>Navigate in the video frame by frame (requires player focus)</source> 10336 <source>Navigate in the video frame by frame (requires player focus)</source>
10282 <target state="translated">Navigi tra la filmo po filmeroj (bezonas fokusitan ludilon)</target> 10337 <target state="translated">Navigi tra la filmo po filmeroj (bezonas fokusitan ludilon)</target>
10283 10338
10284 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10339 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10285 <trans-unit id="8025996572234182184"> 10340 <trans-unit id="8025996572234182184">
10286 <source>Like the video</source> 10341 <source>Like the video</source>
10287 <target>Ŝati la filmon</target> 10342 <target>Ŝati la filmon</target>
diff --git a/client/src/locale/angular.es-ES.xlf b/client/src/locale/angular.es-ES.xlf
index d2905bb7b..62fe3b704 100644
--- a/client/src/locale/angular.es-ES.xlf
+++ b/client/src/locale/angular.es-ES.xlf
@@ -162,19 +162,19 @@
162 <target state="translated"> 162 <target state="translated">
163 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 163 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
164 </target> 164 </target>
165 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group> 165
166 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group> 166
167 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group> 167
168 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group> 168
169 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group> 169
170 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 170
171 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group> 171
172 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group> 172
173 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group> 173
174 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group> 174
175 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group> 175
176 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group> 176
177 </trans-unit> 177 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
178 <trans-unit id="1486537403020619891" datatype="html"> 178 <trans-unit id="1486537403020619891" datatype="html">
179 <source>My watch history</source> 179 <source>My watch history</source>
180 <target state="translated">Mi historial de visionados</target> 180 <target state="translated">Mi historial de visionados</target>
@@ -243,22 +243,22 @@
243 <trans-unit id="5674286808255988565"> 243 <trans-unit id="5674286808255988565">
244 <source>Create</source> 244 <source>Create</source>
245 <target>Crear</target> 245 <target>Crear</target>
246 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 246
247 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 247
248 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 248
249 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 249
250 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 250
251 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 251
252 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 252
253 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 253
254 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 254
255 </trans-unit> 255 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
256 <trans-unit id="1006562256968398209" datatype="html"> 256 <trans-unit id="1006562256968398209" datatype="html">
257 <source>video</source> 257 <source>video</source>
258 <target state="translated">video</target> 258 <target state="translated">video</target>
259 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group> 259
260 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group> 260
261 </trans-unit> 261 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
262 <trans-unit id="6438815964972582865" datatype="html"> 262 <trans-unit id="6438815964972582865" datatype="html">
263 <source>The following link contains a private token and should not be shared with anyone.</source> 263 <source>The following link contains a private token and should not be shared with anyone.</source>
264 <target state="translated">El siguiente enlace contiene un token privado y no debe compartirse con nadie.</target> 264 <target state="translated">El siguiente enlace contiene un token privado y no debe compartirse con nadie.</target>
@@ -330,13 +330,13 @@
330 <trans-unit id="6995024616159044376" datatype="html"> 330 <trans-unit id="6995024616159044376" datatype="html">
331 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 331 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
332 <target state="translated">Su cuota de video se excede con este video (tamaño del video:<x id="PH" equiv-text="videoSizeBytes"/>, usado: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 332 <target state="translated">Su cuota de video se excede con este video (tamaño del video:<x id="PH" equiv-text="videoSizeBytes"/>, usado: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
333 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 333
334 </trans-unit> 334 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
335 <trans-unit id="7873395933409147217" datatype="html"> 335 <trans-unit id="7873395933409147217" datatype="html">
336 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 336 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
337 <target state="translated">Su cuota diaria de video se excede con este video (tamaño del video:<x id="PH" equiv-text="videoSizeBytes"/>, usado: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 337 <target state="translated">Su cuota diaria de video se excede con este video (tamaño del video:<x id="PH" equiv-text="videoSizeBytes"/>, usado: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
338 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 338
339 </trans-unit> 339 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
340 <trans-unit id="5235042777215655908" datatype="html"> 340 <trans-unit id="5235042777215655908" datatype="html">
341 <source>subtitles</source> 341 <source>subtitles</source>
342 <target state="translated">subtítulos</target> 342 <target state="translated">subtítulos</target>
@@ -776,10 +776,10 @@
776 <trans-unit id="2602586221576511475"> 776 <trans-unit id="2602586221576511475">
777 <source>Video quota</source> 777 <source>Video quota</source>
778 <target>Cupo de vídeos</target> 778 <target>Cupo de vídeos</target>
779 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 779
780 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 780
781 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 781
782 </trans-unit> 782 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
783 <trans-unit id="1502595455339510144"> 783 <trans-unit id="1502595455339510144">
784 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 784 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
785 <target>Illimitado <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> por día) <x id="CLOSE_TAG_NG_CONTAINER"/></target> 785 <target>Illimitado <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> por día) <x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -859,6 +859,30 @@
859 <target state="translated">Federación</target> 859 <target state="translated">Federación</target>
860 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 860 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
861 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 861 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
862 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
863 <source>Following</source><target state="new">Following</target>
864 <context-group purpose="location">
865 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
866 <context context-type="linenumber">29</context>
867 </context-group>
868 <context-group purpose="location">
869 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
870 <context context-type="linenumber">31</context>
871 </context-group>
872 <context-group purpose="location">
873 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
874 <context context-type="linenumber">28</context>
875 </context-group>
876 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
877 <source>Followers</source><target state="new">Followers</target>
878 <context-group purpose="location">
879 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
880 <context context-type="linenumber">34</context>
881 </context-group>
882 <context-group purpose="location">
883 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
884 <context context-type="linenumber">37</context>
885 </context-group>
862 </trans-unit> 886 </trans-unit>
863 <trans-unit id="3541687134897970106" datatype="html"> 887 <trans-unit id="3541687134897970106" datatype="html">
864 <source>followers</source> 888 <source>followers</source>
@@ -922,25 +946,25 @@
922 <trans-unit id="2159130950882492111"> 946 <trans-unit id="2159130950882492111">
923 <source>Cancel</source> 947 <source>Cancel</source>
924 <target>cancelar</target> 948 <target>cancelar</target>
925 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group> 949
926 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group> 950
927 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group> 951
928 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group> 952
929 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group> 953
930 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group> 954
931 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group> 955
932 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group> 956
933 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group> 957
934 <context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group> 958
935 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group> 959
936 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group> 960
937 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 961
938 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 962
939 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group> 963
940 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group> 964
941 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group> 965
942 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group> 966
943 </trans-unit> 967 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
944 <trans-unit id="3616223838716839702"> 968 <trans-unit id="3616223838716839702">
945 <source>Ban this user</source> 969 <source>Ban this user</source>
946 <target>Expulsar este usuario</target> 970 <target>Expulsar este usuario</target>
@@ -1005,19 +1029,13 @@ Iniciar sesión</target>
1005 <trans-unit id="7252854992688790751" datatype="html"> 1029 <trans-unit id="7252854992688790751" datatype="html">
1006 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1030 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1007 <target state="translated">Esta instancia permite el registro. Sin embargo, tenga cuidado de comprobar las <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Condiciones<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Condiciones<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>antes de crear una cuenta. También puede buscar otra instancia que coincida con sus necesidades exactas en: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1031 <target state="translated">Esta instancia permite el registro. Sin embargo, tenga cuidado de comprobar las <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Condiciones<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Condiciones<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>antes de crear una cuenta. También puede buscar otra instancia que coincida con sus necesidades exactas en: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1008 <context-group purpose="location"> 1032
1009 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1033 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1010 <context context-type="linenumber">60,62</context>
1011 </context-group>
1012 </trans-unit>
1013 <trans-unit id="7215649348148521605" datatype="html"> 1034 <trans-unit id="7215649348148521605" datatype="html">
1014 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1035 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1015 <target state="translated">Actualmente, esta instancia no permite el registro de usuarios, puede marcar la <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>para obtener más detalles o busque una instancia que le brinde la posibilidad de registrarse para obtener una cuenta y cargar sus videos allí. Encuentre el suyo entre varias instancias en:<x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1036 <target state="translated">Actualmente, esta instancia no permite el registro de usuarios, puede marcar la <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>para obtener más detalles o busque una instancia que le brinde la posibilidad de registrarse para obtener una cuenta y cargar sus videos allí. Encuentre el suyo entre varias instancias en:<x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1016 <context-group purpose="location"> 1037
1017 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1038 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1018 <context context-type="linenumber">65,67</context>
1019 </context-group>
1020 </trans-unit>
1021 <trans-unit id="2392488717875840729"> 1039 <trans-unit id="2392488717875840729">
1022 <source>User</source> 1040 <source>User</source>
1023 <target>Usuario</target> 1041 <target>Usuario</target>
@@ -1028,67 +1046,67 @@ Iniciar sesión</target>
1028 <source>Username or email address</source> 1046 <source>Username or email address</source>
1029 <target>Nombre de usuario o correo electrónico</target> 1047 <target>Nombre de usuario o correo electrónico</target>
1030 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1048 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1049 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1050 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1051 <context-group purpose="location">
1052 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1053 <context context-type="linenumber">33,34</context>
1054 </context-group>
1031 </trans-unit> 1055 </trans-unit>
1032 <trans-unit id="1431416938026210429"> 1056 <trans-unit id="1431416938026210429">
1033 <source>Password</source> 1057 <source>Password</source>
1034 <target>Contraseña</target> 1058 <target>Contraseña</target>
1035 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1059
1036 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1060
1037 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1061
1038 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1062
1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1063
1040 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1064
1041 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1065
1042 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1066
1043 </trans-unit> 1067 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1044 <trans-unit id="8715156686857791956" datatype="html"> 1068 <trans-unit id="8715156686857791956" datatype="html">
1045 <source>Click here to reset your password</source> 1069 <source>Click here to reset your password</source>
1046 <target state="translated">Haga clic aquí para restablecer la contraseña</target> 1070 <target state="translated">Haga clic aquí para restablecer la contraseña</target>
1047 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1071
1048 </trans-unit> 1072 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1049 <trans-unit id="892063502898494584" datatype="html"> 1073 <trans-unit id="892063502898494584" datatype="html">
1050 <source>I forgot my password</source> 1074 <source>I forgot my password</source>
1051 <target state="translated">Olvidé mi contraseña</target> 1075 <target state="translated">Olvidé mi contraseña</target>
1052 <context-group purpose="location"> 1076
1053 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1077 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1054 <context context-type="linenumber">47</context>
1055 </context-group>
1056 </trans-unit>
1057 <trans-unit id="2101170466365500913" datatype="html"> 1078 <trans-unit id="2101170466365500913" datatype="html">
1058 <source>Logging into an account lets you publish content</source> 1079 <source>Logging into an account lets you publish content</source>
1059 <target state="translated">Iniciar sesión en una cuenta le permite publicar contenido</target> 1080 <target state="translated">Iniciar sesión en una cuenta le permite publicar contenido</target>
1060 <context-group purpose="location"> 1081
1061 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1082 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1062 <context context-type="linenumber">56,57</context>
1063 </context-group>
1064 </trans-unit>
1065 <trans-unit id="2454050363478003966"> 1083 <trans-unit id="2454050363478003966">
1066 <source>Login</source> 1084 <source>Login</source>
1067 <target>Iniciar sesión</target> 1085 <target>Iniciar sesión</target>
1068 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1086
1069 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1087
1070 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1088
1071 </trans-unit> 1089 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1072 <trans-unit id="3183213940445113677" datatype="html"> 1090 <trans-unit id="3183213940445113677" datatype="html">
1073 <source>Or sign in with</source> 1091 <source>Or sign in with</source>
1074 <target state="translated">O inicia sesión con</target> 1092 <target state="translated">O inicia sesión con</target>
1075 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1093
1076 </trans-unit> 1094 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1077 <trans-unit id="3238209155172574367"> 1095 <trans-unit id="3238209155172574367">
1078 <source>Forgot your password</source> 1096 <source>Forgot your password</source>
1079 <target>Contraseña olvidada</target> 1097 <target>Contraseña olvidada</target>
1080 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1098
1081 </trans-unit> 1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1082 <trans-unit id="87327320394367488" datatype="html"> 1100 <trans-unit id="87327320394367488" datatype="html">
1083 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1101 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1084 <target state="translated">Lo sentimos, no es posible recuperar la contraseña porque el administrador de la instancia no ha configurado el sistema de correo electrónico de PeerTube.</target> 1102 <target state="translated">Lo sentimos, no es posible recuperar la contraseña porque el administrador de la instancia no ha configurado el sistema de correo electrónico de PeerTube.</target>
1085 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1103
1086 </trans-unit> 1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1087 <trans-unit id="3188014010833256853" datatype="html"> 1105 <trans-unit id="3188014010833256853" datatype="html">
1088 <source>Enter your email address and we will send you a link to reset your password.</source> 1106 <source>Enter your email address and we will send you a link to reset your password.</source>
1089 <target state="translated">Ingrese su dirección de correo electrónico y le enviaremos un enlace para restablecer su contraseña.</target> 1107 <target state="translated">Ingrese su dirección de correo electrónico y le enviaremos un enlace para restablecer su contraseña.</target>
1090 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1108
1091 </trans-unit> 1109 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1092 <trans-unit id="1190256911880544559" datatype="html"> 1110 <trans-unit id="1190256911880544559" datatype="html">
1093 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1111 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1094The link will expire within 1 hour.</source> 1112The link will expire within 1 hour.</source>
@@ -1098,26 +1116,26 @@ The link will expire within 1 hour.</source>
1098 <trans-unit id="4768749765465246664"> 1116 <trans-unit id="4768749765465246664">
1099 <source>Email</source> 1117 <source>Email</source>
1100 <target>Correo electrónico </target> 1118 <target>Correo electrónico </target>
1101 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1119
1102 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1120
1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1121
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1122
1105 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1123
1106 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1124
1107 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1125
1108 </trans-unit> 1126 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1109 <trans-unit id="3967269098753656610"> 1127 <trans-unit id="3967269098753656610">
1110 <source>Email address</source> 1128 <source>Email address</source>
1111 <target>Correo electrónico </target> 1129 <target>Correo electrónico </target>
1112 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1130
1113 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1131
1114 </trans-unit> 1132 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1115 <trans-unit id="7808756054397155068" datatype="html"> 1133 <trans-unit id="7808756054397155068" datatype="html">
1116 <source>Reset</source> 1134 <source>Reset</source>
1117 <target state="translated">Reiniciar</target> 1135 <target state="translated">Reiniciar</target>
1118 <note priority="1" from="description">Password reset button</note> 1136 <note priority="1" from="description">Password reset button</note>
1119 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1137
1120 </trans-unit> 1138 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1121 <trans-unit id="4319634264526091601" datatype="html"> 1139 <trans-unit id="4319634264526091601" datatype="html">
1122 <source>on this instance</source> 1140 <source>on this instance</source>
1123 <target state="translated">en esta instancia</target> 1141 <target state="translated">en esta instancia</target>
@@ -1445,9 +1463,9 @@ The link will expire within 1 hour.</source>
1445 <trans-unit id="2308975396733519902"> 1463 <trans-unit id="2308975396733519902">
1446 <source>Create an account</source> 1464 <source>Create an account</source>
1447 <target>Crear una cuenta</target> 1465 <target>Crear una cuenta</target>
1448 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1466
1449 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1467
1450 </trans-unit> 1468 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1451 <trans-unit id="3058024914967508975" datatype="html"> 1469 <trans-unit id="3058024914967508975" datatype="html">
1452 <source>My videos</source> 1470 <source>My videos</source>
1453 <target state="translated">Mis videos</target> 1471 <target state="translated">Mis videos</target>
@@ -1514,10 +1532,10 @@ The link will expire within 1 hour.</source>
1514 <trans-unit id="1504521795586863905" datatype="html"> 1532 <trans-unit id="1504521795586863905" datatype="html">
1515 <source>VIDEOS</source> 1533 <source>VIDEOS</source>
1516 <target state="translated">VÍDEOS</target> 1534 <target state="translated">VÍDEOS</target>
1517 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1535
1518 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1536
1519 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1537
1520 </trans-unit> 1538 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1521 <trans-unit id="667372110624203230" datatype="html"> 1539 <trans-unit id="667372110624203230" datatype="html">
1522 <source>Import jobs concurrency</source> 1540 <source>Import jobs concurrency</source>
1523 <target state="translated">Importar simultaneidad de trabajos</target> 1541 <target state="translated">Importar simultaneidad de trabajos</target>
@@ -1596,8 +1614,8 @@ The link will expire within 1 hour.</source>
1596 <trans-unit id="4424964105331349857" datatype="html"> 1614 <trans-unit id="4424964105331349857" datatype="html">
1597 <source>I'm a teapot</source> 1615 <source>I'm a teapot</source>
1598 <target state="translated">Soy una tetera</target> 1616 <target state="translated">Soy una tetera</target>
1599 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1617
1600 </trans-unit> 1618 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1601 <trans-unit id="1597262876035959248" datatype="html"> 1619 <trans-unit id="1597262876035959248" datatype="html">
1602 <source>That's an error.</source> 1620 <source>That's an error.</source>
1603 <target state="translated">Eso es un error.</target> 1621 <target state="translated">Eso es un error.</target>
@@ -1690,8 +1708,8 @@ The link will expire within 1 hour.</source>
1690 <trans-unit id="2971365540217107489" datatype="html"> 1708 <trans-unit id="2971365540217107489" datatype="html">
1691 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1709 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1692 <target state="translated">El medio es demasiado grande para el servidor. Comuníquese con su administrador si desea aumentar el tamaño del límite.</target> 1710 <target state="translated">El medio es demasiado grande para el servidor. Comuníquese con su administrador si desea aumentar el tamaño del límite.</target>
1693 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1711
1694 </trans-unit> 1712 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1695 <trans-unit id="5131854469652959713" datatype="html"> 1713 <trans-unit id="5131854469652959713" datatype="html">
1696 <source>GLOBAL SEARCH</source> 1714 <source>GLOBAL SEARCH</source>
1697 <target state="translated">BÚSQUEDA GLOBAL</target> 1715 <target state="translated">BÚSQUEDA GLOBAL</target>
@@ -2429,8 +2447,8 @@ The link will expire within 1 hour.</source>
2429 <trans-unit id="6161604372916832458" datatype="html"> 2447 <trans-unit id="6161604372916832458" datatype="html">
2430 <source>Upload on hold</source> 2448 <source>Upload on hold</source>
2431 <target state="translated">Subir en espera</target> 2449 <target state="translated">Subir en espera</target>
2432 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2450
2433 </trans-unit> 2451 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2434 <trans-unit id="285180972645018518" datatype="html"> 2452 <trans-unit id="285180972645018518" datatype="html">
2435 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2453 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2436 <target state="translated">Lo sentimos, la función de carga está deshabilitada para su cuenta. Si desea agregar videos, un administrador debe desbloquear su cuota.</target> 2454 <target state="translated">Lo sentimos, la función de carga está deshabilitada para su cuenta. Si desea agregar videos, un administrador debe desbloquear su cuota.</target>
@@ -3113,11 +3131,7 @@ The link will expire within 1 hour.</source>
3113 <target>ID</target> 3131 <target>ID</target>
3114 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3132 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3115 </trans-unit> 3133 </trans-unit>
3116 <trans-unit id="2265605798180116441" datatype="html"> 3134
3117 <source>Follower handle</source>
3118 <target state="translated">Control de seguidor</target>
3119 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3120 </trans-unit>
3121 <trans-unit id="5911214550882917183"> 3135 <trans-unit id="5911214550882917183">
3122 <source>State</source> 3136 <source>State</source>
3123 <target>Estado</target> 3137 <target>Estado</target>
@@ -3185,11 +3199,7 @@ The link will expire within 1 hour.</source>
3185 </target> 3199 </target>
3186 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3200 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3187 </trans-unit> 3201 </trans-unit>
3188 <trans-unit id="6641024648411549335"> 3202
3189 <source>Host</source>
3190 <target>Host</target>
3191 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3192 </trans-unit>
3193 <trans-unit id="6571718060636962350" datatype="html"> 3203 <trans-unit id="6571718060636962350" datatype="html">
3194 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3204 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3195 <target state="translated">Redundancia permitida <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 3205 <target state="translated">Redundancia permitida <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -3198,9 +3208,9 @@ The link will expire within 1 hour.</source>
3198 <trans-unit id="9160510009013134726" datatype="html"> 3208 <trans-unit id="9160510009013134726" datatype="html">
3199 <source>Unfollow</source> 3209 <source>Unfollow</source>
3200 <target state="translated">Dejar de seguir</target> 3210 <target state="translated">Dejar de seguir</target>
3201 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3211
3202 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3212
3203 </trans-unit> 3213 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3204 <trans-unit id="8246779176913476983" datatype="html"> 3214 <trans-unit id="8246779176913476983" datatype="html">
3205 <source>Open instance in a new tab</source> 3215 <source>Open instance in a new tab</source>
3206 <target state="translated">Abrir instancia en una pestaña nueva</target> 3216 <target state="translated">Abrir instancia en una pestaña nueva</target>
@@ -3211,28 +3221,20 @@ The link will expire within 1 hour.</source>
3211 <trans-unit id="9132918641931433659" datatype="html"> 3221 <trans-unit id="9132918641931433659" datatype="html">
3212 <source>No host found matching current filters.</source> 3222 <source>No host found matching current filters.</source>
3213 <target state="translated">No se ha encontrado ningún host que coincida con los filtros actuales.</target> 3223 <target state="translated">No se ha encontrado ningún host que coincida con los filtros actuales.</target>
3214 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3224
3215 </trans-unit> 3225 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3216 <trans-unit id="7274241885665071790" datatype="html"> 3226 <trans-unit id="7274241885665071790" datatype="html">
3217 <source>Your instance is not following anyone.</source> 3227 <source>Your instance is not following anyone.</source>
3218 <target state="translated">Tu instancia no sigue a nadie.</target> 3228 <target state="translated">Tu instancia no sigue a nadie.</target>
3219 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3229
3220 </trans-unit> 3230 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3221 <trans-unit id="4774348799569692380" datatype="html"> 3231 <trans-unit id="4774348799569692380" datatype="html">
3222 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3232 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3223 <target state="translated">Mostrando <x id="INTERPOLATION"/> a <x id="INTERPOLATION_1"/> de <x id="INTERPOLATION_2"/> hosts</target> 3233 <target state="translated">Mostrando <x id="INTERPOLATION"/> a <x id="INTERPOLATION_1"/> de <x id="INTERPOLATION_2"/> hosts</target>
3224 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3234 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3225 </trans-unit> 3235 </trans-unit>
3226 <trans-unit id="6275803119759621687" datatype="html"> 3236
3227 <source>Follow domains</source> 3237
3228 <target state="translated">Seguir dominios</target>
3229 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3230 </trans-unit>
3231 <trans-unit id="1268699198448750610" datatype="html">
3232 <source>Follow instances</source>
3233 <target state="translated">Seguir instancias</target>
3234 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3235 </trans-unit>
3236 <trans-unit id="9216117865911519658" datatype="html"> 3238 <trans-unit id="9216117865911519658" datatype="html">
3237 <source>Action</source> 3239 <source>Action</source>
3238 <target state="translated">Acción</target> 3240 <target state="translated">Acción</target>
@@ -3282,11 +3284,11 @@ The link will expire within 1 hour.</source>
3282 <trans-unit id="5248717555542428023"> 3284 <trans-unit id="5248717555542428023">
3283 <source>Username</source> 3285 <source>Username</source>
3284 <target>Nombre de usuario</target> 3286 <target>Nombre de usuario</target>
3285 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3287
3286 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3288
3287 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3289
3288 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3290
3289 </trans-unit> 3291 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3290 <trans-unit id="5428411040014095392" datatype="html"> 3292 <trans-unit id="5428411040014095392" datatype="html">
3291 <source>e.g. jane_doe</source> 3293 <source>e.g. jane_doe</source>
3292 <target state="translated">ejemplo: jane_doe</target> 3294 <target state="translated">ejemplo: jane_doe</target>
@@ -3314,9 +3316,9 @@ The link will expire within 1 hour.</source>
3314 <trans-unit id="4145496584631696119"> 3316 <trans-unit id="4145496584631696119">
3315 <source>Role</source> 3317 <source>Role</source>
3316 <target>Rol</target> 3318 <target>Rol</target>
3317 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3319
3318 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3320
3319 </trans-unit> 3321 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3320 <trans-unit id="7046347992315328430" datatype="html"> 3322 <trans-unit id="7046347992315328430" datatype="html">
3321 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3323 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3322 <target state="translated">La transcodificación está habilitada. La cuota de video solo tiene en cuenta el peso <x id="START_TAG_STRONG"/>original <x id="CLOSE_TAG_STRONG"/> del vídeo. <x id="LINE_BREAK"/> Como máximo, este usuario podría subir ~ <x id="INTERPOLATION"/>. </target> 3324 <target state="translated">La transcodificación está habilitada. La cuota de video solo tiene en cuenta el peso <x id="START_TAG_STRONG"/>original <x id="CLOSE_TAG_STRONG"/> del vídeo. <x id="LINE_BREAK"/> Como máximo, este usuario podría subir ~ <x id="INTERPOLATION"/>. </target>
@@ -3333,15 +3335,9 @@ The link will expire within 1 hour.</source>
3333 <trans-unit id="2622255144026150901" datatype="html"> 3335 <trans-unit id="2622255144026150901" datatype="html">
3334 <source>Auth plugin</source> 3336 <source>Auth plugin</source>
3335 <target state="translated">Complemento de autenticación</target> 3337 <target state="translated">Complemento de autenticación</target>
3336 <context-group purpose="location"> 3338
3337 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3339
3338 <context context-type="linenumber">188</context> 3340 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3339 </context-group>
3340 <context-group purpose="location">
3341 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3342 <context context-type="linenumber">188</context>
3343 </context-group>
3344 </trans-unit>
3345 <trans-unit id="588099657508661970" datatype="html"> 3341 <trans-unit id="588099657508661970" datatype="html">
3346 <source>None (local authentication)</source> 3342 <source>None (local authentication)</source>
3347 <target state="translated">Ninguna autenticación local</target> 3343 <target state="translated">Ninguna autenticación local</target>
@@ -3592,6 +3588,12 @@ The link will expire within 1 hour.</source>
3592 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3588 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3593 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3589 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3594 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3590 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3591 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3592 <source>Follower</source><target state="new">Follower</target>
3593 <context-group purpose="location">
3594 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3595 <context context-type="linenumber">24</context>
3596 </context-group>
3595 </trans-unit> 3597 </trans-unit>
3596 <trans-unit id="4691552465058437520" datatype="html"> 3598 <trans-unit id="4691552465058437520" datatype="html">
3597 <source>Commented video</source> 3599 <source>Commented video</source>
@@ -3891,8 +3893,8 @@ The link will expire within 1 hour.</source>
3891 <trans-unit id="4917252294930256268" datatype="html"> 3893 <trans-unit id="4917252294930256268" datatype="html">
3892 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3894 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3893 <target state="translated">Parece que no estás en un servidor HTTPS. Su servidor web necesita tener TLS activado para seguir otros servidores.</target> 3895 <target state="translated">Parece que no estás en un servidor HTTPS. Su servidor web necesita tener TLS activado para seguir otros servidores.</target>
3894 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 3896
3895 </trans-unit> 3897 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3896 <trans-unit id="4058814854824495833" datatype="html"> 3898 <trans-unit id="4058814854824495833" datatype="html">
3897 <source>Mute domains</source> 3899 <source>Mute domains</source>
3898 <target state="translated">Dominios silenciados</target> 3900 <target state="translated">Dominios silenciados</target>
@@ -5842,11 +5844,8 @@ color: red;
5842 <trans-unit id="5512878593724620692" datatype="html"> 5844 <trans-unit id="5512878593724620692" datatype="html">
5843 <source>CHANNELS</source> 5845 <source>CHANNELS</source>
5844 <target state="translated">CANALES</target> 5846 <target state="translated">CANALES</target>
5845 <context-group purpose="location"> 5847
5846 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5848 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5847 <context context-type="linenumber">82</context>
5848 </context-group>
5849 </trans-unit>
5850 <trans-unit id="3666829335406793239" datatype="html"> 5849 <trans-unit id="3666829335406793239" datatype="html">
5851 <source>This account does not have channels.</source> 5850 <source>This account does not have channels.</source>
5852 <target state="translated">Esta cuenta no tiene canales.</target> 5851 <target state="translated">Esta cuenta no tiene canales.</target>
@@ -5885,6 +5884,12 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
5885channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 5884channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
5886 <target state="translated">¿Realmente quieres eliminar <x id="PH" equiv-text="videoChannel.displayName"/>? Se eliminarán<x id="PH_1" equiv-text="videoChannel.videosCount"/>videos subidos en este canal ¡y no podrás crear otro canal con el mismo nombre (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target> 5885 <target state="translated">¿Realmente quieres eliminar <x id="PH" equiv-text="videoChannel.displayName"/>? Se eliminarán<x id="PH_1" equiv-text="videoChannel.videosCount"/>videos subidos en este canal ¡y no podrás crear otro canal con el mismo nombre (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target>
5887 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 5886 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
5887 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
5888 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5889 <context-group purpose="location">
5890 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5891 <context context-type="linenumber">48</context>
5892 </context-group>
5888 </trans-unit> 5893 </trans-unit>
5889 <trans-unit id="5387007581996837469" datatype="html"> 5894 <trans-unit id="5387007581996837469" datatype="html">
5890 <source>My Channels</source> 5895 <source>My Channels</source>
@@ -6398,13 +6403,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6398 <trans-unit id="6979021199788941693"> 6403 <trans-unit id="6979021199788941693">
6399 <source>Your message has been sent.</source> 6404 <source>Your message has been sent.</source>
6400 <target>Su mensaje ha sido enviado.</target> 6405 <target>Su mensaje ha sido enviado.</target>
6401 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group> 6406
6402 </trans-unit> 6407 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6403 <trans-unit id="2072135752262464360"> 6408 <trans-unit id="2072135752262464360">
6404 <source>You already sent this form recently</source> 6409 <source>You already sent this form recently</source>
6405 <target>Ya envió este formulario recientemente</target> 6410 <target>Ya envió este formulario recientemente</target>
6406 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group> 6411
6407 </trans-unit> 6412 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6408 <trans-unit id="819067926858619041" datatype="html"> 6413 <trans-unit id="819067926858619041" datatype="html">
6409 <source>Account videos</source> 6414 <source>Account videos</source>
6410 <target state="translated">Videos de cuenta</target> 6415 <target state="translated">Videos de cuenta</target>
@@ -6449,13 +6454,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6449 <target state="translated"> 6454 <target state="translated">
6450 <x id="PH"/> seguidores de cuenta directa 6455 <x id="PH"/> seguidores de cuenta directa
6451 </target> 6456 </target>
6452 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6457
6453 </trans-unit> 6458 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6454 <trans-unit id="6250999352462648289" datatype="html"> 6459 <trans-unit id="6250999352462648289" datatype="html">
6455 <source>Report this account</source> 6460 <source>Report this account</source>
6456 <target state="translated">Informar sbre esta cuenta</target> 6461 <target state="translated">Informar sbre esta cuenta</target>
6457 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6462
6458 </trans-unit> 6463 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6459 <trans-unit id="1504521795586863905" datatype="html"> 6464 <trans-unit id="1504521795586863905" datatype="html">
6460 <source>VIDEOS</source> 6465 <source>VIDEOS</source>
6461 <target state="translated">VÍDEOS</target> 6466 <target state="translated">VÍDEOS</target>
@@ -6465,31 +6470,21 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6465 <trans-unit id="25349740244798533" datatype="html"> 6470 <trans-unit id="25349740244798533" datatype="html">
6466 <source>Username copied</source> 6471 <source>Username copied</source>
6467 <target state="translated">Nombre de usuario copiado</target> 6472 <target state="translated">Nombre de usuario copiado</target>
6468 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6473
6469 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6474
6470 </trans-unit> 6475 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6471 <trans-unit id="9221735175659318025" datatype="html"> 6476 <trans-unit id="9221735175659318025" datatype="html">
6472 <source>1 subscriber</source> 6477 <source>1 subscriber</source>
6473 <target state="translated">1 suscriptor</target> 6478 <target state="translated">1 suscriptor</target>
6474 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6479
6475 </trans-unit> 6480 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6476 <trans-unit id="4097331874769079975" datatype="html"> 6481 <trans-unit id="4097331874769079975" datatype="html">
6477 <source><x id="PH"/> subscribers</source> 6482 <source><x id="PH"/> subscribers</source>
6478 <target state="translated"><x id="PH"/> suscriptores</target> 6483 <target state="translated"><x id="PH"/> suscriptores</target>
6479 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6484
6480 </trans-unit> 6485 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6481 <trans-unit id="4682675125751819107" datatype="html"> 6486
6482 <source>Instances you follow</source> 6487
6483 <target state="translated">Instancias que sigues</target>
6484 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6485 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6486 </trans-unit>
6487 <trans-unit id="8899833753704589712" datatype="html">
6488 <source>Instances following you</source>
6489 <target state="translated">Instancias que te siguen</target>
6490 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6491 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6492 </trans-unit>
6493 <trans-unit id="1035838766454786107" datatype="html"> 6488 <trans-unit id="1035838766454786107" datatype="html">
6494 <source>Audio-only</source> 6489 <source>Audio-only</source>
6495 <target state="translated">Solo audio</target> 6490 <target state="translated">Solo audio</target>
@@ -6539,6 +6534,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6539 <source>Auto (via ffmpeg)</source> 6534 <source>Auto (via ffmpeg)</source>
6540 <target>Auto (vía ffmpeg)</target> 6535 <target>Auto (vía ffmpeg)</target>
6541 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6536 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6537 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6538 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6539 <context-group purpose="location">
6540 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6541 <context context-type="linenumber">3</context>
6542 </context-group>
6542 </trans-unit> 6543 </trans-unit>
6543 <trans-unit id="931255636742351800" datatype="html"> 6544 <trans-unit id="931255636742351800" datatype="html">
6544 <source>No limit</source> 6545 <source>No limit</source>
@@ -6689,18 +6690,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6689 <trans-unit id="2127446333083057097" datatype="html"> 6690 <trans-unit id="2127446333083057097" datatype="html">
6690 <source>Domain is required.</source> 6691 <source>Domain is required.</source>
6691 <target state="translated">Se requiere dominio.</target> 6692 <target state="translated">Se requiere dominio.</target>
6692 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6693
6693 </trans-unit> 6694 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6694 <trans-unit id="6780793142903080663" datatype="html"> 6695 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6695 <source>Domains entered are invalid.</source> 6696 <context-group purpose="location">
6696 <target state="translated">Los dominios ingresados no son válidos.</target> 6697 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6697 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6698 <context context-type="linenumber">93</context>
6698 </trans-unit> 6699 </context-group>
6699 <trans-unit id="5886492514458202177" datatype="html"> 6700 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6700 <source>Domains entered contain duplicates.</source> 6701 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6701 <target state="translated">Los dominios ingresados contienen duplicados.</target> 6702 <context-group purpose="location">
6702 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6703 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6704 <context context-type="linenumber">94</context>
6705 </context-group>
6706 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6707 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6708 <context-group purpose="location">
6709 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6710 <context context-type="linenumber">102</context>
6711 </context-group>
6712 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6713 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6714 <context-group purpose="location">
6715 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6716 <context context-type="linenumber">103</context>
6717 </context-group>
6703 </trans-unit> 6718 </trans-unit>
6719
6720
6704 <trans-unit id="240806681889331244"> 6721 <trans-unit id="240806681889331244">
6705 <source>Unlimited</source> 6722 <source>Unlimited</source>
6706 <target>Ilimitado</target> 6723 <target>Ilimitado</target>
@@ -6870,24 +6887,50 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6870 <x id="PH"/> eliminado de seguidores de instancia 6887 <x id="PH"/> eliminado de seguidores de instancia
6871 </target> 6888 </target>
6872 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6889 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6890 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6891 <source>Follow</source><target state="new">Follow</target>
6892 <context-group purpose="location">
6893 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6894 <context context-type="linenumber">3</context>
6895 </context-group>
6896 <context-group purpose="location">
6897 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6898 <context context-type="linenumber">37</context>
6899 </context-group>
6900 <context-group purpose="location">
6901 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6902 <context context-type="linenumber">18</context>
6903 </context-group>
6904 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6905 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6906 <context-group purpose="location">
6907 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6908 <context context-type="linenumber">11</context>
6909 </context-group>
6873 </trans-unit> 6910 </trans-unit>
6874 <trans-unit id="2740793005745065895"> 6911 <trans-unit id="2740793005745065895">
6875 <source><x id="PH"/> is not valid </source> 6912 <source><x id="PH"/> is not valid </source>
6876 <target> 6913 <target>
6877 <x id="PH"/> no es válido 6914 <x id="PH"/> no es válido
6878 </target> 6915 </target>
6879 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6916
6880 </trans-unit> 6917 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6881 <trans-unit id="2355066641781598196"> 6918 <trans-unit id="2355066641781598196">
6882 <source>Follow request(s) sent!</source> 6919 <source>Follow request(s) sent!</source>
6883 <target>¡Petición(es) de seguimiento enviada(s)!</target> 6920 <target>¡Petición(es) de seguimiento enviada(s)!</target>
6884 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6921
6922 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6923 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6924 <context-group purpose="location">
6925 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6926 <context context-type="linenumber">3</context>
6927 </context-group>
6885 </trans-unit> 6928 </trans-unit>
6886 <trans-unit id="4245720728052819482"> 6929 <trans-unit id="4245720728052819482">
6887 <source>Do you really want to unfollow <x id="PH"/>?</source> 6930 <source>Do you really want to unfollow <x id="PH"/>?</source>
6888 <target>¿De verdad quieres dejar de seguir a <x id="PH"/>?</target> 6931 <target>¿De verdad quieres dejar de seguir a <x id="PH"/>?</target>
6889 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6932
6890 </trans-unit> 6933 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6891 <trans-unit id="9160510009013134726"> 6934 <trans-unit id="9160510009013134726">
6892 <source>Unfollow</source> 6935 <source>Unfollow</source>
6893 <target>Dejar de seguir</target> 6936 <target>Dejar de seguir</target>
@@ -6896,8 +6939,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6896 <trans-unit id="3935234189109112926"> 6939 <trans-unit id="3935234189109112926">
6897 <source>You are not following <x id="PH"/> anymore.</source> 6940 <source>You are not following <x id="PH"/> anymore.</source>
6898 <target>Ya no estás siguiendo a <x id="PH"/>.</target> 6941 <target>Ya no estás siguiendo a <x id="PH"/>.</target>
6899 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6942
6900 </trans-unit> 6943 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6901 <trans-unit id="2593763089859685916"> 6944 <trans-unit id="2593763089859685916">
6902 <source>enabled</source> 6945 <source>enabled</source>
6903 <target>habilitada</target> 6946 <target>habilitada</target>
@@ -7369,9 +7412,9 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7369 <trans-unit id="1519954996184640001"> 7412 <trans-unit id="1519954996184640001">
7370 <source>Error</source> 7413 <source>Error</source>
7371 <target>Error</target> 7414 <target>Error</target>
7372 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7415
7373 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7416
7374 </trans-unit> 7417 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7375 <trans-unit id="5076187961693950167" datatype="html"> 7418 <trans-unit id="5076187961693950167" datatype="html">
7376 <source>Standard logs</source> 7419 <source>Standard logs</source>
7377 <target state="translated">Registros estándar</target> 7420 <target state="translated">Registros estándar</target>
@@ -7412,16 +7455,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7412 <target state="translated">Actualizar contraseña de usuario</target> 7455 <target state="translated">Actualizar contraseña de usuario</target>
7413 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7456 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7414 </trans-unit> 7457 </trans-unit>
7415 <trans-unit id="177544274549739411" datatype="html"> 7458
7416 <source>Following list</source> 7459
7417 <target state="translated">Lista de seguidores</target>
7418 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7419 </trans-unit>
7420 <trans-unit id="8092429110007204784" datatype="html">
7421 <source>Followers list</source>
7422 <target state="translated">Lista de seguidores</target>
7423 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7424 </trans-unit>
7425 <trans-unit id="780323526182667308" datatype="html"> 7460 <trans-unit id="780323526182667308" datatype="html">
7426 <source>User <x id="PH"/> updated.</source> 7461 <source>User <x id="PH"/> updated.</source>
7427 <target state="translated">Usuario <x id="PH"/> actualizado.</target> 7462 <target state="translated">Usuario <x id="PH"/> actualizado.</target>
@@ -7457,16 +7492,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7457 <target state="translated">Federación</target> 7492 <target state="translated">Federación</target>
7458 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7493 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7459 </trans-unit> 7494 </trans-unit>
7460 <trans-unit id="4682675125751819107" datatype="html"> 7495
7461 <source>Instances you follow</source> 7496
7462 <target state="translated">Instancias que sigues</target>
7463 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7464 </trans-unit>
7465 <trans-unit id="8899833753704589712" datatype="html">
7466 <source>Instances following you</source>
7467 <target state="translated">Instancias siguiéndote</target>
7468 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7469 </trans-unit>
7470 <trans-unit id="3767259920053407667" datatype="html"> 7497 <trans-unit id="3767259920053407667" datatype="html">
7471 <source>Videos will be deleted, comments will be tombstoned.</source> 7498 <source>Videos will be deleted, comments will be tombstoned.</source>
7472 <target state="translated">Los videos serán eliminados, los comentarios serán destruidos.</target> 7499 <target state="translated">Los videos serán eliminados, los comentarios serán destruidos.</target>
@@ -7497,6 +7524,24 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7497 <target>Establecer la dirección de correo electrónico como Verificada</target> 7524 <target>Establecer la dirección de correo electrónico como Verificada</target>
7498 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7525 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7499 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7526 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7527 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7528 <source>Created</source><target state="new">Created</target>
7529 <context-group purpose="location">
7530 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7531 <context context-type="linenumber">115</context>
7532 </context-group>
7533 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7534 <source>Daily quota</source><target state="new">Daily quota</target>
7535 <context-group purpose="location">
7536 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7537 <context context-type="linenumber">120</context>
7538 </context-group>
7539 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7540 <source>Last login</source><target state="new">Last login</target>
7541 <context-group purpose="location">
7542 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7543 <context context-type="linenumber">122</context>
7544 </context-group>
7500 </trans-unit> 7545 </trans-unit>
7501 <trans-unit id="3403978719736970622"> 7546 <trans-unit id="3403978719736970622">
7502 <source>You cannot ban root.</source> 7547 <source>You cannot ban root.</source>
@@ -7801,13 +7846,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7801 <trans-unit id="1137937154872046253"> 7846 <trans-unit id="1137937154872046253">
7802 <source>Video channel <x id="PH"/> created.</source> 7847 <source>Video channel <x id="PH"/> created.</source>
7803 <target>Canal de vídeo <x id="PH"/> creado.</target> 7848 <target>Canal de vídeo <x id="PH"/> creado.</target>
7804 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 7849
7805 </trans-unit> 7850 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7806 <trans-unit id="8723777130353305761"> 7851 <trans-unit id="8723777130353305761">
7807 <source>This name already exists on this instance.</source> 7852 <source>This name already exists on this instance.</source>
7808 <target>El nombre ya existe en esta instancia.</target> 7853 <target>El nombre ya existe en esta instancia.</target>
7809 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 7854
7810 </trans-unit> 7855 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7811 <trans-unit id="7589345916094713536"> 7856 <trans-unit id="7589345916094713536">
7812 <source>Video channel <x id="PH"/> updated.</source> 7857 <source>Video channel <x id="PH"/> updated.</source>
7813 <target>Canal de vídeo <x id="PH"/> actualizado.</target> 7858 <target>Canal de vídeo <x id="PH"/> actualizado.</target>
@@ -7828,11 +7873,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7828 <target state="translated">Banner eliminado.</target> 7873 <target state="translated">Banner eliminado.</target>
7829 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 7874 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
7830 </trans-unit> 7875 </trans-unit>
7831 <trans-unit id="2575302837003821736" datatype="html"> 7876
7832 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7833 <target state="translated">Escriba el nombre para mostrar del canal de video ( <x id="PH"/>) para confirmar</target>
7834 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
7835 </trans-unit>
7836 <trans-unit id="624066830180032195"> 7877 <trans-unit id="624066830180032195">
7837 <source>Video channel <x id="PH"/> deleted.</source> 7878 <source>Video channel <x id="PH"/> deleted.</source>
7838 <target>Canal de vídeo <x id="PH"/> eliminado.</target> 7879 <target>Canal de vídeo <x id="PH"/> eliminado.</target>
@@ -7984,6 +8025,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7984 <source>Ownership change request sent.</source> 8025 <source>Ownership change request sent.</source>
7985 <target>Solicitud de cambio de titularidad enviada.</target> 8026 <target>Solicitud de cambio de titularidad enviada.</target>
7986 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8027 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8028 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8029 <source>Sort by</source><target state="new">Sort by</target>
8030 <context-group purpose="location">
8031 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8032 <context context-type="linenumber">26</context>
8033 </context-group>
7987 </trans-unit> 8034 </trans-unit>
7988 <trans-unit id="3245220240937722814"> 8035 <trans-unit id="3245220240937722814">
7989 <source>My channels</source> 8036 <source>My channels</source>
@@ -8082,7 +8129,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8082 <target>Suscribirse a la cuenta</target> 8129 <target>Suscribirse a la cuenta</target>
8083 8130
8084 8131
8085 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8132 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8086 <trans-unit id="3131904093925601441" datatype="html"> 8133 <trans-unit id="3131904093925601441" datatype="html">
8087 <source>PLAYLISTS</source> 8134 <source>PLAYLISTS</source>
8088 <target state="translated">LISTAS DE REPRODUCCIÓN</target> 8135 <target state="translated">LISTAS DE REPRODUCCIÓN</target>
@@ -8129,34 +8176,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8129 <trans-unit id="3779524668013120370"> 8176 <trans-unit id="3779524668013120370">
8130 <source>Go to my subscriptions</source> 8177 <source>Go to my subscriptions</source>
8131 <target>Ir a mis suscripciones</target> 8178 <target>Ir a mis suscripciones</target>
8132 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8179
8133 </trans-unit> 8180 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8134 <trans-unit id="1136469849928650779"> 8181 <trans-unit id="1136469849928650779">
8135 <source>Go to my videos</source> 8182 <source>Go to my videos</source>
8136 <target>Ir a mis vídeos</target> 8183 <target>Ir a mis vídeos</target>
8137 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8184
8138 </trans-unit> 8185 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8139 <trans-unit id="7836683738999600376"> 8186 <trans-unit id="7836683738999600376">
8140 <source>Go to my imports</source> 8187 <source>Go to my imports</source>
8141 <target>Ir a mis importaciones</target> 8188 <target>Ir a mis importaciones</target>
8142 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8189
8143 </trans-unit> 8190 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8144 <trans-unit id="7511292153332773503"> 8191 <trans-unit id="7511292153332773503">
8145 <source>Go to my channels</source> 8192 <source>Go to my channels</source>
8146 <target>Ir a mis canales</target> 8193 <target>Ir a mis canales</target>
8147 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8194
8148 </trans-unit> 8195 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8149 <trans-unit id="2013324644839511073" datatype="html"> 8196 <trans-unit id="2013324644839511073" datatype="html">
8150 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8197 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8151Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8198Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8152 <target state="translated">No se pueden recuperar las credenciales del cliente OAuth: <x id="PH" equiv-text="error.text"/>. Asegúrese de haber configurado correctamente PeerTube (config / directorio), en particular la sección "servidor web".</target> 8199 <target state="translated">No se pueden recuperar las credenciales del cliente OAuth: <x id="PH" equiv-text="error.text"/>. Asegúrese de haber configurado correctamente PeerTube (config / directorio), en particular la sección "servidor web".</target>
8153 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8200
8154 </trans-unit> 8201 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8155 <trans-unit id="375263728166936544"> 8202 <trans-unit id="375263728166936544">
8156 <source>You need to reconnect.</source> 8203 <source>You need to reconnect.</source>
8157 <target>Tienes que reconectar.</target> 8204 <target>Tienes que reconectar.</target>
8158 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8205
8159 </trans-unit> 8206 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8160 <trans-unit id="2206638022166154361"> 8207 <trans-unit id="2206638022166154361">
8161 <source>Keyboard Shortcuts:</source> 8208 <source>Keyboard Shortcuts:</source>
8162 <target>Atajos de teclado:</target> 8209 <target>Atajos de teclado:</target>
@@ -8169,6 +8216,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8169 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8216 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8170 <context context-type="linenumber">98</context> 8217 <context context-type="linenumber">98</context>
8171 </context-group> 8218 </context-group>
8219 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8220 <source>In my library</source><target state="new">In my library</target>
8221 <context-group purpose="location">
8222 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8223 <context context-type="linenumber">104</context>
8224 </context-group>
8172 </trans-unit> 8225 </trans-unit>
8173 <trans-unit id="232050922346936574" datatype="html"> 8226 <trans-unit id="232050922346936574" datatype="html">
8174 <source>Trending</source> 8227 <source>Trending</source>
@@ -8197,38 +8250,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8197 <trans-unit id="1266887509445371246"> 8250 <trans-unit id="1266887509445371246">
8198 <source>Incorrect username or password.</source> 8251 <source>Incorrect username or password.</source>
8199 <target>El nombre de usuario o la contraseña son incorrectos.</target> 8252 <target>El nombre de usuario o la contraseña son incorrectos.</target>
8200 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8253
8201 </trans-unit> 8254 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8202 <trans-unit id="6974874606619467663" datatype="html"> 8255 <trans-unit id="6974874606619467663" datatype="html">
8203 <source>Your account is blocked.</source> 8256 <source>Your account is blocked.</source>
8204 <target state="translated">Tu cuenta está bloqueada.</target> 8257 <target state="translated">Tu cuenta está bloqueada.</target>
8205 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8258
8206 </trans-unit> 8259 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8207 <trans-unit id="7939914198003891823" datatype="html"> 8260 <trans-unit id="7939914198003891823" datatype="html">
8208 <source>any language</source> 8261 <source>any language</source>
8209 <target state="translated">cualquier idioma</target> 8262 <target state="translated">cualquier idioma</target>
8210 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8263
8211 </trans-unit> 8264 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8212 <trans-unit id="5633144232269377096" datatype="html"> 8265 <trans-unit id="5633144232269377096" datatype="html">
8213 <source>hide</source> 8266 <source>hide</source>
8214 <target state="translated">esconder</target> 8267 <target state="translated">esconder</target>
8215 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8268
8216 </trans-unit> 8269 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8217 <trans-unit id="8603861867909474404" datatype="html"> 8270 <trans-unit id="8603861867909474404" datatype="html">
8218 <source>blur</source> 8271 <source>blur</source>
8219 <target state="translated">difuminar</target> 8272 <target state="translated">difuminar</target>
8220 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8273
8221 </trans-unit> 8274 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8222 <trans-unit id="4534458451100881847" datatype="html"> 8275 <trans-unit id="4534458451100881847" datatype="html">
8223 <source>display</source> 8276 <source>display</source>
8224 <target state="translated">monitor</target> 8277 <target state="translated">monitor</target>
8225 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8278
8226 </trans-unit> 8279 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8227 <trans-unit id="4467323362722952678" datatype="html"> 8280 <trans-unit id="4467323362722952678" datatype="html">
8228 <source>Unknown</source> 8281 <source>Unknown</source>
8229 <target state="translated">Desconocido</target> 8282 <target state="translated">Desconocido</target>
8230 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8283
8231 </trans-unit> 8284 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8232 <trans-unit id="8781423666414310853"> 8285 <trans-unit id="8781423666414310853">
8233 <source>Your password has been successfully reset!</source> 8286 <source>Your password has been successfully reset!</source>
8234 <target>¡Tu contraseña ha sido restablecida con éxito!</target> 8287 <target>¡Tu contraseña ha sido restablecida con éxito!</target>
@@ -9814,18 +9867,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9814 <trans-unit id="968295009933361070"> 9867 <trans-unit id="968295009933361070">
9815 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9868 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9816 <target>Demasiados intentos, por favor inténtalo de nuevo pasados <x id="PH"/> minutos.</target> 9869 <target>Demasiados intentos, por favor inténtalo de nuevo pasados <x id="PH"/> minutos.</target>
9817 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9870
9818 </trans-unit> 9871 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9819 <trans-unit id="4965472196059235310"> 9872 <trans-unit id="4965472196059235310">
9820 <source>Too many attempts, please try again later.</source> 9873 <source>Too many attempts, please try again later.</source>
9821 <target>Demasiados intentos, por favor inténtelo más tarde.</target> 9874 <target>Demasiados intentos, por favor inténtelo más tarde.</target>
9822 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9875
9823 </trans-unit> 9876 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9824 <trans-unit id="1693549688987384699"> 9877 <trans-unit id="1693549688987384699">
9825 <source>Server error. Please retry later.</source> 9878 <source>Server error. Please retry later.</source>
9826 <target>Error del servidor. Por favor, inténtalo más tarde.</target> 9879 <target>Error del servidor. Por favor, inténtalo más tarde.</target>
9827 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9880
9828 </trans-unit> 9881 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9829 <trans-unit id="5927402622550505067" datatype="html"> 9882 <trans-unit id="5927402622550505067" datatype="html">
9830 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9883 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9831 <target state="translated">Suscrito a todos los canales actuales de <x id="PH"/>. Serás notificado de todos sus nuevos videos.</target> 9884 <target state="translated">Suscrito a todos los canales actuales de <x id="PH"/>. Serás notificado de todos sus nuevos videos.</target>
@@ -10408,35 +10461,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10408 <trans-unit id="3284171506518522275"> 10461 <trans-unit id="3284171506518522275">
10409 <source>Your video was uploaded to your account and is private.</source> 10462 <source>Your video was uploaded to your account and is private.</source>
10410 <target>Tu vídeo ha sido subida a tu cuenta y es privado.</target> 10463 <target>Tu vídeo ha sido subida a tu cuenta y es privado.</target>
10411 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10464
10412 </trans-unit> 10465 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10413 <trans-unit id="5699822024600815733"> 10466 <trans-unit id="5699822024600815733">
10414 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10467 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10415 <target>Pero los datos asociados (etiquetas, descripción...) se perderán, ¿seguro que quieres abandonar esta página?</target> 10468 <target>Pero los datos asociados (etiquetas, descripción...) se perderán, ¿seguro que quieres abandonar esta página?</target>
10416 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10469
10417 </trans-unit> 10470 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10418 <trans-unit id="1219739004043110649"> 10471 <trans-unit id="1219739004043110649">
10419 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10472 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10420 <target>Tu vídeo aún no se ha subido, ¿seguro que quieres abandonar esta página?</target> 10473 <target>Tu vídeo aún no se ha subido, ¿seguro que quieres abandonar esta página?</target>
10421 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10474
10422 </trans-unit> 10475 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10423 <trans-unit id="6932865105766151309" datatype="html"> 10476 <trans-unit id="6932865105766151309" datatype="html">
10424 <source>Upload</source> 10477 <source>Upload</source>
10425 <target state="translated">Subir</target> 10478 <target state="translated">Subir</target>
10426 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10479
10427 </trans-unit> 10480 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10428 <trans-unit id="8278735427925094503" datatype="html"> 10481 <trans-unit id="8278735427925094503" datatype="html">
10429 <source>Upload <x id="PH"/> </source> 10482 <source>Upload <x id="PH"/> </source>
10430 <target state="translated">Cargue 10483 <target state="translated">Cargue
10431 <x id="PH"/> 10484 <x id="PH"/>
10432 </target> 10485 </target>
10433 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10486
10434 </trans-unit> 10487 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10435 <trans-unit id="5981816353437801748"> 10488 <trans-unit id="5981816353437801748">
10436 <source>Video published.</source> 10489 <source>Video published.</source>
10437 <target>Vídeo publicado.</target> 10490 <target>Vídeo publicado.</target>
10438 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10491
10439 </trans-unit> 10492 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10440 <trans-unit id="764164089183618119" datatype="html"> 10493 <trans-unit id="764164089183618119" datatype="html">
10441 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10494 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10442 <target state="translated">¡Usted tiene cambios no guardados! Si te vas, tus cambios se perderán.</target> 10495 <target state="translated">¡Usted tiene cambios no guardados! Si te vas, tus cambios se perderán.</target>
@@ -10483,28 +10536,28 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10483 <trans-unit id="961774488937452220" datatype="html"> 10536 <trans-unit id="961774488937452220" datatype="html">
10484 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10537 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10485 <target state="translated">Este video no está disponible en esta instancia. ¿Quieres ser redirigido a la instancia de origen: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10538 <target state="translated">Este video no está disponible en esta instancia. ¿Quieres ser redirigido a la instancia de origen: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10486 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group> 10539
10487 </trans-unit> 10540 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10488 <trans-unit id="5761611056224181752" datatype="html"> 10541 <trans-unit id="5761611056224181752" datatype="html">
10489 <source>Redirection</source> 10542 <source>Redirection</source>
10490 <target state="translated">Redirección</target> 10543 <target state="translated">Redirección</target>
10491 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group> 10544
10492 </trans-unit> 10545 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10493 <trans-unit id="8858527736400081688"> 10546 <trans-unit id="8858527736400081688">
10494 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10547 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10495 <target>Este vídeo contiene material para adultos o explícito. ¿Seguro que lo quieres ver?</target> 10548 <target>Este vídeo contiene material para adultos o explícito. ¿Seguro que lo quieres ver?</target>
10496 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group> 10549
10497 </trans-unit> 10550 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10498 <trans-unit id="3937119019020041049"> 10551 <trans-unit id="3937119019020041049">
10499 <source>Mature or explicit content</source> 10552 <source>Mature or explicit content</source>
10500 <target>Contenido para adultos o explícito</target> 10553 <target>Contenido para adultos o explícito</target>
10501 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group> 10554
10502 </trans-unit> 10555 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10503 <trans-unit id="1755474755114288376" datatype="html"> 10556 <trans-unit id="1755474755114288376" datatype="html">
10504 <source>Up Next</source> 10557 <source>Up Next</source>
10505 <target state="translated">Hasta la siguiente</target> 10558 <target state="translated">Hasta la siguiente</target>
10506 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group> 10559
10507 </trans-unit> 10560 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10508 <trans-unit id="2159130950882492111" datatype="html"> 10561 <trans-unit id="2159130950882492111" datatype="html">
10509 <source>Cancel</source> 10562 <source>Cancel</source>
10510 <target state="translated">Cancelar</target> 10563 <target state="translated">Cancelar</target>
@@ -10513,63 +10566,63 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10513 <trans-unit id="3354816756665089864" datatype="html"> 10566 <trans-unit id="3354816756665089864" datatype="html">
10514 <source>Autoplay is suspended</source> 10567 <source>Autoplay is suspended</source>
10515 <target state="translated">La reproducción automática está suspendida</target> 10568 <target state="translated">La reproducción automática está suspendida</target>
10516 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group> 10569
10517 </trans-unit> 10570 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10518 <trans-unit id="7895294730547405228" datatype="html"> 10571 <trans-unit id="7895294730547405228" datatype="html">
10519 <source>Enter/exit fullscreen (requires player focus)</source> 10572 <source>Enter/exit fullscreen (requires player focus)</source>
10520 <target state="translated">Entrar/salir de pantalla completa (requiere foco en el reproductor)</target> 10573 <target state="translated">Entrar/salir de pantalla completa (requiere foco en el reproductor)</target>
10521 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group> 10574
10522 </trans-unit> 10575 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10523 <trans-unit id="7618388257165864759" datatype="html"> 10576 <trans-unit id="7618388257165864759" datatype="html">
10524 <source>Play/Pause the video (requires player focus)</source> 10577 <source>Play/Pause the video (requires player focus)</source>
10525 <target state="translated">Reproducir / Pausar el video (requiere foco en el reproductor)</target> 10578 <target state="translated">Reproducir / Pausar el video (requiere foco en el reproductor)</target>
10526 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group> 10579
10527 </trans-unit> 10580 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10528 <trans-unit id="7761890399634216630" datatype="html"> 10581 <trans-unit id="7761890399634216630" datatype="html">
10529 <source>Mute/unmute the video (requires player focus)</source> 10582 <source>Mute/unmute the video (requires player focus)</source>
10530 <target state="translated">Silenciar / activar el video (requiere foco en el reproductor)</target> 10583 <target state="translated">Silenciar / activar el video (requiere foco en el reproductor)</target>
10531 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group> 10584
10532 </trans-unit> 10585 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10533 <trans-unit id="5996585232248234904" datatype="html"> 10586 <trans-unit id="5996585232248234904" datatype="html">
10534 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10587 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10535 <target state="translated">Salte a un porcentaje del video: 0 es 0% y 9 es 90% (requiere foco en el reproductor)</target> 10588 <target state="translated">Salte a un porcentaje del video: 0 es 0% y 9 es 90% (requiere foco en el reproductor)</target>
10536 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group> 10589
10537 </trans-unit> 10590 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10538 <trans-unit id="3748765405903319998" datatype="html"> 10591 <trans-unit id="3748765405903319998" datatype="html">
10539 <source>Increase the volume (requires player focus)</source> 10592 <source>Increase the volume (requires player focus)</source>
10540 <target state="translated">Aumenta el volumen (requiere foco en el reproductor)</target> 10593 <target state="translated">Aumenta el volumen (requiere foco en el reproductor)</target>
10541 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group> 10594
10542 </trans-unit> 10595 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10543 <trans-unit id="5810704036407159982" datatype="html"> 10596 <trans-unit id="5810704036407159982" datatype="html">
10544 <source>Decrease the volume (requires player focus)</source> 10597 <source>Decrease the volume (requires player focus)</source>
10545 <target state="translated">Disminuye el volumen (requiere foco en el reproductor)</target> 10598 <target state="translated">Disminuye el volumen (requiere foco en el reproductor)</target>
10546 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group> 10599
10547 </trans-unit> 10600 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10548 <trans-unit id="2622048822548065691" datatype="html"> 10601 <trans-unit id="2622048822548065691" datatype="html">
10549 <source>Seek the video forward (requires player focus)</source> 10602 <source>Seek the video forward (requires player focus)</source>
10550 <target state="translated">Buscar el video hacia adelante (requiere foco en el reproductor)</target> 10603 <target state="translated">Buscar el video hacia adelante (requiere foco en el reproductor)</target>
10551 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group> 10604
10552 </trans-unit> 10605 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10553 <trans-unit id="6540078205109221153" datatype="html"> 10606 <trans-unit id="6540078205109221153" datatype="html">
10554 <source>Seek the video backward (requires player focus)</source> 10607 <source>Seek the video backward (requires player focus)</source>
10555 <target state="translated">Busque el video hacia atrás (requiere foco en el reproductor)</target> 10608 <target state="translated">Busque el video hacia atrás (requiere foco en el reproductor)</target>
10556 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group> 10609
10557 </trans-unit> 10610 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10558 <trans-unit id="1956491957766210808" datatype="html"> 10611 <trans-unit id="1956491957766210808" datatype="html">
10559 <source>Increase playback rate (requires player focus)</source> 10612 <source>Increase playback rate (requires player focus)</source>
10560 <target state="translated">Aumentar la velocidad de reproducción (requiere foco en el reproductor)</target> 10613 <target state="translated">Aumentar la velocidad de reproducción (requiere foco en el reproductor)</target>
10561 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group> 10614
10562 </trans-unit> 10615 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10563 <trans-unit id="5495529997674803186" datatype="html"> 10616 <trans-unit id="5495529997674803186" datatype="html">
10564 <source>Decrease playback rate (requires player focus)</source> 10617 <source>Decrease playback rate (requires player focus)</source>
10565 <target state="translated">Disminuir la velocidad de reproducción (requiere foco en el reproductor)</target> 10618 <target state="translated">Disminuir la velocidad de reproducción (requiere foco en el reproductor)</target>
10566 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group> 10619
10567 </trans-unit> 10620 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10568 <trans-unit id="3178343147230721210" datatype="html"> 10621 <trans-unit id="3178343147230721210" datatype="html">
10569 <source>Navigate in the video frame by frame (requires player focus)</source> 10622 <source>Navigate in the video frame by frame (requires player focus)</source>
10570 <target state="translated">Navegue en el video cuadro por cuadro (requiere foco en el reproductor)</target> 10623 <target state="translated">Navegue en el video cuadro por cuadro (requiere foco en el reproductor)</target>
10571 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group> 10624
10572 </trans-unit> 10625 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10573 <trans-unit id="8025996572234182184"> 10626 <trans-unit id="8025996572234182184">
10574 <source>Like the video</source> 10627 <source>Like the video</source>
10575 <target>Colocar Me gusta a este vídeo</target> 10628 <target>Colocar Me gusta a este vídeo</target>
diff --git a/client/src/locale/angular.eu-ES.xlf b/client/src/locale/angular.eu-ES.xlf
index 627f6c5d3..d3486f1bc 100644
--- a/client/src/locale/angular.eu-ES.xlf
+++ b/client/src/locale/angular.eu-ES.xlf
@@ -206,7 +206,7 @@
206 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source> 206 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source>
207 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target> 207 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target>
208 208
209 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 209 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
210 <trans-unit id="1486537403020619891" datatype="html"> 210 <trans-unit id="1486537403020619891" datatype="html">
211 <source>My watch history</source> 211 <source>My watch history</source>
212 <target state="translated">Nire ikustaldien erregistroa</target> 212 <target state="translated">Nire ikustaldien erregistroa</target>
@@ -281,12 +281,12 @@
281 281
282 282
283 283
284 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 284 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
285 <trans-unit id="1006562256968398209" datatype="html"> 285 <trans-unit id="1006562256968398209" datatype="html">
286 <source>video</source> 286 <source>video</source>
287 <target state="translated">bideoa</target> 287 <target state="translated">bideoa</target>
288 288
289 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 289 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
290 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 290 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
291 291
292 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 292 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -348,10 +348,10 @@
348 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 348 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
349 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 349 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
350 350
351 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 351 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
352 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 352 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
353 353
354 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 354 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
355 355
356 <trans-unit id="5235042777215655908" datatype="html"> 356 <trans-unit id="5235042777215655908" datatype="html">
357 <source>subtitles</source> 357 <source>subtitles</source>
@@ -759,10 +759,10 @@
759 <trans-unit id="2602586221576511475"> 759 <trans-unit id="2602586221576511475">
760 <source>Video quota</source> 760 <source>Video quota</source>
761 <target>Bideo-kuota</target> 761 <target>Bideo-kuota</target>
762 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 762
763 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 763
764 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 764
765 </trans-unit> 765 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
766 <trans-unit id="1502595455339510144"> 766 <trans-unit id="1502595455339510144">
767 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 767 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
768 <target> 768 <target>
@@ -846,7 +846,31 @@
846 <source>Federation</source> 846 <source>Federation</source>
847 <target state="translated">Federazioa</target> 847 <target state="translated">Federazioa</target>
848 848
849 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 849 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
850 <source>Following</source><target state="new">Following</target>
851 <context-group purpose="location">
852 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
853 <context context-type="linenumber">29</context>
854 </context-group>
855 <context-group purpose="location">
856 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
857 <context context-type="linenumber">31</context>
858 </context-group>
859 <context-group purpose="location">
860 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
861 <context context-type="linenumber">28</context>
862 </context-group>
863 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
864 <source>Followers</source><target state="new">Followers</target>
865 <context-group purpose="location">
866 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
867 <context context-type="linenumber">34</context>
868 </context-group>
869 <context-group purpose="location">
870 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
871 <context context-type="linenumber">37</context>
872 </context-group>
873 </trans-unit>
850 <trans-unit id="3541687134897970106" datatype="html"> 874 <trans-unit id="3541687134897970106" datatype="html">
851 <source>followers</source> 875 <source>followers</source>
852 <target state="translated">jarraitzaile</target> 876 <target state="translated">jarraitzaile</target>
@@ -921,7 +945,7 @@
921 945
922 946
923 947
924 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 948 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
925 <trans-unit id="3616223838716839702"> 949 <trans-unit id="3616223838716839702">
926 <source>Ban this user</source> 950 <source>Ban this user</source>
927 <target>Debekatu erabiltzaile hau</target> 951 <target>Debekatu erabiltzaile hau</target>
@@ -988,19 +1012,13 @@
988 <trans-unit id="7252854992688790751" datatype="html"> 1012 <trans-unit id="7252854992688790751" datatype="html">
989 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1013 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
990 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1014 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
991 <context-group purpose="location"> 1015
992 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1016 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
993 <context context-type="linenumber">60,62</context>
994 </context-group>
995 </trans-unit>
996 <trans-unit id="7215649348148521605" datatype="html"> 1017 <trans-unit id="7215649348148521605" datatype="html">
997 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1018 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
998 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1019 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
999 <context-group purpose="location"> 1020
1000 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1021 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1001 <context context-type="linenumber">65,67</context>
1002 </context-group>
1003 </trans-unit>
1004 <trans-unit id="2392488717875840729"> 1022 <trans-unit id="2392488717875840729">
1005 <source>User</source> 1023 <source>User</source>
1006 <target>Erabiltzailea</target> 1024 <target>Erabiltzailea</target>
@@ -1011,66 +1029,66 @@
1011 <source>Username or email address</source> 1029 <source>Username or email address</source>
1012 <target>Erabiltzaile-izena edo eposta helbidea</target> 1030 <target>Erabiltzaile-izena edo eposta helbidea</target>
1013 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1031 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1032 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1033 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1034 <context-group purpose="location">
1035 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1036 <context context-type="linenumber">33,34</context>
1037 </context-group>
1014 </trans-unit> 1038 </trans-unit>
1015 <trans-unit id="1431416938026210429"> 1039 <trans-unit id="1431416938026210429">
1016 <source>Password</source> 1040 <source>Password</source>
1017 <target>Pasahitza</target> 1041 <target>Pasahitza</target>
1018 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1042
1019 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1043
1020 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1044
1021 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1045
1022 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1046
1023 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1047
1024 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1048
1025 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1049
1026 </trans-unit> 1050 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1027 <trans-unit id="8715156686857791956" datatype="html"> 1051 <trans-unit id="8715156686857791956" datatype="html">
1028 <source>Click here to reset your password</source> 1052 <source>Click here to reset your password</source>
1029 <target state="translated">Egin klik hemen zure pasahitza berrezartzeko</target> 1053 <target state="translated">Egin klik hemen zure pasahitza berrezartzeko</target>
1030 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1054
1031 </trans-unit> 1055 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1032 <trans-unit id="892063502898494584" datatype="html"> 1056 <trans-unit id="892063502898494584" datatype="html">
1033 <source>I forgot my password</source> 1057 <source>I forgot my password</source>
1034 <target state="translated">Pasahitza ahaztu dut</target> 1058 <target state="translated">Pasahitza ahaztu dut</target>
1035 <context-group purpose="location"> 1059
1036 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1060 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1037 <context context-type="linenumber">47</context>
1038 </context-group>
1039 </trans-unit>
1040 <trans-unit id="2101170466365500913" datatype="html"> 1061 <trans-unit id="2101170466365500913" datatype="html">
1041 <source>Logging into an account lets you publish content</source> 1062 <source>Logging into an account lets you publish content</source>
1042 <target state="translated">Kontu batekin sartzeak edukia argitaratzea ahalbidetuko dizu</target> 1063 <target state="translated">Kontu batekin sartzeak edukia argitaratzea ahalbidetuko dizu</target>
1043 <context-group purpose="location"> 1064
1044 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1065 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1045 <context context-type="linenumber">56,57</context>
1046 </context-group>
1047 </trans-unit>
1048 <trans-unit id="2454050363478003966"> 1066 <trans-unit id="2454050363478003966">
1049 <source>Login</source> 1067 <source>Login</source>
1050 <target>Hasi saioa</target> 1068 <target>Hasi saioa</target>
1051 1069
1052 1070
1053 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1071 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1054 <trans-unit id="3183213940445113677" datatype="html"> 1072 <trans-unit id="3183213940445113677" datatype="html">
1055 <source>Or sign in with</source> 1073 <source>Or sign in with</source>
1056 <target state="translated">Edo hasi saioa honekin</target> 1074 <target state="translated">Edo hasi saioa honekin</target>
1057 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1075
1058 </trans-unit> 1076 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1059 <trans-unit id="3238209155172574367"> 1077 <trans-unit id="3238209155172574367">
1060 <source>Forgot your password</source> 1078 <source>Forgot your password</source>
1061 <target>Pasahitza ahaztu duzu</target> 1079 <target>Pasahitza ahaztu duzu</target>
1062 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1080
1063 </trans-unit> 1081 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1064 <trans-unit id="87327320394367488" datatype="html"> 1082 <trans-unit id="87327320394367488" datatype="html">
1065 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1083 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1066 <target state="translated">Sentitzen dugu, ezin duzu zure pasahitza berreskuratu, instantziaren administratzaileak ez baitzuen PeerTubeko posta elektronikoko sistema konfiguratu.</target> 1084 <target state="translated">Sentitzen dugu, ezin duzu zure pasahitza berreskuratu, instantziaren administratzaileak ez baitzuen PeerTubeko posta elektronikoko sistema konfiguratu.</target>
1067 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1085
1068 </trans-unit> 1086 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1069 <trans-unit id="3188014010833256853" datatype="html"> 1087 <trans-unit id="3188014010833256853" datatype="html">
1070 <source>Enter your email address and we will send you a link to reset your password.</source> 1088 <source>Enter your email address and we will send you a link to reset your password.</source>
1071 <target state="translated">Sartu zure eposta helbidea eta zure pasahitza berrezartzeko esteka bat bidaliko dizugu.</target> 1089 <target state="translated">Sartu zure eposta helbidea eta zure pasahitza berrezartzeko esteka bat bidaliko dizugu.</target>
1072 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1090
1073 </trans-unit> 1091 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1074 <trans-unit id="1190256911880544559" datatype="html"> 1092 <trans-unit id="1190256911880544559" datatype="html">
1075 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1093 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1076The link will expire within 1 hour.</source> 1094The link will expire within 1 hour.</source>
@@ -1080,26 +1098,26 @@ The link will expire within 1 hour.</source>
1080 <trans-unit id="4768749765465246664"> 1098 <trans-unit id="4768749765465246664">
1081 <source>Email</source> 1099 <source>Email</source>
1082 <target>Eposta</target> 1100 <target>Eposta</target>
1083 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1101
1084 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1102
1085 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1103
1086 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1104
1087 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1105
1088 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1106
1089 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1107
1090 </trans-unit> 1108 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1091 <trans-unit id="3967269098753656610"> 1109 <trans-unit id="3967269098753656610">
1092 <source>Email address</source> 1110 <source>Email address</source>
1093 <target>Eposta helbidea</target> 1111 <target>Eposta helbidea</target>
1094 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1112
1095 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1113
1096 </trans-unit> 1114 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1097 <trans-unit id="7808756054397155068" datatype="html"> 1115 <trans-unit id="7808756054397155068" datatype="html">
1098 <source>Reset</source> 1116 <source>Reset</source>
1099 <target state="translated">Berrezarri</target> 1117 <target state="translated">Berrezarri</target>
1100 <note priority="1" from="description">Password reset button</note> 1118 <note priority="1" from="description">Password reset button</note>
1101 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1119
1102 </trans-unit> 1120 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1103 1121
1104 <trans-unit id="4319634264526091601" datatype="html"> 1122 <trans-unit id="4319634264526091601" datatype="html">
1105 <source>on this instance</source> 1123 <source>on this instance</source>
@@ -1438,7 +1456,7 @@ The link will expire within 1 hour.</source>
1438 <target>Sortu kontu bat</target> 1456 <target>Sortu kontu bat</target>
1439 1457
1440 1458
1441 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1459 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1442 1460
1443 <trans-unit id="3058024914967508975" datatype="html"> 1461 <trans-unit id="3058024914967508975" datatype="html">
1444 <source>My videos</source> 1462 <source>My videos</source>
@@ -1495,7 +1513,7 @@ The link will expire within 1 hour.</source>
1495 <source>VIDEOS</source> 1513 <source>VIDEOS</source>
1496 <target state="translated">BIDEOAK</target> 1514 <target state="translated">BIDEOAK</target>
1497 1515
1498 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 1516 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1499 <trans-unit id="667372110624203230" datatype="html"> 1517 <trans-unit id="667372110624203230" datatype="html">
1500 <source>Import jobs concurrency</source> 1518 <source>Import jobs concurrency</source>
1501 <target state="new">Import jobs concurrency</target> 1519 <target state="new">Import jobs concurrency</target>
@@ -1575,7 +1593,7 @@ The link will expire within 1 hour.</source>
1575 <source>I'm a teapot</source> 1593 <source>I'm a teapot</source>
1576 <target state="new">I'm a teapot</target> 1594 <target state="new">I'm a teapot</target>
1577 1595
1578 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit> 1596 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1579 <trans-unit id="1597262876035959248" datatype="html"> 1597 <trans-unit id="1597262876035959248" datatype="html">
1580 <source>That's an error.</source> 1598 <source>That's an error.</source>
1581 <target state="new">That's an error.</target> 1599 <target state="new">That's an error.</target>
@@ -1659,8 +1677,8 @@ The link will expire within 1 hour.</source>
1659 <trans-unit id="2971365540217107489" datatype="html"> 1677 <trans-unit id="2971365540217107489" datatype="html">
1660 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1678 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1661 <target state="translated">Multimedia-fitxategia handiegia da zerbitzarirako. Kontaktatu administratzailea tamaina-muga handitu nahi baduzu.</target> 1679 <target state="translated">Multimedia-fitxategia handiegia da zerbitzarirako. Kontaktatu administratzailea tamaina-muga handitu nahi baduzu.</target>
1662 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1680
1663 </trans-unit> 1681 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1664 1682
1665 <trans-unit id="5131854469652959713" datatype="html"> 1683 <trans-unit id="5131854469652959713" datatype="html">
1666 <source>GLOBAL SEARCH</source> 1684 <source>GLOBAL SEARCH</source>
@@ -2346,7 +2364,7 @@ The link will expire within 1 hour.</source>
2346 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2364 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2347 <source>Upload on hold</source><target state="new">Upload on hold</target> 2365 <source>Upload on hold</source><target state="new">Upload on hold</target>
2348 2366
2349 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2367 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2350 <trans-unit id="285180972645018518" datatype="html"> 2368 <trans-unit id="285180972645018518" datatype="html">
2351 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2369 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2352 <target state="translated">Igotzeko-funtzioa desgaituta dago zure konturako. Bideoak gehitu nahi badituzu, administratzaile batek zure kuota desblokeatu behar du.</target> 2370 <target state="translated">Igotzeko-funtzioa desgaituta dago zure konturako. Bideoak gehitu nahi badituzu, administratzaile batek zure kuota desblokeatu behar du.</target>
@@ -3070,11 +3088,7 @@ The link will expire within 1 hour.</source>
3070 <target>ID-a</target> 3088 <target>ID-a</target>
3071 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3089 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3072 </trans-unit> 3090 </trans-unit>
3073 <trans-unit id="2265605798180116441" datatype="html"> 3091
3074 <source>Follower handle</source>
3075 <target state="new">Follower handle</target>
3076
3077 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
3078 <trans-unit id="5911214550882917183"> 3092 <trans-unit id="5911214550882917183">
3079 <source>State</source> 3093 <source>State</source>
3080 <target>Egoera</target> 3094 <target>Egoera</target>
@@ -3149,11 +3163,7 @@ The link will expire within 1 hour.</source>
3149 </target> 3163 </target>
3150 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3164 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3151 </trans-unit> 3165 </trans-unit>
3152 <trans-unit id="6641024648411549335"> 3166
3153 <source>Host</source>
3154 <target>Ostalaria </target>
3155
3156 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3157 <trans-unit id="6571718060636962350" datatype="html"> 3167 <trans-unit id="6571718060636962350" datatype="html">
3158 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3168 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3159 <target state="new">Redundancy allowed 3169 <target state="new">Redundancy allowed
@@ -3166,7 +3176,7 @@ The link will expire within 1 hour.</source>
3166 <source>Unfollow</source> 3176 <source>Unfollow</source>
3167 <target state="new">Unfollow</target> 3177 <target state="new">Unfollow</target>
3168 3178
3169 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3179 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3170 <trans-unit id="8246779176913476983" datatype="html"> 3180 <trans-unit id="8246779176913476983" datatype="html">
3171 <source>Open instance in a new tab</source> 3181 <source>Open instance in a new tab</source>
3172 <target state="translated">Zabaldu instantzia fitxa berri batean</target> 3182 <target state="translated">Zabaldu instantzia fitxa berri batean</target>
@@ -3178,12 +3188,12 @@ The link will expire within 1 hour.</source>
3178 <source>No host found matching current filters.</source> 3188 <source>No host found matching current filters.</source>
3179 <target state="translated">Ez da aurkitu iragazkiekin bat datorren ostalaririk.</target> 3189 <target state="translated">Ez da aurkitu iragazkiekin bat datorren ostalaririk.</target>
3180 3190
3181 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3191 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3182 <trans-unit id="7274241885665071790" datatype="html"> 3192 <trans-unit id="7274241885665071790" datatype="html">
3183 <source>Your instance is not following anyone.</source> 3193 <source>Your instance is not following anyone.</source>
3184 <target state="translated">Zure instantziak ez du besterik jarraitzen</target> 3194 <target state="translated">Zure instantziak ez du besterik jarraitzen</target>
3185 3195
3186 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3196 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3187 <trans-unit id="4774348799569692380" datatype="html"> 3197 <trans-unit id="4774348799569692380" datatype="html">
3188 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3198 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3189 <target state="new">Showing 3199 <target state="new">Showing
@@ -3193,16 +3203,8 @@ The link will expire within 1 hour.</source>
3193 </target> 3203 </target>
3194 3204
3195 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3205 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3196 <trans-unit id="6275803119759621687" datatype="html"> 3206
3197 <source>Follow domains</source> 3207 <trans-unit id="9216117865911519658" datatype="html">
3198 <target state="translated">Jarraitu domeinuak</target>
3199
3200 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit>
3201 <trans-unit id="1268699198448750610" datatype="html">
3202 <source>Follow instances</source>
3203 <target state="new">Follow instances</target>
3204
3205 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3206 <source>Action</source><target state="new">Action</target> 3208 <source>Action</source><target state="new">Action</target>
3207 3209
3208 3210
@@ -3249,11 +3251,11 @@ The link will expire within 1 hour.</source>
3249 <trans-unit id="5248717555542428023"> 3251 <trans-unit id="5248717555542428023">
3250 <source>Username</source> 3252 <source>Username</source>
3251 <target>Erabiltzaile izena</target> 3253 <target>Erabiltzaile izena</target>
3252 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3254
3253 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3255
3254 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3256
3255 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3257
3256 </trans-unit> 3258 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3257 <trans-unit id="5428411040014095392" datatype="html"> 3259 <trans-unit id="5428411040014095392" datatype="html">
3258 <source>e.g. jane_doe</source> 3260 <source>e.g. jane_doe</source>
3259 <target state="new">e.g. jane_doe</target> 3261 <target state="new">e.g. jane_doe</target>
@@ -3281,9 +3283,9 @@ The link will expire within 1 hour.</source>
3281 <trans-unit id="4145496584631696119"> 3283 <trans-unit id="4145496584631696119">
3282 <source>Role</source> 3284 <source>Role</source>
3283 <target>Rola</target> 3285 <target>Rola</target>
3284 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3286
3285 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3287
3286 </trans-unit> 3288 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3287 <trans-unit id="7046347992315328430" datatype="html"> 3289 <trans-unit id="7046347992315328430" datatype="html">
3288 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3290 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3289 <target state="new"> 3291 <target state="new">
@@ -3308,15 +3310,9 @@ The link will expire within 1 hour.</source>
3308 <trans-unit id="2622255144026150901" datatype="html"> 3310 <trans-unit id="2622255144026150901" datatype="html">
3309 <source>Auth plugin</source> 3311 <source>Auth plugin</source>
3310 <target state="new">Auth plugin</target> 3312 <target state="new">Auth plugin</target>
3311 <context-group purpose="location"> 3313
3312 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3314
3313 <context context-type="linenumber">188</context> 3315 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3314 </context-group>
3315 <context-group purpose="location">
3316 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3317 <context context-type="linenumber">188</context>
3318 </context-group>
3319 </trans-unit>
3320 <trans-unit id="588099657508661970" datatype="html"> 3316 <trans-unit id="588099657508661970" datatype="html">
3321 <source>None (local authentication)</source> 3317 <source>None (local authentication)</source>
3322 <target state="new">None (local authentication)</target> 3318 <target state="new">None (local authentication)</target>
@@ -3575,7 +3571,13 @@ The link will expire within 1 hour.</source>
3575 3571
3576 3572
3577 3573
3578 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit> 3574 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3575 <source>Follower</source><target state="new">Follower</target>
3576 <context-group purpose="location">
3577 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3578 <context context-type="linenumber">24</context>
3579 </context-group>
3580 </trans-unit>
3579 <trans-unit id="4691552465058437520" datatype="html"> 3581 <trans-unit id="4691552465058437520" datatype="html">
3580 <source>Commented video</source> 3582 <source>Commented video</source>
3581 <target state="new">Commented video</target> 3583 <target state="new">Commented video</target>
@@ -3902,7 +3904,7 @@ The link will expire within 1 hour.</source>
3902 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3904 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3903 <target state="translated">Badirudi ez zaudela HTTPS zerbitzari batean. Zure web zerbitzariak TLS aktibatuta eduki behar du zerbitzariak jarraitu ahal izateko.</target> 3905 <target state="translated">Badirudi ez zaudela HTTPS zerbitzari batean. Zure web zerbitzariak TLS aktibatuta eduki behar du zerbitzariak jarraitu ahal izateko.</target>
3904 3906
3905 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3907 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3906 <trans-unit id="4058814854824495833" datatype="html"> 3908 <trans-unit id="4058814854824495833" datatype="html">
3907 <source>Mute domains</source> 3909 <source>Mute domains</source>
3908 <target state="translated">Mututu domeinuak</target> 3910 <target state="translated">Mututu domeinuak</target>
@@ -5838,11 +5840,8 @@ color: red;
5838 5840
5839 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5841 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5840 <source>CHANNELS</source><target state="new">CHANNELS</target> 5842 <source>CHANNELS</source><target state="new">CHANNELS</target>
5841 <context-group purpose="location"> 5843
5842 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5844 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5843 <context context-type="linenumber">82</context>
5844 </context-group>
5845 </trans-unit>
5846 5845
5847 <trans-unit id="3666829335406793239" datatype="html"> 5846 <trans-unit id="3666829335406793239" datatype="html">
5848 <source>This account does not have channels.</source> 5847 <source>This account does not have channels.</source>
@@ -5882,7 +5881,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
5882It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 5881It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
5883channel with the same name (<x id="PH_2"/>)!</target> 5882channel with the same name (<x id="PH_2"/>)!</target>
5884 5883
5885 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5884 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5885 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5886 <context-group purpose="location">
5887 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5888 <context context-type="linenumber">48</context>
5889 </context-group>
5890 </trans-unit>
5886 <trans-unit id="5387007581996837469" datatype="html"> 5891 <trans-unit id="5387007581996837469" datatype="html">
5887 <source>My Channels</source> 5892 <source>My Channels</source>
5888 <target state="translated">Nire kanalak</target> 5893 <target state="translated">Nire kanalak</target>
@@ -6475,12 +6480,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6475 <source>Your message has been sent.</source> 6480 <source>Your message has been sent.</source>
6476 <target state="new">Your message has been sent.</target> 6481 <target state="new">Your message has been sent.</target>
6477 6482
6478 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6483 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6479 <trans-unit id="2072135752262464360" datatype="html"> 6484 <trans-unit id="2072135752262464360" datatype="html">
6480 <source>You already sent this form recently</source> 6485 <source>You already sent this form recently</source>
6481 <target state="new">You already sent this form recently</target> 6486 <target state="new">You already sent this form recently</target>
6482 6487
6483 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6488 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6484 6489
6485 <trans-unit id="819067926858619041" datatype="html"> 6490 <trans-unit id="819067926858619041" datatype="html">
6486 <source>Account videos</source> 6491 <source>Account videos</source>
@@ -6530,12 +6535,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6530 <x id="PH"/> direct account followers 6535 <x id="PH"/> direct account followers
6531 </target> 6536 </target>
6532 6537
6533 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit> 6538 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6534 <trans-unit id="6250999352462648289" datatype="html"> 6539 <trans-unit id="6250999352462648289" datatype="html">
6535 <source>Report this account</source> 6540 <source>Report this account</source>
6536 <target state="new">Report this account</target> 6541 <target state="new">Report this account</target>
6537 6542
6538 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6543 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6539 6544
6540 6545
6541 <trans-unit id="1504521795586863905" datatype="html"> 6546 <trans-unit id="1504521795586863905" datatype="html">
@@ -6550,27 +6555,19 @@ channel with the same name (<x id="PH_2"/>)!</target>
6550 <target state="new">Username copied</target> 6555 <target state="new">Username copied</target>
6551 6556
6552 6557
6553 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit> 6558 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6554 <trans-unit id="9221735175659318025" datatype="html"> 6559 <trans-unit id="9221735175659318025" datatype="html">
6555 <source>1 subscriber</source> 6560 <source>1 subscriber</source>
6556 <target state="new">1 subscriber</target> 6561 <target state="new">1 subscriber</target>
6557 6562
6558 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit> 6563 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6559 <trans-unit id="4097331874769079975" datatype="html"> 6564 <trans-unit id="4097331874769079975" datatype="html">
6560 <source><x id="PH"/> subscribers</source> 6565 <source><x id="PH"/> subscribers</source>
6561 <target state="new"><x id="PH"/> subscribers</target> 6566 <target state="new"><x id="PH"/> subscribers</target>
6562 6567
6563 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6568 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6564 <trans-unit id="4682675125751819107" datatype="html"> 6569
6565 <source>Instances you follow</source> 6570
6566 <target state="new">Instances you follow</target>
6567
6568 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6569 <trans-unit id="8899833753704589712" datatype="html">
6570 <source>Instances following you</source>
6571 <target state="new">Instances following you</target>
6572
6573 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6574 <trans-unit id="1035838766454786107" datatype="html"> 6571 <trans-unit id="1035838766454786107" datatype="html">
6575 <source>Audio-only</source> 6572 <source>Audio-only</source>
6576 <target state="new">Audio-only</target> 6573 <target state="new">Audio-only</target>
@@ -6620,6 +6617,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6620 <source>Auto (via ffmpeg)</source> 6617 <source>Auto (via ffmpeg)</source>
6621 <target>Automatikoa (ffmpeg bidez)</target> 6618 <target>Automatikoa (ffmpeg bidez)</target>
6622 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6619 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6620 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6621 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6622 <context-group purpose="location">
6623 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6624 <context context-type="linenumber">3</context>
6625 </context-group>
6623 </trans-unit> 6626 </trans-unit>
6624 <trans-unit id="931255636742351800" datatype="html"> 6627 <trans-unit id="931255636742351800" datatype="html">
6625 <source>No limit</source> 6628 <source>No limit</source>
@@ -6762,18 +6765,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
6762 <trans-unit id="2127446333083057097" datatype="html"> 6765 <trans-unit id="2127446333083057097" datatype="html">
6763 <source>Domain is required.</source> 6766 <source>Domain is required.</source>
6764 <target state="new">Domain is required.</target> 6767 <target state="new">Domain is required.</target>
6765 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6768
6766 </trans-unit> 6769 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6767 <trans-unit id="6780793142903080663" datatype="html"> 6770 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6768 <source>Domains entered are invalid.</source> 6771 <context-group purpose="location">
6769 <target state="new">Domains entered are invalid.</target> 6772 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6770 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6773 <context context-type="linenumber">93</context>
6771 </trans-unit> 6774 </context-group>
6772 <trans-unit id="5886492514458202177" datatype="html"> 6775 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6773 <source>Domains entered contain duplicates.</source> 6776 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6774 <target state="new">Domains entered contain duplicates.</target> 6777 <context-group purpose="location">
6775 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6778 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6779 <context context-type="linenumber">94</context>
6780 </context-group>
6781 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6782 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6783 <context-group purpose="location">
6784 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6785 <context context-type="linenumber">102</context>
6786 </context-group>
6787 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6788 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6789 <context-group purpose="location">
6790 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6791 <context context-type="linenumber">103</context>
6792 </context-group>
6776 </trans-unit> 6793 </trans-unit>
6794
6795
6777 <trans-unit id="240806681889331244"> 6796 <trans-unit id="240806681889331244">
6778 <source>Unlimited</source> 6797 <source>Unlimited</source>
6779 <target>Mugagabea</target> 6798 <target>Mugagabea</target>
@@ -6933,26 +6952,52 @@ channel with the same name (<x id="PH_2"/>)!</target>
6933 <x id="PH"/> removed from instance followers 6952 <x id="PH"/> removed from instance followers
6934 </target> 6953 </target>
6935 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6954 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6955 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6956 <source>Follow</source><target state="new">Follow</target>
6957 <context-group purpose="location">
6958 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6959 <context context-type="linenumber">3</context>
6960 </context-group>
6961 <context-group purpose="location">
6962 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6963 <context context-type="linenumber">37</context>
6964 </context-group>
6965 <context-group purpose="location">
6966 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6967 <context context-type="linenumber">18</context>
6968 </context-group>
6969 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6970 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6971 <context-group purpose="location">
6972 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6973 <context context-type="linenumber">11</context>
6974 </context-group>
6936 </trans-unit> 6975 </trans-unit>
6937 <trans-unit id="2740793005745065895"> 6976 <trans-unit id="2740793005745065895">
6938 <source><x id="PH"/> is not valid </source> 6977 <source><x id="PH"/> is not valid </source>
6939 <target> 6978 <target>
6940 <x id="PH"/> baliogabea da 6979 <x id="PH"/> baliogabea da
6941 </target> 6980 </target>
6942 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6981
6943 </trans-unit> 6982 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6944 <trans-unit id="2355066641781598196"> 6983 <trans-unit id="2355066641781598196">
6945 <source>Follow request(s) sent!</source> 6984 <source>Follow request(s) sent!</source>
6946 <target>Jarraitzeko eskaria(k) bidalita!</target> 6985 <target>Jarraitzeko eskaria(k) bidalita!</target>
6947 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6986
6987 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6988 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6989 <context-group purpose="location">
6990 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6991 <context context-type="linenumber">3</context>
6992 </context-group>
6948 </trans-unit> 6993 </trans-unit>
6949 <trans-unit id="4245720728052819482"> 6994 <trans-unit id="4245720728052819482">
6950 <source>Do you really want to unfollow <x id="PH"/>?</source> 6995 <source>Do you really want to unfollow <x id="PH"/>?</source>
6951 <target>Ziur 6996 <target>Ziur
6952 <x id="PH"/> jarraitzeari utzi nahi diozula? 6997 <x id="PH"/> jarraitzeari utzi nahi diozula?
6953 </target> 6998 </target>
6954 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6999
6955 </trans-unit> 7000 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6956 <trans-unit id="9160510009013134726"> 7001 <trans-unit id="9160510009013134726">
6957 <source>Unfollow</source> 7002 <source>Unfollow</source>
6958 <target>Utzi jarraitzeari</target> 7003 <target>Utzi jarraitzeari</target>
@@ -6963,8 +7008,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
6963 <target>Ez duzu jada 7008 <target>Ez duzu jada
6964 <x id="PH"/> jarraitzen. 7009 <x id="PH"/> jarraitzen.
6965 </target> 7010 </target>
6966 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 7011
6967 </trans-unit> 7012 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6968 <trans-unit id="2593763089859685916"> 7013 <trans-unit id="2593763089859685916">
6969 <source>enabled</source> 7014 <source>enabled</source>
6970 <target>gaituta</target> 7015 <target>gaituta</target>
@@ -7431,9 +7476,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7431 <trans-unit id="1519954996184640001"> 7476 <trans-unit id="1519954996184640001">
7432 <source>Error</source> 7477 <source>Error</source>
7433 <target>Errorea</target> 7478 <target>Errorea</target>
7434 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7479
7435 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7480
7436 </trans-unit> 7481 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7437 <trans-unit id="5076187961693950167" datatype="html"> 7482 <trans-unit id="5076187961693950167" datatype="html">
7438 <source>Standard logs</source> 7483 <source>Standard logs</source>
7439 <target state="new">Standard logs</target> 7484 <target state="new">Standard logs</target>
@@ -7478,16 +7523,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7478 <target state="new">Update user password</target> 7523 <target state="new">Update user password</target>
7479 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7524 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7480 </trans-unit> 7525 </trans-unit>
7481 <trans-unit id="177544274549739411" datatype="html"> 7526
7482 <source>Following list</source> 7527
7483 <target state="new">Following list</target>
7484 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7485 </trans-unit>
7486 <trans-unit id="8092429110007204784" datatype="html">
7487 <source>Followers list</source>
7488 <target state="new">Followers list</target>
7489 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7490 </trans-unit>
7491 <trans-unit id="780323526182667308" datatype="html"> 7528 <trans-unit id="780323526182667308" datatype="html">
7492 <source>User <x id="PH"/> updated.</source> 7529 <source>User <x id="PH"/> updated.</source>
7493 <target state="new">User 7530 <target state="new">User
@@ -7527,16 +7564,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7527 <target state="new">Federation</target> 7564 <target state="new">Federation</target>
7528 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7565 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7529 </trans-unit> 7566 </trans-unit>
7530 <trans-unit id="4682675125751819107" datatype="html"> 7567
7531 <source>Instances you follow</source> 7568
7532 <target state="new">Instances you follow</target>
7533 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7534 </trans-unit>
7535 <trans-unit id="8899833753704589712" datatype="html">
7536 <source>Instances following you</source>
7537 <target state="new">Instances following you</target>
7538 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7539 </trans-unit>
7540 <trans-unit id="3767259920053407667" datatype="html"> 7569 <trans-unit id="3767259920053407667" datatype="html">
7541 <source>Videos will be deleted, comments will be tombstoned.</source> 7570 <source>Videos will be deleted, comments will be tombstoned.</source>
7542 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7571 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7567,7 +7596,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7567 <target>Jo eposta egiaztatutzat</target> 7596 <target>Jo eposta egiaztatutzat</target>
7568 7597
7569 7598
7570 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7599 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7600 <source>Created</source><target state="new">Created</target>
7601 <context-group purpose="location">
7602 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7603 <context context-type="linenumber">115</context>
7604 </context-group>
7605 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7606 <source>Daily quota</source><target state="new">Daily quota</target>
7607 <context-group purpose="location">
7608 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7609 <context context-type="linenumber">120</context>
7610 </context-group>
7611 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7612 <source>Last login</source><target state="new">Last login</target>
7613 <context-group purpose="location">
7614 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7615 <context context-type="linenumber">122</context>
7616 </context-group>
7617 </trans-unit>
7571 <trans-unit id="3403978719736970622"> 7618 <trans-unit id="3403978719736970622">
7572 <source>You cannot ban root.</source> 7619 <source>You cannot ban root.</source>
7573 <target>Ezin duzu root debekatu</target> 7620 <target>Ezin duzu root debekatu</target>
@@ -7877,12 +7924,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
7877 <x id="PH"/> bideo kanala sortuta. 7924 <x id="PH"/> bideo kanala sortuta.
7878 </target> 7925 </target>
7879 7926
7880 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7927 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7881 <trans-unit id="8723777130353305761"> 7928 <trans-unit id="8723777130353305761">
7882 <source>This name already exists on this instance.</source> 7929 <source>This name already exists on this instance.</source>
7883 <target>Izen hau hartuta dago instantzia honetan</target> 7930 <target>Izen hau hartuta dago instantzia honetan</target>
7884 7931
7885 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7932 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7886 <trans-unit id="7589345916094713536"> 7933 <trans-unit id="7589345916094713536">
7887 <source>Video channel <x id="PH"/> updated.</source> 7934 <source>Video channel <x id="PH"/> updated.</source>
7888 <target> 7935 <target>
@@ -7899,13 +7946,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7899 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 7946 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7900 7947
7901 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7948 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7902 <trans-unit id="2575302837003821736" datatype="html"> 7949
7903 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7904 <target state="new">Please type the display name of the video channel (
7905 <x id="PH"/>) to confirm
7906 </target>
7907
7908 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7909 <trans-unit id="624066830180032195"> 7950 <trans-unit id="624066830180032195">
7910 <source>Video channel <x id="PH"/> deleted.</source> 7951 <source>Video channel <x id="PH"/> deleted.</source>
7911 <target> 7952 <target>
@@ -8068,6 +8109,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8068 <source>Ownership change request sent.</source> 8109 <source>Ownership change request sent.</source>
8069 <target>Jabetza aldatzeko eskaria bidalita.</target> 8110 <target>Jabetza aldatzeko eskaria bidalita.</target>
8070 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8111 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8112 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8113 <source>Sort by</source><target state="new">Sort by</target>
8114 <context-group purpose="location">
8115 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8116 <context context-type="linenumber">26</context>
8117 </context-group>
8071 </trans-unit> 8118 </trans-unit>
8072 <trans-unit id="3245220240937722814"> 8119 <trans-unit id="3245220240937722814">
8073 <source>My channels</source> 8120 <source>My channels</source>
@@ -8171,7 +8218,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8171 <target>Harpidetu kontura</target> 8218 <target>Harpidetu kontura</target>
8172 8219
8173 8220
8174 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 8221 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
8175 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 8222 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
8176 <context-group purpose="location"> 8223 <context-group purpose="location">
8177 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 8224 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -8217,35 +8264,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8217 <trans-unit id="3779524668013120370"> 8264 <trans-unit id="3779524668013120370">
8218 <source>Go to my subscriptions</source> 8265 <source>Go to my subscriptions</source>
8219 <target>Joan nire harpidetzetara</target> 8266 <target>Joan nire harpidetzetara</target>
8220 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8267
8221 </trans-unit> 8268 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8222 <trans-unit id="1136469849928650779"> 8269 <trans-unit id="1136469849928650779">
8223 <source>Go to my videos</source> 8270 <source>Go to my videos</source>
8224 <target>Joan nire bideoetara</target> 8271 <target>Joan nire bideoetara</target>
8225 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8272
8226 </trans-unit> 8273 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8227 <trans-unit id="7836683738999600376"> 8274 <trans-unit id="7836683738999600376">
8228 <source>Go to my imports</source> 8275 <source>Go to my imports</source>
8229 <target>Joan nire inportazioetara</target> 8276 <target>Joan nire inportazioetara</target>
8230 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8277
8231 </trans-unit> 8278 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8232 <trans-unit id="7511292153332773503"> 8279 <trans-unit id="7511292153332773503">
8233 <source>Go to my channels</source> 8280 <source>Go to my channels</source>
8234 <target>Joan nire kanaletara</target> 8281 <target>Joan nire kanaletara</target>
8235 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8282
8236 </trans-unit> 8283 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8237 <trans-unit id="2013324644839511073" datatype="html"> 8284 <trans-unit id="2013324644839511073" datatype="html">
8238 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8285 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8239Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8286Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8240 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8287 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8241Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8288Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8242 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8289
8243 </trans-unit> 8290 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8244 <trans-unit id="375263728166936544"> 8291 <trans-unit id="375263728166936544">
8245 <source>You need to reconnect.</source> 8292 <source>You need to reconnect.</source>
8246 <target>Berriro konektatu behar duzu.</target> 8293 <target>Berriro konektatu behar duzu.</target>
8247 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8294
8248 </trans-unit> 8295 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8249 <trans-unit id="2206638022166154361"> 8296 <trans-unit id="2206638022166154361">
8250 <source>Keyboard Shortcuts:</source> 8297 <source>Keyboard Shortcuts:</source>
8251 <target>Teklatu laster-bideak:</target> 8298 <target>Teklatu laster-bideak:</target>
@@ -8256,6 +8303,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8256 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8303 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8257 <context context-type="linenumber">98</context> 8304 <context context-type="linenumber">98</context>
8258 </context-group> 8305 </context-group>
8306 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8307 <source>In my library</source><target state="new">In my library</target>
8308 <context-group purpose="location">
8309 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8310 <context context-type="linenumber">104</context>
8311 </context-group>
8259 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 8312 </trans-unit><trans-unit id="232050922346936574" datatype="html">
8260 <source>Trending</source><target state="new">Trending</target> 8313 <source>Trending</source><target state="new">Trending</target>
8261 8314
@@ -8279,38 +8332,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8279 <source>Incorrect username or password.</source> 8332 <source>Incorrect username or password.</source>
8280 <target>Erabiltzaile-izen edo pasahitz okerra.</target> 8333 <target>Erabiltzaile-izen edo pasahitz okerra.</target>
8281 8334
8282 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 8335 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8283 <trans-unit id="6974874606619467663" datatype="html"> 8336 <trans-unit id="6974874606619467663" datatype="html">
8284 <source>Your account is blocked.</source> 8337 <source>Your account is blocked.</source>
8285 <target state="new">Your account is blocked.</target> 8338 <target state="new">Your account is blocked.</target>
8286 8339
8287 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 8340 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8288 <trans-unit id="7939914198003891823" datatype="html"> 8341 <trans-unit id="7939914198003891823" datatype="html">
8289 <source>any language</source> 8342 <source>any language</source>
8290 <target state="new">any language</target> 8343 <target state="new">any language</target>
8291 8344
8292 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8345 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8293 8346
8294 <trans-unit id="5633144232269377096" datatype="html"> 8347 <trans-unit id="5633144232269377096" datatype="html">
8295 <source>hide</source> 8348 <source>hide</source>
8296 <target state="new">hide</target> 8349 <target state="new">hide</target>
8297 8350
8298 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8351 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8299 <trans-unit id="8603861867909474404" datatype="html"> 8352 <trans-unit id="8603861867909474404" datatype="html">
8300 <source>blur</source> 8353 <source>blur</source>
8301 <target state="new">blur</target> 8354 <target state="new">blur</target>
8302 8355
8303 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8356 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8304 <trans-unit id="4534458451100881847" datatype="html"> 8357 <trans-unit id="4534458451100881847" datatype="html">
8305 <source>display</source> 8358 <source>display</source>
8306 <target state="new">display</target> 8359 <target state="new">display</target>
8307 8360
8308 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8361 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8309 <trans-unit id="4467323362722952678" datatype="html"> 8362 <trans-unit id="4467323362722952678" datatype="html">
8310 <source>Unknown</source> 8363 <source>Unknown</source>
8311 <target state="new">Unknown</target> 8364 <target state="new">Unknown</target>
8312 8365
8313 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8366 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8314 <trans-unit id="8781423666414310853"> 8367 <trans-unit id="8781423666414310853">
8315 <source>Your password has been successfully reset!</source> 8368 <source>Your password has been successfully reset!</source>
8316 <target>Zure pasahitza ongi berrezarri da!</target> 8369 <target>Zure pasahitza ongi berrezarri da!</target>
@@ -9897,18 +9950,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9897 <target>Saiakera gehiegi, saiatu berriro geroago, 9950 <target>Saiakera gehiegi, saiatu berriro geroago,
9898 <x id="PH"/> minutu igarotakoan. 9951 <x id="PH"/> minutu igarotakoan.
9899 </target> 9952 </target>
9900 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9953
9901 </trans-unit> 9954 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9902 <trans-unit id="4965472196059235310"> 9955 <trans-unit id="4965472196059235310">
9903 <source>Too many attempts, please try again later.</source> 9956 <source>Too many attempts, please try again later.</source>
9904 <target>Saiakera gehiegi, saiatu berriro geroago.</target> 9957 <target>Saiakera gehiegi, saiatu berriro geroago.</target>
9905 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9958
9906 </trans-unit> 9959 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9907 <trans-unit id="1693549688987384699"> 9960 <trans-unit id="1693549688987384699">
9908 <source>Server error. Please retry later.</source> 9961 <source>Server error. Please retry later.</source>
9909 <target>Zerbitzariaren errorea, Saiatu berriro geroago.</target> 9962 <target>Zerbitzariaren errorea, Saiatu berriro geroago.</target>
9910 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9963
9911 </trans-unit> 9964 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9912 <trans-unit id="5927402622550505067" datatype="html"> 9965 <trans-unit id="5927402622550505067" datatype="html">
9913 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9966 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9914 <target state="new">Subscribed to all current channels of 9967 <target state="new">Subscribed to all current channels of
@@ -10502,35 +10555,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10502 <source>Your video was uploaded to your account and is private.</source> 10555 <source>Your video was uploaded to your account and is private.</source>
10503 <target>Zure bideoa zure kontura igo da eta pribatua da.</target> 10556 <target>Zure bideoa zure kontura igo da eta pribatua da.</target>
10504 10557
10505 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10558 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10506 <trans-unit id="5699822024600815733"> 10559 <trans-unit id="5699822024600815733">
10507 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10560 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10508 <target>Baina dagozkion datuak (etiketak, deskripzioa...) galduko dira, ziur orri hau utzi nahi duzula?</target> 10561 <target>Baina dagozkion datuak (etiketak, deskripzioa...) galduko dira, ziur orri hau utzi nahi duzula?</target>
10509 10562
10510 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10563 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10511 <trans-unit id="1219739004043110649"> 10564 <trans-unit id="1219739004043110649">
10512 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10565 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10513 <target>Zur bideoa ez da igo oraindik, ziur orri hau utzi nahi duzula?</target> 10566 <target>Zur bideoa ez da igo oraindik, ziur orri hau utzi nahi duzula?</target>
10514 10567
10515 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10568 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10516 <trans-unit id="6932865105766151309" datatype="html"> 10569 <trans-unit id="6932865105766151309" datatype="html">
10517 <source>Upload</source> 10570 <source>Upload</source>
10518 <target state="new">Upload</target> 10571 <target state="new">Upload</target>
10519 10572
10520 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10573 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10521 <trans-unit id="8278735427925094503" datatype="html"> 10574 <trans-unit id="8278735427925094503" datatype="html">
10522 <source>Upload <x id="PH"/> </source> 10575 <source>Upload <x id="PH"/> </source>
10523 <target state="new">Upload 10576 <target state="new">Upload
10524 <x id="PH"/> 10577 <x id="PH"/>
10525 </target> 10578 </target>
10526 10579
10527 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10580 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10528 10581
10529 <trans-unit id="5981816353437801748"> 10582 <trans-unit id="5981816353437801748">
10530 <source>Video published.</source> 10583 <source>Video published.</source>
10531 <target>Bideoa argitaratuta.</target> 10584 <target>Bideoa argitaratuta.</target>
10532 10585
10533 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10586 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10534 10587
10535 10588
10536 <trans-unit id="764164089183618119" datatype="html"> 10589 <trans-unit id="764164089183618119" datatype="html">
@@ -10580,27 +10633,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10580 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10633 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10581 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10634 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10582 10635
10583 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10636 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10584 <trans-unit id="5761611056224181752" datatype="html"> 10637 <trans-unit id="5761611056224181752" datatype="html">
10585 <source>Redirection</source> 10638 <source>Redirection</source>
10586 <target state="new">Redirection</target> 10639 <target state="new">Redirection</target>
10587 10640
10588 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10641 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10589 <trans-unit id="8858527736400081688"> 10642 <trans-unit id="8858527736400081688">
10590 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10643 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10591 <target>Bideo honek helduentzako edo hunkigarria den edukia du. Ziur ikusi nahi duzula?</target> 10644 <target>Bideo honek helduentzako edo hunkigarria den edukia du. Ziur ikusi nahi duzula?</target>
10592 10645
10593 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10646 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10594 <trans-unit id="3937119019020041049"> 10647 <trans-unit id="3937119019020041049">
10595 <source>Mature or explicit content</source> 10648 <source>Mature or explicit content</source>
10596 <target>Helduentzako edo hunkigarria den edukia</target> 10649 <target>Helduentzako edo hunkigarria den edukia</target>
10597 10650
10598 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10651 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10599 <trans-unit id="1755474755114288376" datatype="html"> 10652 <trans-unit id="1755474755114288376" datatype="html">
10600 <source>Up Next</source> 10653 <source>Up Next</source>
10601 <target state="new">Up Next</target> 10654 <target state="new">Up Next</target>
10602 10655
10603 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10656 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10604 <trans-unit id="2159130950882492111" datatype="html"> 10657 <trans-unit id="2159130950882492111" datatype="html">
10605 <source>Cancel</source> 10658 <source>Cancel</source>
10606 <target state="new">Cancel</target> 10659 <target state="new">Cancel</target>
@@ -10610,62 +10663,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10610 <source>Autoplay is suspended</source> 10663 <source>Autoplay is suspended</source>
10611 <target state="new">Autoplay is suspended</target> 10664 <target state="new">Autoplay is suspended</target>
10612 10665
10613 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10666 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10614 <trans-unit id="7895294730547405228" datatype="html"> 10667 <trans-unit id="7895294730547405228" datatype="html">
10615 <source>Enter/exit fullscreen (requires player focus)</source> 10668 <source>Enter/exit fullscreen (requires player focus)</source>
10616 <target state="new">Enter/exit fullscreen (requires player focus)</target> 10669 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10617 10670
10618 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10671 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10619 <trans-unit id="7618388257165864759" datatype="html"> 10672 <trans-unit id="7618388257165864759" datatype="html">
10620 <source>Play/Pause the video (requires player focus)</source> 10673 <source>Play/Pause the video (requires player focus)</source>
10621 <target state="new">Play/Pause the video (requires player focus)</target> 10674 <target state="new">Play/Pause the video (requires player focus)</target>
10622 10675
10623 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10676 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10624 <trans-unit id="7761890399634216630" datatype="html"> 10677 <trans-unit id="7761890399634216630" datatype="html">
10625 <source>Mute/unmute the video (requires player focus)</source> 10678 <source>Mute/unmute the video (requires player focus)</source>
10626 <target state="new">Mute/unmute the video (requires player focus)</target> 10679 <target state="new">Mute/unmute the video (requires player focus)</target>
10627 10680
10628 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10681 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10629 <trans-unit id="5996585232248234904" datatype="html"> 10682 <trans-unit id="5996585232248234904" datatype="html">
10630 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10683 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10631 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10684 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10632 10685
10633 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10686 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10634 <trans-unit id="3748765405903319998" datatype="html"> 10687 <trans-unit id="3748765405903319998" datatype="html">
10635 <source>Increase the volume (requires player focus)</source> 10688 <source>Increase the volume (requires player focus)</source>
10636 <target state="new">Increase the volume (requires player focus)</target> 10689 <target state="new">Increase the volume (requires player focus)</target>
10637 10690
10638 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10691 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10639 <trans-unit id="5810704036407159982" datatype="html"> 10692 <trans-unit id="5810704036407159982" datatype="html">
10640 <source>Decrease the volume (requires player focus)</source> 10693 <source>Decrease the volume (requires player focus)</source>
10641 <target state="new">Decrease the volume (requires player focus)</target> 10694 <target state="new">Decrease the volume (requires player focus)</target>
10642 10695
10643 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10696 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10644 <trans-unit id="2622048822548065691" datatype="html"> 10697 <trans-unit id="2622048822548065691" datatype="html">
10645 <source>Seek the video forward (requires player focus)</source> 10698 <source>Seek the video forward (requires player focus)</source>
10646 <target state="new">Seek the video forward (requires player focus)</target> 10699 <target state="new">Seek the video forward (requires player focus)</target>
10647 10700
10648 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10701 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10649 <trans-unit id="6540078205109221153" datatype="html"> 10702 <trans-unit id="6540078205109221153" datatype="html">
10650 <source>Seek the video backward (requires player focus)</source> 10703 <source>Seek the video backward (requires player focus)</source>
10651 <target state="new">Seek the video backward (requires player focus)</target> 10704 <target state="new">Seek the video backward (requires player focus)</target>
10652 10705
10653 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10706 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10654 <trans-unit id="1956491957766210808" datatype="html"> 10707 <trans-unit id="1956491957766210808" datatype="html">
10655 <source>Increase playback rate (requires player focus)</source> 10708 <source>Increase playback rate (requires player focus)</source>
10656 <target state="new">Increase playback rate (requires player focus)</target> 10709 <target state="new">Increase playback rate (requires player focus)</target>
10657 10710
10658 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10711 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10659 <trans-unit id="5495529997674803186" datatype="html"> 10712 <trans-unit id="5495529997674803186" datatype="html">
10660 <source>Decrease playback rate (requires player focus)</source> 10713 <source>Decrease playback rate (requires player focus)</source>
10661 <target state="new">Decrease playback rate (requires player focus)</target> 10714 <target state="new">Decrease playback rate (requires player focus)</target>
10662 10715
10663 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10716 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10664 <trans-unit id="3178343147230721210" datatype="html"> 10717 <trans-unit id="3178343147230721210" datatype="html">
10665 <source>Navigate in the video frame by frame (requires player focus)</source> 10718 <source>Navigate in the video frame by frame (requires player focus)</source>
10666 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 10719 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10667 10720
10668 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10721 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10669 <trans-unit id="8025996572234182184"> 10722 <trans-unit id="8025996572234182184">
10670 <source>Like the video</source> 10723 <source>Like the video</source>
10671 <target>Gehitu bideoa gogokoetara</target> 10724 <target>Gehitu bideoa gogokoetara</target>
diff --git a/client/src/locale/angular.fa-IR.xlf b/client/src/locale/angular.fa-IR.xlf
index 5d8ef8d28..35f589b30 100644
--- a/client/src/locale/angular.fa-IR.xlf
+++ b/client/src/locale/angular.fa-IR.xlf
@@ -170,7 +170,7 @@
170 170
171 171
172 172
173 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 173 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
174 <trans-unit id="1486537403020619891" datatype="html"> 174 <trans-unit id="1486537403020619891" datatype="html">
175 <source>My watch history</source> 175 <source>My watch history</source>
176 <target state="new">My watch history</target> 176 <target state="new">My watch history</target>
@@ -239,23 +239,23 @@
239 <trans-unit id="5674286808255988565" datatype="html"> 239 <trans-unit id="5674286808255988565" datatype="html">
240 <source>Create</source> 240 <source>Create</source>
241 <target state="new">Create</target> 241 <target state="new">Create</target>
242 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 242
243 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 243
244 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 244
245 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 245
246 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 246
247 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 247
248 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 248
249 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 249
250 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 250
251 </trans-unit> 251 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
252 <trans-unit id="1006562256968398209" datatype="html"> 252 <trans-unit id="1006562256968398209" datatype="html">
253 <source>video</source> 253 <source>video</source>
254 <target state="new">video</target> 254 <target state="new">video</target>
255 255
256 256
257 257
258 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 258 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
259 <trans-unit id="6438815964972582865" datatype="html"> 259 <trans-unit id="6438815964972582865" datatype="html">
260 <source>The following link contains a private token and should not be shared with anyone.</source> 260 <source>The following link contains a private token and should not be shared with anyone.</source>
261 <target state="translated">پیوند زیر دارای یک رمز خصوصی است و نباید با کسی به اشتراک گذاشته شود.</target> 261 <target state="translated">پیوند زیر دارای یک رمز خصوصی است و نباید با کسی به اشتراک گذاشته شود.</target>
@@ -327,12 +327,12 @@
327 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 327 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
328 <target state="translated">با این ویدیو از سهمیه ویدیوی شما بیشتر می شود (اندازه ویدیو: <x id="PH" equiv-text="videoSizeBytes"/>، استفاده شده: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>، سهم : <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 328 <target state="translated">با این ویدیو از سهمیه ویدیوی شما بیشتر می شود (اندازه ویدیو: <x id="PH" equiv-text="videoSizeBytes"/>، استفاده شده: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>، سهم : <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
329 329
330 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit> 330 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
331 <trans-unit id="7873395933409147217" datatype="html"> 331 <trans-unit id="7873395933409147217" datatype="html">
332 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 332 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
333 <target state="translated">سهمیه ویدئویی روزانه شما با این ویدیو (اندازه ویدیو: <x id="PH" equiv-text="videoSizeBytes"/>استفاده شده: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>، سهم: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 333 <target state="translated">سهمیه ویدئویی روزانه شما با این ویدیو (اندازه ویدیو: <x id="PH" equiv-text="videoSizeBytes"/>استفاده شده: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>، سهم: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
334 334
335 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 335 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
336 <trans-unit id="5235042777215655908" datatype="html"> 336 <trans-unit id="5235042777215655908" datatype="html">
337 <source>subtitles</source> 337 <source>subtitles</source>
338 <target state="translated">زیرنویس</target> 338 <target state="translated">زیرنویس</target>
@@ -775,10 +775,10 @@
775 <trans-unit id="2602586221576511475" datatype="html"> 775 <trans-unit id="2602586221576511475" datatype="html">
776 <source>Video quota</source> 776 <source>Video quota</source>
777 <target state="new">Video quota</target> 777 <target state="new">Video quota</target>
778 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 778
779 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 779
780 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 780
781 </trans-unit> 781 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
782 <trans-unit id="1502595455339510144" datatype="html"> 782 <trans-unit id="1502595455339510144" datatype="html">
783 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 783 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
784 <target state="new"> 784 <target state="new">
@@ -863,6 +863,30 @@
863 <target state="new">Federation</target> 863 <target state="new">Federation</target>
864 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 864 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
865 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 865 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
866 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
867 <source>Following</source><target state="new">Following</target>
868 <context-group purpose="location">
869 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
870 <context context-type="linenumber">29</context>
871 </context-group>
872 <context-group purpose="location">
873 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
874 <context context-type="linenumber">31</context>
875 </context-group>
876 <context-group purpose="location">
877 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
878 <context context-type="linenumber">28</context>
879 </context-group>
880 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
881 <source>Followers</source><target state="new">Followers</target>
882 <context-group purpose="location">
883 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
884 <context context-type="linenumber">34</context>
885 </context-group>
886 <context-group purpose="location">
887 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
888 <context context-type="linenumber">37</context>
889 </context-group>
866 </trans-unit> 890 </trans-unit>
867 <trans-unit id="3541687134897970106" datatype="html"> 891 <trans-unit id="3541687134897970106" datatype="html">
868 <source>followers</source> 892 <source>followers</source>
@@ -944,7 +968,7 @@
944 968
945 969
946 970
947 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 971 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
948 <trans-unit id="3616223838716839702" datatype="html"> 972 <trans-unit id="3616223838716839702" datatype="html">
949 <source>Ban this user</source> 973 <source>Ban this user</source>
950 <target state="new">Ban this user</target> 974 <target state="new">Ban this user</target>
@@ -1017,19 +1041,13 @@
1017 <trans-unit id="7252854992688790751" datatype="html"> 1041 <trans-unit id="7252854992688790751" datatype="html">
1018 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1042 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1019 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1043 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1020 <context-group purpose="location"> 1044
1021 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1045 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1022 <context context-type="linenumber">60,62</context>
1023 </context-group>
1024 </trans-unit>
1025 <trans-unit id="7215649348148521605" datatype="html"> 1046 <trans-unit id="7215649348148521605" datatype="html">
1026 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1047 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1027 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1048 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1028 <context-group purpose="location"> 1049
1029 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1050 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1030 <context context-type="linenumber">65,67</context>
1031 </context-group>
1032 </trans-unit>
1033 <trans-unit id="2392488717875840729"> 1051 <trans-unit id="2392488717875840729">
1034 <source>User</source> 1052 <source>User</source>
1035 <target>کاربر</target> 1053 <target>کاربر</target>
@@ -1040,69 +1058,69 @@
1040 <source>Username or email address</source> 1058 <source>Username or email address</source>
1041 <target>نام کاربری یا آدرس رایانامه</target> 1059 <target>نام کاربری یا آدرس رایانامه</target>
1042 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1060 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1061 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1062 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1063 <context-group purpose="location">
1064 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1065 <context context-type="linenumber">33,34</context>
1066 </context-group>
1043 </trans-unit> 1067 </trans-unit>
1044 <trans-unit id="1431416938026210429"> 1068 <trans-unit id="1431416938026210429">
1045 <source>Password</source> 1069 <source>Password</source>
1046 <target>گذرواژه</target> 1070 <target>گذرواژه</target>
1047 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1071
1048 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1072
1049 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1073
1050 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1074
1051 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1075
1052 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1076
1053 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1077
1054 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1078
1055 </trans-unit> 1079 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1056 <trans-unit id="8715156686857791956" datatype="html"> 1080 <trans-unit id="8715156686857791956" datatype="html">
1057 <source>Click here to reset your password</source> 1081 <source>Click here to reset your password</source>
1058 <target state="new">Click here to reset your password</target> 1082 <target state="new">Click here to reset your password</target>
1059 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1083
1060 </trans-unit> 1084 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1061 <trans-unit id="892063502898494584" datatype="html"> 1085 <trans-unit id="892063502898494584" datatype="html">
1062 <source>I forgot my password</source> 1086 <source>I forgot my password</source>
1063 <target state="new">I forgot my password</target> 1087 <target state="new">I forgot my password</target>
1064 <context-group purpose="location"> 1088
1065 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1089 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1066 <context context-type="linenumber">47</context>
1067 </context-group>
1068 </trans-unit>
1069 <trans-unit id="2101170466365500913" datatype="html"> 1090 <trans-unit id="2101170466365500913" datatype="html">
1070 <source>Logging into an account lets you publish content</source> 1091 <source>Logging into an account lets you publish content</source>
1071 <target state="new"> Logging into an account lets you publish content </target> 1092 <target state="new"> Logging into an account lets you publish content </target>
1072 <context-group purpose="location"> 1093
1073 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1094 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1074 <context context-type="linenumber">56,57</context>
1075 </context-group>
1076 </trans-unit>
1077 <trans-unit id="2454050363478003966"> 1095 <trans-unit id="2454050363478003966">
1078 <source>Login</source> 1096 <source>Login</source>
1079 <target>ورود</target> 1097 <target>ورود</target>
1080 1098
1081 1099
1082 1100
1083 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1101 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1084 <trans-unit id="3183213940445113677" datatype="html"> 1102 <trans-unit id="3183213940445113677" datatype="html">
1085 <source>Or sign in with</source> 1103 <source>Or sign in with</source>
1086 <target state="new">Or sign in with</target> 1104 <target state="new">Or sign in with</target>
1087 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1105
1088 </trans-unit> 1106 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1089 <trans-unit id="3238209155172574367"> 1107 <trans-unit id="3238209155172574367">
1090 <source>Forgot your password</source> 1108 <source>Forgot your password</source>
1091 <target>گذرواژه‌تان را فراموش کرده‌اید</target> 1109 <target>گذرواژه‌تان را فراموش کرده‌اید</target>
1092 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1110
1093 </trans-unit> 1111 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1094 <trans-unit id="87327320394367488" datatype="html"> 1112 <trans-unit id="87327320394367488" datatype="html">
1095 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1113 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1096 <target state="new"> 1114 <target state="new">
1097 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1115 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
1098 </target> 1116 </target>
1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1117
1100 </trans-unit> 1118 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1101 <trans-unit id="3188014010833256853" datatype="html"> 1119 <trans-unit id="3188014010833256853" datatype="html">
1102 <source>Enter your email address and we will send you a link to reset your password.</source> 1120 <source>Enter your email address and we will send you a link to reset your password.</source>
1103 <target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1121 <target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1122
1105 </trans-unit> 1123 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1106 <trans-unit id="1190256911880544559" datatype="html"> 1124 <trans-unit id="1190256911880544559" datatype="html">
1107 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1125 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1108The link will expire within 1 hour.</source> 1126The link will expire within 1 hour.</source>
@@ -1113,26 +1131,26 @@ The link will expire within 1 hour.</target>
1113 <trans-unit id="4768749765465246664"> 1131 <trans-unit id="4768749765465246664">
1114 <source>Email</source> 1132 <source>Email</source>
1115 <target>رایانامه</target> 1133 <target>رایانامه</target>
1116 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1134
1117 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1135
1118 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1136
1119 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1137
1120 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1138
1121 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1139
1122 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1140
1123 </trans-unit> 1141 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1124 <trans-unit id="3967269098753656610"> 1142 <trans-unit id="3967269098753656610">
1125 <source>Email address</source> 1143 <source>Email address</source>
1126 <target>آدرس رایانامه</target> 1144 <target>آدرس رایانامه</target>
1127 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1145
1128 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1146
1129 </trans-unit> 1147 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1130 <trans-unit id="7808756054397155068" datatype="html"> 1148 <trans-unit id="7808756054397155068" datatype="html">
1131 <source>Reset</source> 1149 <source>Reset</source>
1132 <target state="new">Reset</target> 1150 <target state="new">Reset</target>
1133 <note priority="1" from="description">Password reset button</note> 1151 <note priority="1" from="description">Password reset button</note>
1134 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1152
1135 </trans-unit> 1153 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1136 <trans-unit id="4319634264526091601" datatype="html"> 1154 <trans-unit id="4319634264526091601" datatype="html">
1137 <source>on this instance</source> 1155 <source>on this instance</source>
1138 <target state="new">on this instance</target> 1156 <target state="new">on this instance</target>
@@ -1508,7 +1526,7 @@ The link will expire within 1 hour.</target>
1508 <target>ساخت حساب</target> 1526 <target>ساخت حساب</target>
1509 1527
1510 1528
1511 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1529 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1512 1530
1513 <trans-unit id="3058024914967508975" datatype="html"> 1531 <trans-unit id="3058024914967508975" datatype="html">
1514 <source>My videos</source> 1532 <source>My videos</source>
@@ -1576,7 +1594,7 @@ The link will expire within 1 hour.</target>
1576 1594
1577 1595
1578 1596
1579 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 1597 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1580 <trans-unit id="667372110624203230" datatype="html"> 1598 <trans-unit id="667372110624203230" datatype="html">
1581 <source>Import jobs concurrency</source> 1599 <source>Import jobs concurrency</source>
1582 <target state="new">Import jobs concurrency</target> 1600 <target state="new">Import jobs concurrency</target>
@@ -1654,8 +1672,8 @@ The link will expire within 1 hour.</target>
1654 <trans-unit id="4424964105331349857" datatype="html"> 1672 <trans-unit id="4424964105331349857" datatype="html">
1655 <source>I'm a teapot</source> 1673 <source>I'm a teapot</source>
1656 <target state="new">I'm a teapot</target> 1674 <target state="new">I'm a teapot</target>
1657 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1675
1658 </trans-unit> 1676 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1659 <trans-unit id="1597262876035959248" datatype="html"> 1677 <trans-unit id="1597262876035959248" datatype="html">
1660 <source>That's an error.</source> 1678 <source>That's an error.</source>
1661 <target state="new">That's an error.</target> 1679 <target state="new">That's an error.</target>
@@ -1748,8 +1766,8 @@ The link will expire within 1 hour.</target>
1748 <trans-unit id="2971365540217107489" datatype="html"> 1766 <trans-unit id="2971365540217107489" datatype="html">
1749 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1767 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1750 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1768 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1751 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1769
1752 </trans-unit> 1770 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1753 1771
1754 <trans-unit id="5131854469652959713" datatype="html"> 1772 <trans-unit id="5131854469652959713" datatype="html">
1755 <source>GLOBAL SEARCH</source> 1773 <source>GLOBAL SEARCH</source>
@@ -2523,7 +2541,7 @@ The link will expire within 1 hour.</target>
2523 <source>Upload on hold</source> 2541 <source>Upload on hold</source>
2524 <target state="new">Upload on hold</target> 2542 <target state="new">Upload on hold</target>
2525 2543
2526 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2544 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2527 <trans-unit id="285180972645018518" datatype="html"> 2545 <trans-unit id="285180972645018518" datatype="html">
2528 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2546 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2529 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2547 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3239,11 +3257,7 @@ The link will expire within 1 hour.</target>
3239 <target state="new">ID</target> 3257 <target state="new">ID</target>
3240 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3258 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3241 </trans-unit> 3259 </trans-unit>
3242 <trans-unit id="2265605798180116441" datatype="html"> 3260
3243 <source>Follower handle</source>
3244 <target state="new">Follower handle</target>
3245 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3246 </trans-unit>
3247 <trans-unit id="5911214550882917183"> 3261 <trans-unit id="5911214550882917183">
3248 <source>State</source> 3262 <source>State</source>
3249 <target state="new">State</target> 3263 <target state="new">State</target>
@@ -3318,11 +3332,7 @@ The link will expire within 1 hour.</target>
3318 </target> 3332 </target>
3319 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3333 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3320 </trans-unit> 3334 </trans-unit>
3321 <trans-unit id="6641024648411549335"> 3335
3322 <source>Host</source>
3323 <target>میزبان</target>
3324 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3325 </trans-unit>
3326 <trans-unit id="6571718060636962350" datatype="html"> 3336 <trans-unit id="6571718060636962350" datatype="html">
3327 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3337 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3328 <target state="new">Redundancy allowed 3338 <target state="new">Redundancy allowed
@@ -3334,9 +3344,9 @@ The link will expire within 1 hour.</target>
3334 <trans-unit id="9160510009013134726" datatype="html"> 3344 <trans-unit id="9160510009013134726" datatype="html">
3335 <source>Unfollow</source> 3345 <source>Unfollow</source>
3336 <target state="new">Unfollow</target> 3346 <target state="new">Unfollow</target>
3337 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3347
3338 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3348
3339 </trans-unit> 3349 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3340 <trans-unit id="8246779176913476983" datatype="html"> 3350 <trans-unit id="8246779176913476983" datatype="html">
3341 <source>Open instance in a new tab</source> 3351 <source>Open instance in a new tab</source>
3342 <target state="new">Open instance in a new tab</target> 3352 <target state="new">Open instance in a new tab</target>
@@ -3347,13 +3357,13 @@ The link will expire within 1 hour.</target>
3347 <trans-unit id="9132918641931433659" datatype="html"> 3357 <trans-unit id="9132918641931433659" datatype="html">
3348 <source>No host found matching current filters.</source> 3358 <source>No host found matching current filters.</source>
3349 <target state="new">No host found matching current filters.</target> 3359 <target state="new">No host found matching current filters.</target>
3350 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3360
3351 </trans-unit> 3361 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3352 <trans-unit id="7274241885665071790" datatype="html"> 3362 <trans-unit id="7274241885665071790" datatype="html">
3353 <source>Your instance is not following anyone.</source> 3363 <source>Your instance is not following anyone.</source>
3354 <target state="new">Your instance is not following anyone.</target> 3364 <target state="new">Your instance is not following anyone.</target>
3355 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3365
3356 </trans-unit> 3366 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3357 <trans-unit id="4774348799569692380" datatype="html"> 3367 <trans-unit id="4774348799569692380" datatype="html">
3358 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3368 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3359 <target state="new">Showing 3369 <target state="new">Showing
@@ -3363,16 +3373,8 @@ The link will expire within 1 hour.</target>
3363 </target> 3373 </target>
3364 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3374 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3365 </trans-unit> 3375 </trans-unit>
3366 <trans-unit id="6275803119759621687" datatype="html"> 3376
3367 <source>Follow domains</source> 3377
3368 <target state="new">Follow domains</target>
3369 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3370 </trans-unit>
3371 <trans-unit id="1268699198448750610" datatype="html">
3372 <source>Follow instances</source>
3373 <target state="new">Follow instances</target>
3374 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3375 </trans-unit>
3376 <trans-unit id="9216117865911519658" datatype="html"> 3378 <trans-unit id="9216117865911519658" datatype="html">
3377 <source>Action</source> 3379 <source>Action</source>
3378 <target state="new">Action</target> 3380 <target state="new">Action</target>
@@ -3422,11 +3424,11 @@ The link will expire within 1 hour.</target>
3422 <trans-unit id="5248717555542428023"> 3424 <trans-unit id="5248717555542428023">
3423 <source>Username</source> 3425 <source>Username</source>
3424 <target state="new">Username</target> 3426 <target state="new">Username</target>
3425 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3427
3426 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3428
3427 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3429
3428 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3430
3429 </trans-unit> 3431 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3430 <trans-unit id="5428411040014095392" datatype="html"> 3432 <trans-unit id="5428411040014095392" datatype="html">
3431 <source>e.g. jane_doe</source> 3433 <source>e.g. jane_doe</source>
3432 <target state="new">e.g. jane_doe</target> 3434 <target state="new">e.g. jane_doe</target>
@@ -3456,9 +3458,9 @@ The link will expire within 1 hour.</target>
3456 <trans-unit id="4145496584631696119"> 3458 <trans-unit id="4145496584631696119">
3457 <source>Role</source> 3459 <source>Role</source>
3458 <target>نقش</target> 3460 <target>نقش</target>
3459 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3461
3460 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3462
3461 </trans-unit> 3463 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3462 <trans-unit id="7046347992315328430" datatype="html"> 3464 <trans-unit id="7046347992315328430" datatype="html">
3463 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3465 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3464 <target state="new"> 3466 <target state="new">
@@ -3483,15 +3485,9 @@ The link will expire within 1 hour.</target>
3483 <trans-unit id="2622255144026150901" datatype="html"> 3485 <trans-unit id="2622255144026150901" datatype="html">
3484 <source>Auth plugin</source> 3486 <source>Auth plugin</source>
3485 <target state="new">Auth plugin</target> 3487 <target state="new">Auth plugin</target>
3486 <context-group purpose="location"> 3488
3487 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3489
3488 <context context-type="linenumber">188</context> 3490 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3489 </context-group>
3490 <context-group purpose="location">
3491 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3492 <context context-type="linenumber">188</context>
3493 </context-group>
3494 </trans-unit>
3495 <trans-unit id="588099657508661970" datatype="html"> 3491 <trans-unit id="588099657508661970" datatype="html">
3496 <source>None (local authentication)</source> 3492 <source>None (local authentication)</source>
3497 <target state="new">None (local authentication)</target> 3493 <target state="new">None (local authentication)</target>
@@ -3756,6 +3752,12 @@ The link will expire within 1 hour.</target>
3756 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3752 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3757 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3753 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3758 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3754 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3755 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3756 <source>Follower</source><target state="new">Follower</target>
3757 <context-group purpose="location">
3758 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3759 <context context-type="linenumber">24</context>
3760 </context-group>
3759 </trans-unit> 3761 </trans-unit>
3760 <trans-unit id="4691552465058437520" datatype="html"> 3762 <trans-unit id="4691552465058437520" datatype="html">
3761 <source>Commented video</source> 3763 <source>Commented video</source>
@@ -4096,8 +4098,8 @@ The link will expire within 1 hour.</target>
4096 <target state="new"> 4098 <target state="new">
4097 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 4099 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
4098 </target> 4100 </target>
4099 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 4101
4100 </trans-unit> 4102 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
4101 <trans-unit id="4058814854824495833" datatype="html"> 4103 <trans-unit id="4058814854824495833" datatype="html">
4102 <source>Mute domains</source> 4104 <source>Mute domains</source>
4103 <target state="new">Mute domains</target> 4105 <target state="new">Mute domains</target>
@@ -6101,11 +6103,8 @@ color: red;
6101 <trans-unit id="5512878593724620692" datatype="html"> 6103 <trans-unit id="5512878593724620692" datatype="html">
6102 <source>CHANNELS</source> 6104 <source>CHANNELS</source>
6103 <target state="new">CHANNELS</target> 6105 <target state="new">CHANNELS</target>
6104 <context-group purpose="location"> 6106
6105 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 6107 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
6106 <context context-type="linenumber">82</context>
6107 </context-group>
6108 </trans-unit>
6109 <trans-unit id="3666829335406793239" datatype="html"> 6108 <trans-unit id="3666829335406793239" datatype="html">
6110 <source>This account does not have channels.</source> 6109 <source>This account does not have channels.</source>
6111 <target state="new">This account does not have channels.</target> 6110 <target state="new">This account does not have channels.</target>
@@ -6148,6 +6147,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6148It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 6147It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
6149channel with the same name (<x id="PH_2"/>)!</target> 6148channel with the same name (<x id="PH_2"/>)!</target>
6150 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 6149 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
6150 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
6151 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
6152 <context-group purpose="location">
6153 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
6154 <context context-type="linenumber">48</context>
6155 </context-group>
6151 </trans-unit> 6156 </trans-unit>
6152 <trans-unit id="5387007581996837469" datatype="html"> 6157 <trans-unit id="5387007581996837469" datatype="html">
6153 <source>My Channels</source> 6158 <source>My Channels</source>
@@ -6746,12 +6751,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6746 <source>Your message has been sent.</source> 6751 <source>Your message has been sent.</source>
6747 <target state="new">Your message has been sent.</target> 6752 <target state="new">Your message has been sent.</target>
6748 6753
6749 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6754 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6750 <trans-unit id="2072135752262464360" datatype="html"> 6755 <trans-unit id="2072135752262464360" datatype="html">
6751 <source>You already sent this form recently</source> 6756 <source>You already sent this form recently</source>
6752 <target state="new">You already sent this form recently</target> 6757 <target state="new">You already sent this form recently</target>
6753 6758
6754 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6759 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6755 <trans-unit id="819067926858619041" datatype="html"> 6760 <trans-unit id="819067926858619041" datatype="html">
6756 <source>Account videos</source> 6761 <source>Account videos</source>
6757 <target state="new">Account videos</target> 6762 <target state="new">Account videos</target>
@@ -6797,13 +6802,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6797 <target state="new"> 6802 <target state="new">
6798 <x id="PH"/> direct account followers 6803 <x id="PH"/> direct account followers
6799 </target> 6804 </target>
6800 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6805
6801 </trans-unit> 6806 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6802 <trans-unit id="6250999352462648289" datatype="html"> 6807 <trans-unit id="6250999352462648289" datatype="html">
6803 <source>Report this account</source> 6808 <source>Report this account</source>
6804 <target state="new">Report this account</target> 6809 <target state="new">Report this account</target>
6805 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6810
6806 </trans-unit> 6811 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6807 <trans-unit id="1504521795586863905" datatype="html"> 6812 <trans-unit id="1504521795586863905" datatype="html">
6808 <source>VIDEOS</source> 6813 <source>VIDEOS</source>
6809 <target state="new">VIDEOS</target> 6814 <target state="new">VIDEOS</target>
@@ -6813,31 +6818,21 @@ channel with the same name (<x id="PH_2"/>)!</target>
6813 <trans-unit id="25349740244798533" datatype="html"> 6818 <trans-unit id="25349740244798533" datatype="html">
6814 <source>Username copied</source> 6819 <source>Username copied</source>
6815 <target state="new">Username copied</target> 6820 <target state="new">Username copied</target>
6816 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6821
6817 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6822
6818 </trans-unit> 6823 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6819 <trans-unit id="9221735175659318025" datatype="html"> 6824 <trans-unit id="9221735175659318025" datatype="html">
6820 <source>1 subscriber</source> 6825 <source>1 subscriber</source>
6821 <target state="new">1 subscriber</target> 6826 <target state="new">1 subscriber</target>
6822 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6827
6823 </trans-unit> 6828 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6824 <trans-unit id="4097331874769079975" datatype="html"> 6829 <trans-unit id="4097331874769079975" datatype="html">
6825 <source><x id="PH"/> subscribers</source> 6830 <source><x id="PH"/> subscribers</source>
6826 <target state="new"><x id="PH"/> subscribers</target> 6831 <target state="new"><x id="PH"/> subscribers</target>
6827 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6832
6828 </trans-unit> 6833 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6829 <trans-unit id="4682675125751819107" datatype="html"> 6834
6830 <source>Instances you follow</source> 6835
6831 <target state="new">Instances you follow</target>
6832 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6833 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6834 </trans-unit>
6835 <trans-unit id="8899833753704589712" datatype="html">
6836 <source>Instances following you</source>
6837 <target state="new">Instances following you</target>
6838 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6839 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6840 </trans-unit>
6841 <trans-unit id="1035838766454786107" datatype="html"> 6836 <trans-unit id="1035838766454786107" datatype="html">
6842 <source>Audio-only</source> 6837 <source>Audio-only</source>
6843 <target state="new">Audio-only</target> 6838 <target state="new">Audio-only</target>
@@ -6887,6 +6882,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6887 <source>Auto (via ffmpeg)</source> 6882 <source>Auto (via ffmpeg)</source>
6888 <target state="new">Auto (via ffmpeg)</target> 6883 <target state="new">Auto (via ffmpeg)</target>
6889 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6884 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6885 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6886 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6887 <context-group purpose="location">
6888 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6889 <context context-type="linenumber">3</context>
6890 </context-group>
6890 </trans-unit> 6891 </trans-unit>
6891 <trans-unit id="931255636742351800" datatype="html"> 6892 <trans-unit id="931255636742351800" datatype="html">
6892 <source>No limit</source> 6893 <source>No limit</source>
@@ -7037,18 +7038,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
7037 <trans-unit id="2127446333083057097" datatype="html"> 7038 <trans-unit id="2127446333083057097" datatype="html">
7038 <source>Domain is required.</source> 7039 <source>Domain is required.</source>
7039 <target state="new">Domain is required.</target> 7040 <target state="new">Domain is required.</target>
7040 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 7041
7041 </trans-unit> 7042 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
7042 <trans-unit id="6780793142903080663" datatype="html"> 7043 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
7043 <source>Domains entered are invalid.</source> 7044 <context-group purpose="location">
7044 <target state="new">Domains entered are invalid.</target> 7045 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7045 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 7046 <context context-type="linenumber">93</context>
7046 </trans-unit> 7047 </context-group>
7047 <trans-unit id="5886492514458202177" datatype="html"> 7048 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
7048 <source>Domains entered contain duplicates.</source> 7049 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
7049 <target state="new">Domains entered contain duplicates.</target> 7050 <context-group purpose="location">
7050 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 7051 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7052 <context context-type="linenumber">94</context>
7053 </context-group>
7054 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
7055 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
7056 <context-group purpose="location">
7057 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7058 <context context-type="linenumber">102</context>
7059 </context-group>
7060 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
7061 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
7062 <context-group purpose="location">
7063 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7064 <context context-type="linenumber">103</context>
7065 </context-group>
7051 </trans-unit> 7066 </trans-unit>
7067
7068
7052 <trans-unit id="240806681889331244" datatype="html"> 7069 <trans-unit id="240806681889331244" datatype="html">
7053 <source>Unlimited</source> 7070 <source>Unlimited</source>
7054 <target state="new">Unlimited</target> 7071 <target state="new">Unlimited</target>
@@ -7208,26 +7225,52 @@ channel with the same name (<x id="PH_2"/>)!</target>
7208 <x id="PH"/> removed from instance followers 7225 <x id="PH"/> removed from instance followers
7209 </target> 7226 </target>
7210 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 7227 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
7228 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
7229 <source>Follow</source><target state="new">Follow</target>
7230 <context-group purpose="location">
7231 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7232 <context context-type="linenumber">3</context>
7233 </context-group>
7234 <context-group purpose="location">
7235 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7236 <context context-type="linenumber">37</context>
7237 </context-group>
7238 <context-group purpose="location">
7239 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7240 <context context-type="linenumber">18</context>
7241 </context-group>
7242 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
7243 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
7244 <context-group purpose="location">
7245 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7246 <context context-type="linenumber">11</context>
7247 </context-group>
7211 </trans-unit> 7248 </trans-unit>
7212 <trans-unit id="2740793005745065895" datatype="html"> 7249 <trans-unit id="2740793005745065895" datatype="html">
7213 <source><x id="PH"/> is not valid </source> 7250 <source><x id="PH"/> is not valid </source>
7214 <target state="new"> 7251 <target state="new">
7215 <x id="PH"/> is not valid 7252 <x id="PH"/> is not valid
7216 </target> 7253 </target>
7217 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 7254
7218 </trans-unit> 7255 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
7219 <trans-unit id="2355066641781598196" datatype="html"> 7256 <trans-unit id="2355066641781598196" datatype="html">
7220 <source>Follow request(s) sent!</source> 7257 <source>Follow request(s) sent!</source>
7221 <target state="new">Follow request(s) sent!</target> 7258 <target state="new">Follow request(s) sent!</target>
7222 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 7259
7260 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
7261 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
7262 <context-group purpose="location">
7263 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7264 <context context-type="linenumber">3</context>
7265 </context-group>
7223 </trans-unit> 7266 </trans-unit>
7224 <trans-unit id="4245720728052819482" datatype="html"> 7267 <trans-unit id="4245720728052819482" datatype="html">
7225 <source>Do you really want to unfollow <x id="PH"/>?</source> 7268 <source>Do you really want to unfollow <x id="PH"/>?</source>
7226 <target state="new">Do you really want to unfollow 7269 <target state="new">Do you really want to unfollow
7227 <x id="PH"/>? 7270 <x id="PH"/>?
7228 </target> 7271 </target>
7229 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 7272
7230 </trans-unit> 7273 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
7231 <trans-unit id="9160510009013134726" datatype="html"> 7274 <trans-unit id="9160510009013134726" datatype="html">
7232 <source>Unfollow</source> 7275 <source>Unfollow</source>
7233 <target state="new">Unfollow</target> 7276 <target state="new">Unfollow</target>
@@ -7238,8 +7281,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7238 <target state="new">You are not following 7281 <target state="new">You are not following
7239 <x id="PH"/> anymore. 7282 <x id="PH"/> anymore.
7240 </target> 7283 </target>
7241 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 7284
7242 </trans-unit> 7285 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
7243 <trans-unit id="2593763089859685916" datatype="html"> 7286 <trans-unit id="2593763089859685916" datatype="html">
7244 <source>enabled</source> 7287 <source>enabled</source>
7245 <target state="new">enabled</target> 7288 <target state="new">enabled</target>
@@ -7730,9 +7773,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7730 <trans-unit id="1519954996184640001" datatype="html"> 7773 <trans-unit id="1519954996184640001" datatype="html">
7731 <source>Error</source> 7774 <source>Error</source>
7732 <target state="new">Error</target> 7775 <target state="new">Error</target>
7733 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7776
7734 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7777
7735 </trans-unit> 7778 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7736 <trans-unit id="5076187961693950167" datatype="html"> 7779 <trans-unit id="5076187961693950167" datatype="html">
7737 <source>Standard logs</source> 7780 <source>Standard logs</source>
7738 <target state="new">Standard logs</target> 7781 <target state="new">Standard logs</target>
@@ -7777,16 +7820,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7777 <target state="new">Update user password</target> 7820 <target state="new">Update user password</target>
7778 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7821 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7779 </trans-unit> 7822 </trans-unit>
7780 <trans-unit id="177544274549739411" datatype="html"> 7823
7781 <source>Following list</source> 7824
7782 <target state="new">Following list</target>
7783 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7784 </trans-unit>
7785 <trans-unit id="8092429110007204784" datatype="html">
7786 <source>Followers list</source>
7787 <target state="new">Followers list</target>
7788 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7789 </trans-unit>
7790 <trans-unit id="780323526182667308" datatype="html"> 7825 <trans-unit id="780323526182667308" datatype="html">
7791 <source>User <x id="PH"/> updated.</source> 7826 <source>User <x id="PH"/> updated.</source>
7792 <target state="new">User 7827 <target state="new">User
@@ -7826,16 +7861,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7826 <target state="new">Federation</target> 7861 <target state="new">Federation</target>
7827 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7862 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7828 </trans-unit> 7863 </trans-unit>
7829 <trans-unit id="4682675125751819107" datatype="html"> 7864
7830 <source>Instances you follow</source> 7865
7831 <target state="new">Instances you follow</target>
7832 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7833 </trans-unit>
7834 <trans-unit id="8899833753704589712" datatype="html">
7835 <source>Instances following you</source>
7836 <target state="new">Instances following you</target>
7837 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7838 </trans-unit>
7839 <trans-unit id="3767259920053407667" datatype="html"> 7866 <trans-unit id="3767259920053407667" datatype="html">
7840 <source>Videos will be deleted, comments will be tombstoned.</source> 7867 <source>Videos will be deleted, comments will be tombstoned.</source>
7841 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7868 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7866,7 +7893,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7866 <target state="new">Set Email as Verified</target> 7893 <target state="new">Set Email as Verified</target>
7867 7894
7868 7895
7869 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7896 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7897 <source>Created</source><target state="new">Created</target>
7898 <context-group purpose="location">
7899 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7900 <context context-type="linenumber">115</context>
7901 </context-group>
7902 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7903 <source>Daily quota</source><target state="new">Daily quota</target>
7904 <context-group purpose="location">
7905 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7906 <context context-type="linenumber">120</context>
7907 </context-group>
7908 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7909 <source>Last login</source><target state="new">Last login</target>
7910 <context-group purpose="location">
7911 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7912 <context context-type="linenumber">122</context>
7913 </context-group>
7914 </trans-unit>
7870 <trans-unit id="3403978719736970622" datatype="html"> 7915 <trans-unit id="3403978719736970622" datatype="html">
7871 <source>You cannot ban root.</source> 7916 <source>You cannot ban root.</source>
7872 <target state="new">You cannot ban root.</target> 7917 <target state="new">You cannot ban root.</target>
@@ -8179,13 +8224,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
8179 <target state="new">Video channel 8224 <target state="new">Video channel
8180 <x id="PH"/> created. 8225 <x id="PH"/> created.
8181 </target> 8226 </target>
8182 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 8227
8183 </trans-unit> 8228 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
8184 <trans-unit id="8723777130353305761" datatype="html"> 8229 <trans-unit id="8723777130353305761" datatype="html">
8185 <source>This name already exists on this instance.</source> 8230 <source>This name already exists on this instance.</source>
8186 <target state="new">This name already exists on this instance.</target> 8231 <target state="new">This name already exists on this instance.</target>
8187 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 8232
8188 </trans-unit> 8233 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
8189 <trans-unit id="7589345916094713536" datatype="html"> 8234 <trans-unit id="7589345916094713536" datatype="html">
8190 <source>Video channel <x id="PH"/> updated.</source> 8235 <source>Video channel <x id="PH"/> updated.</source>
8191 <target state="new">Video channel 8236 <target state="new">Video channel
@@ -8208,13 +8253,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8208 <target state="new">Banner deleted.</target> 8253 <target state="new">Banner deleted.</target>
8209 8254
8210 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 8255 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
8211 <trans-unit id="2575302837003821736" datatype="html"> 8256
8212 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
8213 <target state="new">Please type the display name of the video channel (
8214 <x id="PH"/>) to confirm
8215 </target>
8216 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
8217 </trans-unit>
8218 <trans-unit id="624066830180032195" datatype="html"> 8257 <trans-unit id="624066830180032195" datatype="html">
8219 <source>Video channel <x id="PH"/> deleted.</source> 8258 <source>Video channel <x id="PH"/> deleted.</source>
8220 <target state="new">Video channel 8259 <target state="new">Video channel
@@ -8380,6 +8419,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8380 <source>Ownership change request sent.</source> 8419 <source>Ownership change request sent.</source>
8381 <target state="new">Ownership change request sent.</target> 8420 <target state="new">Ownership change request sent.</target>
8382 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8421 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8422 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8423 <source>Sort by</source><target state="new">Sort by</target>
8424 <context-group purpose="location">
8425 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8426 <context context-type="linenumber">26</context>
8427 </context-group>
8383 </trans-unit> 8428 </trans-unit>
8384 <trans-unit id="3245220240937722814" datatype="html"> 8429 <trans-unit id="3245220240937722814" datatype="html">
8385 <source>My channels</source> 8430 <source>My channels</source>
@@ -8482,7 +8527,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8482 <target state="new">Subscribe to the account</target> 8527 <target state="new">Subscribe to the account</target>
8483 8528
8484 8529
8485 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8530 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8486 <trans-unit id="3131904093925601441" datatype="html"> 8531 <trans-unit id="3131904093925601441" datatype="html">
8487 <source>PLAYLISTS</source> 8532 <source>PLAYLISTS</source>
8488 <target state="new">PLAYLISTS</target> 8533 <target state="new">PLAYLISTS</target>
@@ -8529,35 +8574,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8529 <trans-unit id="3779524668013120370" datatype="html"> 8574 <trans-unit id="3779524668013120370" datatype="html">
8530 <source>Go to my subscriptions</source> 8575 <source>Go to my subscriptions</source>
8531 <target state="new">Go to my subscriptions</target> 8576 <target state="new">Go to my subscriptions</target>
8532 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8577
8533 </trans-unit> 8578 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8534 <trans-unit id="1136469849928650779" datatype="html"> 8579 <trans-unit id="1136469849928650779" datatype="html">
8535 <source>Go to my videos</source> 8580 <source>Go to my videos</source>
8536 <target state="new">Go to my videos</target> 8581 <target state="new">Go to my videos</target>
8537 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8582
8538 </trans-unit> 8583 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8539 <trans-unit id="7836683738999600376" datatype="html"> 8584 <trans-unit id="7836683738999600376" datatype="html">
8540 <source>Go to my imports</source> 8585 <source>Go to my imports</source>
8541 <target state="new">Go to my imports</target> 8586 <target state="new">Go to my imports</target>
8542 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8587
8543 </trans-unit> 8588 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8544 <trans-unit id="7511292153332773503" datatype="html"> 8589 <trans-unit id="7511292153332773503" datatype="html">
8545 <source>Go to my channels</source> 8590 <source>Go to my channels</source>
8546 <target state="new">Go to my channels</target> 8591 <target state="new">Go to my channels</target>
8547 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8592
8548 </trans-unit> 8593 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8549 <trans-unit id="2013324644839511073" datatype="html"> 8594 <trans-unit id="2013324644839511073" datatype="html">
8550 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8595 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8551Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8596Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8552 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8597 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8553Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8598Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8554 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8599
8555 </trans-unit> 8600 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8556 <trans-unit id="375263728166936544" datatype="html"> 8601 <trans-unit id="375263728166936544" datatype="html">
8557 <source>You need to reconnect.</source> 8602 <source>You need to reconnect.</source>
8558 <target state="new">You need to reconnect.</target> 8603 <target state="new">You need to reconnect.</target>
8559 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8604
8560 </trans-unit> 8605 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8561 <trans-unit id="2206638022166154361" datatype="html"> 8606 <trans-unit id="2206638022166154361" datatype="html">
8562 <source>Keyboard Shortcuts:</source> 8607 <source>Keyboard Shortcuts:</source>
8563 <target state="new">Keyboard Shortcuts:</target> 8608 <target state="new">Keyboard Shortcuts:</target>
@@ -8568,6 +8613,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8568 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8613 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8569 <context context-type="linenumber">98</context> 8614 <context context-type="linenumber">98</context>
8570 </context-group> 8615 </context-group>
8616 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8617 <source>In my library</source><target state="new">In my library</target>
8618 <context-group purpose="location">
8619 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8620 <context context-type="linenumber">104</context>
8621 </context-group>
8571 </trans-unit> 8622 </trans-unit>
8572 <trans-unit id="232050922346936574" datatype="html"> 8623 <trans-unit id="232050922346936574" datatype="html">
8573 <source>Trending</source> 8624 <source>Trending</source>
@@ -8594,39 +8645,39 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8594 <trans-unit id="1266887509445371246" datatype="html"> 8645 <trans-unit id="1266887509445371246" datatype="html">
8595 <source>Incorrect username or password.</source> 8646 <source>Incorrect username or password.</source>
8596 <target state="new">Incorrect username or password.</target> 8647 <target state="new">Incorrect username or password.</target>
8597 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8648
8598 </trans-unit> 8649 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8599 <trans-unit id="6974874606619467663" datatype="html"> 8650 <trans-unit id="6974874606619467663" datatype="html">
8600 <source>Your account is blocked.</source> 8651 <source>Your account is blocked.</source>
8601 <target state="new">Your account is blocked.</target> 8652 <target state="new">Your account is blocked.</target>
8602 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8653
8603 </trans-unit> 8654 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8604 <trans-unit id="7939914198003891823" datatype="html"> 8655 <trans-unit id="7939914198003891823" datatype="html">
8605 <source>any language</source> 8656 <source>any language</source>
8606 <target state="new">any language</target> 8657 <target state="new">any language</target>
8607 8658
8608 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8659 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8609 8660
8610 <trans-unit id="5633144232269377096" datatype="html"> 8661 <trans-unit id="5633144232269377096" datatype="html">
8611 <source>hide</source> 8662 <source>hide</source>
8612 <target state="new">hide</target> 8663 <target state="new">hide</target>
8613 8664
8614 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8665 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8615 <trans-unit id="8603861867909474404" datatype="html"> 8666 <trans-unit id="8603861867909474404" datatype="html">
8616 <source>blur</source> 8667 <source>blur</source>
8617 <target state="new">blur</target> 8668 <target state="new">blur</target>
8618 8669
8619 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8670 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8620 <trans-unit id="4534458451100881847" datatype="html"> 8671 <trans-unit id="4534458451100881847" datatype="html">
8621 <source>display</source> 8672 <source>display</source>
8622 <target state="new">display</target> 8673 <target state="new">display</target>
8623 8674
8624 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8675 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8625 <trans-unit id="4467323362722952678" datatype="html"> 8676 <trans-unit id="4467323362722952678" datatype="html">
8626 <source>Unknown</source> 8677 <source>Unknown</source>
8627 <target state="new">Unknown</target> 8678 <target state="new">Unknown</target>
8628 8679
8629 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8680 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8630 <trans-unit id="8781423666414310853" datatype="html"> 8681 <trans-unit id="8781423666414310853" datatype="html">
8631 <source>Your password has been successfully reset!</source> 8682 <source>Your password has been successfully reset!</source>
8632 <target state="new">Your password has been successfully reset!</target> 8683 <target state="new">Your password has been successfully reset!</target>
@@ -10231,18 +10282,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10231 <target state="new">Too many attempts, please try again after 10282 <target state="new">Too many attempts, please try again after
10232 <x id="PH"/> minutes. 10283 <x id="PH"/> minutes.
10233 </target> 10284 </target>
10234 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 10285
10235 </trans-unit> 10286 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
10236 <trans-unit id="4965472196059235310" datatype="html"> 10287 <trans-unit id="4965472196059235310" datatype="html">
10237 <source>Too many attempts, please try again later.</source> 10288 <source>Too many attempts, please try again later.</source>
10238 <target state="new">Too many attempts, please try again later.</target> 10289 <target state="new">Too many attempts, please try again later.</target>
10239 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 10290
10240 </trans-unit> 10291 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
10241 <trans-unit id="1693549688987384699" datatype="html"> 10292 <trans-unit id="1693549688987384699" datatype="html">
10242 <source>Server error. Please retry later.</source> 10293 <source>Server error. Please retry later.</source>
10243 <target state="new">Server error. Please retry later.</target> 10294 <target state="new">Server error. Please retry later.</target>
10244 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 10295
10245 </trans-unit> 10296 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
10246 <trans-unit id="5927402622550505067" datatype="html"> 10297 <trans-unit id="5927402622550505067" datatype="html">
10247 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 10298 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
10248 <target state="new">Subscribed to all current channels of 10299 <target state="new">Subscribed to all current channels of
@@ -10841,34 +10892,34 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10841 <source>Your video was uploaded to your account and is private.</source> 10892 <source>Your video was uploaded to your account and is private.</source>
10842 <target state="new">Your video was uploaded to your account and is private.</target> 10893 <target state="new">Your video was uploaded to your account and is private.</target>
10843 10894
10844 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10895 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10845 <trans-unit id="5699822024600815733" datatype="html"> 10896 <trans-unit id="5699822024600815733" datatype="html">
10846 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10897 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10847 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target> 10898 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target>
10848 10899
10849 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10900 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10850 <trans-unit id="1219739004043110649" datatype="html"> 10901 <trans-unit id="1219739004043110649" datatype="html">
10851 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10902 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10852 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target> 10903 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target>
10853 10904
10854 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10905 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10855 <trans-unit id="6932865105766151309" datatype="html"> 10906 <trans-unit id="6932865105766151309" datatype="html">
10856 <source>Upload</source> 10907 <source>Upload</source>
10857 <target state="new">Upload</target> 10908 <target state="new">Upload</target>
10858 10909
10859 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10910 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10860 <trans-unit id="8278735427925094503" datatype="html"> 10911 <trans-unit id="8278735427925094503" datatype="html">
10861 <source>Upload <x id="PH"/> </source> 10912 <source>Upload <x id="PH"/> </source>
10862 <target state="new">Upload 10913 <target state="new">Upload
10863 <x id="PH"/> 10914 <x id="PH"/>
10864 </target> 10915 </target>
10865 10916
10866 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10917 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10867 <trans-unit id="5981816353437801748"> 10918 <trans-unit id="5981816353437801748">
10868 <source>Video published.</source> 10919 <source>Video published.</source>
10869 <target>ویدئو انتشار‌یافت</target> 10920 <target>ویدئو انتشار‌یافت</target>
10870 10921
10871 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10922 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10872 <trans-unit id="764164089183618119" datatype="html"> 10923 <trans-unit id="764164089183618119" datatype="html">
10873 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10924 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10874 <target state="new">You have unsaved changes! If you leave, your changes will be lost.</target> 10925 <target state="new">You have unsaved changes! If you leave, your changes will be lost.</target>
@@ -10936,27 +10987,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10936 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10987 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10937 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10988 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10938 10989
10939 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10990 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10940 <trans-unit id="5761611056224181752" datatype="html"> 10991 <trans-unit id="5761611056224181752" datatype="html">
10941 <source>Redirection</source> 10992 <source>Redirection</source>
10942 <target state="new">Redirection</target> 10993 <target state="new">Redirection</target>
10943 10994
10944 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10995 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10945 <trans-unit id="8858527736400081688" datatype="html"> 10996 <trans-unit id="8858527736400081688" datatype="html">
10946 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10997 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10947 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target> 10998 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target>
10948 10999
10949 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 11000 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10950 <trans-unit id="3937119019020041049" datatype="html"> 11001 <trans-unit id="3937119019020041049" datatype="html">
10951 <source>Mature or explicit content</source> 11002 <source>Mature or explicit content</source>
10952 <target state="new">Mature or explicit content</target> 11003 <target state="new">Mature or explicit content</target>
10953 11004
10954 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 11005 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10955 <trans-unit id="1755474755114288376" datatype="html"> 11006 <trans-unit id="1755474755114288376" datatype="html">
10956 <source>Up Next</source> 11007 <source>Up Next</source>
10957 <target state="new">Up Next</target> 11008 <target state="new">Up Next</target>
10958 11009
10959 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 11010 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10960 <trans-unit id="2159130950882492111" datatype="html"> 11011 <trans-unit id="2159130950882492111" datatype="html">
10961 <source>Cancel</source> 11012 <source>Cancel</source>
10962 <target state="new">Cancel</target> 11013 <target state="new">Cancel</target>
@@ -10966,62 +11017,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10966 <source>Autoplay is suspended</source> 11017 <source>Autoplay is suspended</source>
10967 <target state="new">Autoplay is suspended</target> 11018 <target state="new">Autoplay is suspended</target>
10968 11019
10969 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 11020 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10970 <trans-unit id="7895294730547405228" datatype="html"> 11021 <trans-unit id="7895294730547405228" datatype="html">
10971 <source>Enter/exit fullscreen (requires player focus)</source> 11022 <source>Enter/exit fullscreen (requires player focus)</source>
10972 <target state="new">Enter/exit fullscreen (requires player focus)</target> 11023 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10973 11024
10974 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 11025 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10975 <trans-unit id="7618388257165864759" datatype="html"> 11026 <trans-unit id="7618388257165864759" datatype="html">
10976 <source>Play/Pause the video (requires player focus)</source> 11027 <source>Play/Pause the video (requires player focus)</source>
10977 <target state="new">Play/Pause the video (requires player focus)</target> 11028 <target state="new">Play/Pause the video (requires player focus)</target>
10978 11029
10979 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 11030 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10980 <trans-unit id="7761890399634216630" datatype="html"> 11031 <trans-unit id="7761890399634216630" datatype="html">
10981 <source>Mute/unmute the video (requires player focus)</source> 11032 <source>Mute/unmute the video (requires player focus)</source>
10982 <target state="new">Mute/unmute the video (requires player focus)</target> 11033 <target state="new">Mute/unmute the video (requires player focus)</target>
10983 11034
10984 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 11035 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10985 <trans-unit id="5996585232248234904" datatype="html"> 11036 <trans-unit id="5996585232248234904" datatype="html">
10986 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 11037 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10987 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 11038 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10988 11039
10989 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 11040 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10990 <trans-unit id="3748765405903319998" datatype="html"> 11041 <trans-unit id="3748765405903319998" datatype="html">
10991 <source>Increase the volume (requires player focus)</source> 11042 <source>Increase the volume (requires player focus)</source>
10992 <target state="new">Increase the volume (requires player focus)</target> 11043 <target state="new">Increase the volume (requires player focus)</target>
10993 11044
10994 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 11045 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10995 <trans-unit id="5810704036407159982" datatype="html"> 11046 <trans-unit id="5810704036407159982" datatype="html">
10996 <source>Decrease the volume (requires player focus)</source> 11047 <source>Decrease the volume (requires player focus)</source>
10997 <target state="new">Decrease the volume (requires player focus)</target> 11048 <target state="new">Decrease the volume (requires player focus)</target>
10998 11049
10999 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 11050 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
11000 <trans-unit id="2622048822548065691" datatype="html"> 11051 <trans-unit id="2622048822548065691" datatype="html">
11001 <source>Seek the video forward (requires player focus)</source> 11052 <source>Seek the video forward (requires player focus)</source>
11002 <target state="new">Seek the video forward (requires player focus)</target> 11053 <target state="new">Seek the video forward (requires player focus)</target>
11003 11054
11004 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 11055 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
11005 <trans-unit id="6540078205109221153" datatype="html"> 11056 <trans-unit id="6540078205109221153" datatype="html">
11006 <source>Seek the video backward (requires player focus)</source> 11057 <source>Seek the video backward (requires player focus)</source>
11007 <target state="new">Seek the video backward (requires player focus)</target> 11058 <target state="new">Seek the video backward (requires player focus)</target>
11008 11059
11009 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 11060 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
11010 <trans-unit id="1956491957766210808" datatype="html"> 11061 <trans-unit id="1956491957766210808" datatype="html">
11011 <source>Increase playback rate (requires player focus)</source> 11062 <source>Increase playback rate (requires player focus)</source>
11012 <target state="new">Increase playback rate (requires player focus)</target> 11063 <target state="new">Increase playback rate (requires player focus)</target>
11013 11064
11014 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 11065 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
11015 <trans-unit id="5495529997674803186" datatype="html"> 11066 <trans-unit id="5495529997674803186" datatype="html">
11016 <source>Decrease playback rate (requires player focus)</source> 11067 <source>Decrease playback rate (requires player focus)</source>
11017 <target state="new">Decrease playback rate (requires player focus)</target> 11068 <target state="new">Decrease playback rate (requires player focus)</target>
11018 11069
11019 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 11070 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
11020 <trans-unit id="3178343147230721210" datatype="html"> 11071 <trans-unit id="3178343147230721210" datatype="html">
11021 <source>Navigate in the video frame by frame (requires player focus)</source> 11072 <source>Navigate in the video frame by frame (requires player focus)</source>
11022 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 11073 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
11023 11074
11024 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 11075 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
11025 <trans-unit id="8025996572234182184" datatype="html"> 11076 <trans-unit id="8025996572234182184" datatype="html">
11026 <source>Like the video</source> 11077 <source>Like the video</source>
11027 <target state="new">Like the video</target> 11078 <target state="new">Like the video</target>
diff --git a/client/src/locale/angular.fi-FI.xlf b/client/src/locale/angular.fi-FI.xlf
index 574375b5f..dd127aea8 100644
--- a/client/src/locale/angular.fi-FI.xlf
+++ b/client/src/locale/angular.fi-FI.xlf
@@ -307,7 +307,7 @@
307 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 307 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
308 </target> 308 </target>
309 309
310 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html"> 310 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html">
311 <source>My watch history</source><target state="new">My watch history</target> 311 <source>My watch history</source><target state="new">My watch history</target>
312 312
313 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 313 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
@@ -388,12 +388,12 @@
388 388
389 389
390 390
391 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 391 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
392 <trans-unit id="1006562256968398209" datatype="html"> 392 <trans-unit id="1006562256968398209" datatype="html">
393 <source>video</source> 393 <source>video</source>
394 <target state="new">video</target> 394 <target state="new">video</target>
395 395
396 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 396 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
397 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 397 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
398 398
399 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 399 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -455,10 +455,10 @@
455 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 455 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
456 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 456 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
457 457
458 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 458 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
459 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 459 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
460 460
461 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html"> 461 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html">
462 <source>subtitles</source><target state="new">subtitles</target> 462 <source>subtitles</source><target state="new">subtitles</target>
463 463
464 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 464 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
@@ -836,7 +836,7 @@
836 836
837 837
838 838
839 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group></trans-unit> 839 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
840 <trans-unit id="1502595455339510144"> 840 <trans-unit id="1502595455339510144">
841 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 841 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
842 <target> 842 <target>
@@ -919,7 +919,31 @@
919 <source>Federation</source> 919 <source>Federation</source>
920 <target state="new">Federation</target> 920 <target state="new">Federation</target>
921 921
922 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 922 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
923 <source>Following</source><target state="new">Following</target>
924 <context-group purpose="location">
925 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
926 <context context-type="linenumber">29</context>
927 </context-group>
928 <context-group purpose="location">
929 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
930 <context context-type="linenumber">31</context>
931 </context-group>
932 <context-group purpose="location">
933 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
934 <context context-type="linenumber">28</context>
935 </context-group>
936 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
937 <source>Followers</source><target state="new">Followers</target>
938 <context-group purpose="location">
939 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
940 <context context-type="linenumber">34</context>
941 </context-group>
942 <context-group purpose="location">
943 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
944 <context context-type="linenumber">37</context>
945 </context-group>
946 </trans-unit>
923 <trans-unit id="3541687134897970106" datatype="html"> 947 <trans-unit id="3541687134897970106" datatype="html">
924 <source>followers</source> 948 <source>followers</source>
925 <target state="new">followers</target> 949 <target state="new">followers</target>
@@ -981,7 +1005,7 @@
981 1005
982 1006
983 1007
984 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 1008 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
985 <trans-unit id="3616223838716839702"> 1009 <trans-unit id="3616223838716839702">
986 <source>Ban this user</source> 1010 <source>Ban this user</source>
987 <target>Sulje tämä käyttäjä</target> 1011 <target>Sulje tämä käyttäjä</target>
@@ -1047,17 +1071,11 @@
1047 1071
1048 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html"> 1072 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html">
1049 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1073 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1050 <context-group purpose="location"> 1074
1051 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1075 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7215649348148521605" datatype="html">
1052 <context context-type="linenumber">60,62</context>
1053 </context-group>
1054 </trans-unit><trans-unit id="7215649348148521605" datatype="html">
1055 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1076 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1056 <context-group purpose="location"> 1077
1057 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1078 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1058 <context context-type="linenumber">65,67</context>
1059 </context-group>
1060 </trans-unit>
1061 <trans-unit id="2392488717875840729"> 1079 <trans-unit id="2392488717875840729">
1062 <source>User</source> 1080 <source>User</source>
1063 <target>Käyttäjä</target> 1081 <target>Käyttäjä</target>
@@ -1068,7 +1086,13 @@
1068 <source>Username or email address</source> 1086 <source>Username or email address</source>
1069 <target>Käyttäjänimi tai sähköpostiosoite</target> 1087 <target>Käyttäjänimi tai sähköpostiosoite</target>
1070 1088
1071 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit> 1089 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="1758058452376026925" datatype="html">
1090 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1091 <context-group purpose="location">
1092 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1093 <context context-type="linenumber">33,34</context>
1094 </context-group>
1095 </trans-unit>
1072 1096
1073 <trans-unit id="1431416938026210429"> 1097 <trans-unit id="1431416938026210429">
1074 <source>Password</source> 1098 <source>Password</source>
@@ -1081,48 +1105,42 @@
1081 1105
1082 1106
1083 1107
1084 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group></trans-unit> 1108 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1085 <trans-unit id="8715156686857791956" datatype="html"> 1109 <trans-unit id="8715156686857791956" datatype="html">
1086 <source>Click here to reset your password</source> 1110 <source>Click here to reset your password</source>
1087 <target state="new">Click here to reset your password</target> 1111 <target state="new">Click here to reset your password</target>
1088 1112
1089 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html"> 1113 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html">
1090 <source>I forgot my password</source><target state="new">I forgot my password</target> 1114 <source>I forgot my password</source><target state="new">I forgot my password</target>
1091 <context-group purpose="location"> 1115
1092 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1116 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="2101170466365500913" datatype="html">
1093 <context context-type="linenumber">47</context>
1094 </context-group>
1095 </trans-unit><trans-unit id="2101170466365500913" datatype="html">
1096 <source> Logging into an account lets you publish content </source><target state="new"> Logging into an account lets you publish content </target> 1117 <source> Logging into an account lets you publish content </source><target state="new"> Logging into an account lets you publish content </target>
1097 <context-group purpose="location"> 1118
1098 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1119 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1099 <context context-type="linenumber">56,57</context>
1100 </context-group>
1101 </trans-unit>
1102 <trans-unit id="2454050363478003966"> 1120 <trans-unit id="2454050363478003966">
1103 <source>Login</source> 1121 <source>Login</source>
1104 <target>Kirjaudu sisään</target> 1122 <target>Kirjaudu sisään</target>
1105 1123
1106 1124
1107 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1125 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1108 <trans-unit id="3183213940445113677" datatype="html"> 1126 <trans-unit id="3183213940445113677" datatype="html">
1109 <source>Or sign in with</source> 1127 <source>Or sign in with</source>
1110 <target state="new">Or sign in with</target> 1128 <target state="new">Or sign in with</target>
1111 1129
1112 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit> 1130 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1113 <trans-unit id="3238209155172574367"> 1131 <trans-unit id="3238209155172574367">
1114 <source>Forgot your password</source> 1132 <source>Forgot your password</source>
1115 <target>Unohda salasanasi</target> 1133 <target>Unohda salasanasi</target>
1116 1134
1117 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group></trans-unit> 1135 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1118 <trans-unit id="87327320394367488" datatype="html"> 1136 <trans-unit id="87327320394367488" datatype="html">
1119 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1137 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1120 <target state="new">We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</target> 1138 <target state="new">We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</target>
1121 1139
1122 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html"> 1140 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html">
1123 <source> Enter your email address and we will send you a link to reset your password. </source><target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1141 <source> Enter your email address and we will send you a link to reset your password. </source><target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1124 1142
1125 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html"> 1143 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html">
1126 <source>An email with the reset password instructions will be sent to <x id="PH"/>. 1144 <source>An email with the reset password instructions will be sent to <x id="PH"/>.
1127The link will expire within 1 hour.</source><target state="new">An email with the reset password instructions will be sent to <x id="PH"/>. 1145The link will expire within 1 hour.</source><target state="new">An email with the reset password instructions will be sent to <x id="PH"/>.
1128The link will expire within 1 hour.</target> 1146The link will expire within 1 hour.</target>
@@ -1138,17 +1156,17 @@ The link will expire within 1 hour.</target>
1138 1156
1139 1157
1140 1158
1141 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group></trans-unit> 1159 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1142 <trans-unit id="3967269098753656610"> 1160 <trans-unit id="3967269098753656610">
1143 <source>Email address</source> 1161 <source>Email address</source>
1144 <target>Sähköpostiosoite</target> 1162 <target>Sähköpostiosoite</target>
1145 1163
1146 1164
1147 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html"> 1165 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html">
1148 <source>Reset</source><target state="new">Reset</target> 1166 <source>Reset</source><target state="new">Reset</target>
1149 1167
1150 <note priority="1" from="description">Password reset button</note> 1168 <note priority="1" from="description">Password reset button</note>
1151 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group></trans-unit> 1169 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1152 1170
1153 1171
1154 <trans-unit id="4319634264526091601" datatype="html"> 1172 <trans-unit id="4319634264526091601" datatype="html">
@@ -1488,7 +1506,7 @@ The link will expire within 1 hour.</target>
1488 <source>Create an account</source> 1506 <source>Create an account</source>
1489 <target>Luo tili</target> 1507 <target>Luo tili</target>
1490 1508
1491 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1509 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1492 1510
1493 <trans-unit id="3058024914967508975" datatype="html"> 1511 <trans-unit id="3058024914967508975" datatype="html">
1494 <source>My videos</source><target state="new">My videos</target> 1512 <source>My videos</source><target state="new">My videos</target>
@@ -1534,7 +1552,7 @@ The link will expire within 1 hour.</target>
1534 <target state="new">VIDEOS</target> 1552 <target state="new">VIDEOS</target>
1535 1553
1536 1554
1537 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html"> 1555 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html">
1538 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target> 1556 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target>
1539 1557
1540 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html"> 1558 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html">
@@ -1602,7 +1620,7 @@ The link will expire within 1 hour.</target>
1602 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html"> 1620 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html">
1603 <source>I'm a teapot</source><target state="new">I'm a teapot</target> 1621 <source>I'm a teapot</source><target state="new">I'm a teapot</target>
1604 1622
1605 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html"> 1623 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html">
1606 <source>That's an error.</source><target state="new">That's an error.</target> 1624 <source>That's an error.</source><target state="new">That's an error.</target>
1607 <context-group purpose="location"> 1625 <context-group purpose="location">
1608 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context> 1626 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context>
@@ -1668,7 +1686,7 @@ The link will expire within 1 hour.</target>
1668 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html"> 1686 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html">
1669 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1687 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1670 1688
1671 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group></trans-unit> 1689 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1672 1690
1673 <trans-unit id="5131854469652959713" datatype="html"> 1691 <trans-unit id="5131854469652959713" datatype="html">
1674 <source>GLOBAL SEARCH</source> 1692 <source>GLOBAL SEARCH</source>
@@ -2358,7 +2376,7 @@ The link will expire within 1 hour.</target>
2358 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2376 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2359 <source>Upload on hold</source><target state="new">Upload on hold</target> 2377 <source>Upload on hold</source><target state="new">Upload on hold</target>
2360 2378
2361 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2379 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2362 <trans-unit id="285180972645018518" datatype="html"> 2380 <trans-unit id="285180972645018518" datatype="html">
2363 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2381 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2364 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2382 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3026,11 +3044,7 @@ The link will expire within 1 hour.</target>
3026 <target>ID</target> 3044 <target>ID</target>
3027 3045
3028 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit> 3046 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit>
3029 <trans-unit id="2265605798180116441"> 3047
3030 <source>Follower handle</source>
3031 <target>Seuraajan käsittelijä</target>
3032
3033 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
3034 <trans-unit id="5911214550882917183"> 3048 <trans-unit id="5911214550882917183">
3035 <source>State</source> 3049 <source>State</source>
3036 <target>Tila</target> 3050 <target>Tila</target>
@@ -3112,11 +3126,7 @@ The link will expire within 1 hour.</target>
3112 </target> 3126 </target>
3113 3127
3114 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit> 3128 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
3115 <trans-unit id="6641024648411549335"> 3129
3116 <source>Host</source>
3117 <target>Isäntä</target>
3118
3119 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3120 <trans-unit id="6571718060636962350" datatype="html"> 3130 <trans-unit id="6571718060636962350" datatype="html">
3121 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3131 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3122 <target state="new">Redundancy allowed 3132 <target state="new">Redundancy allowed
@@ -3127,7 +3137,7 @@ The link will expire within 1 hour.</target>
3127 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html"> 3137 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html">
3128 <source>Unfollow</source><target state="new">Unfollow</target> 3138 <source>Unfollow</source><target state="new">Unfollow</target>
3129 3139
3130 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3140 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3131 <trans-unit id="8246779176913476983" datatype="html"> 3141 <trans-unit id="8246779176913476983" datatype="html">
3132 <source>Open instance in a new tab</source> 3142 <source>Open instance in a new tab</source>
3133 <target state="new">Open instance in a new tab</target> 3143 <target state="new">Open instance in a new tab</target>
@@ -3139,12 +3149,12 @@ The link will expire within 1 hour.</target>
3139 <source>No host found matching current filters.</source> 3149 <source>No host found matching current filters.</source>
3140 <target state="new">No host found matching current filters.</target> 3150 <target state="new">No host found matching current filters.</target>
3141 3151
3142 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3152 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3143 <trans-unit id="7274241885665071790" datatype="html"> 3153 <trans-unit id="7274241885665071790" datatype="html">
3144 <source>Your instance is not following anyone.</source> 3154 <source>Your instance is not following anyone.</source>
3145 <target state="new">Your instance is not following anyone.</target> 3155 <target state="new">Your instance is not following anyone.</target>
3146 3156
3147 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3157 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3148 <trans-unit id="4774348799569692380" datatype="html"> 3158 <trans-unit id="4774348799569692380" datatype="html">
3149 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3159 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3150 <target state="new">Showing 3160 <target state="new">Showing
@@ -3154,14 +3164,7 @@ The link will expire within 1 hour.</target>
3154 </target> 3164 </target>
3155 3165
3156 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3166 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3157 <trans-unit id="6275803119759621687" datatype="html"> 3167 <trans-unit id="9216117865911519658" datatype="html">
3158 <source>Follow domains</source>
3159 <target state="new">Follow domains</target>
3160
3161 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit><trans-unit id="1268699198448750610" datatype="html">
3162 <source>Follow instances</source><target state="new">Follow instances</target>
3163
3164 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3165 <source>Action</source><target state="new">Action</target> 3168 <source>Action</source><target state="new">Action</target>
3166 3169
3167 3170
@@ -3211,7 +3214,7 @@ The link will expire within 1 hour.</target>
3211 3214
3212 3215
3213 3216
3214 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html"> 3217 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html">
3215 <source>e.g. jane_doe</source><target state="new">e.g. jane_doe</target> 3218 <source>e.g. jane_doe</source><target state="new">e.g. jane_doe</target>
3216 3219
3217 <note priority="1" from="description">Username choice placeholder in the registration form</note> 3220 <note priority="1" from="description">Username choice placeholder in the registration form</note>
@@ -3243,7 +3246,7 @@ The link will expire within 1 hour.</target>
3243 <target>Rooli</target> 3246 <target>Rooli</target>
3244 3247
3245 3248
3246 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group></trans-unit> 3249 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3247 <trans-unit id="7046347992315328430" datatype="html"> 3250 <trans-unit id="7046347992315328430" datatype="html">
3248 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3251 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3249 <target state="new"> 3252 <target state="new">
@@ -3266,15 +3269,9 @@ The link will expire within 1 hour.</target>
3266 3269
3267 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html"> 3270 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html">
3268 <source>Auth plugin</source><target state="new">Auth plugin</target> 3271 <source>Auth plugin</source><target state="new">Auth plugin</target>
3269 <context-group purpose="location"> 3272
3270 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3273
3271 <context context-type="linenumber">188</context> 3274 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit><trans-unit id="588099657508661970" datatype="html">
3272 </context-group>
3273 <context-group purpose="location">
3274 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3275 <context context-type="linenumber">188</context>
3276 </context-group>
3277 </trans-unit><trans-unit id="588099657508661970" datatype="html">
3278 <source>None (local authentication)</source><target state="new">None (local authentication)</target> 3275 <source>None (local authentication)</source><target state="new">None (local authentication)</target>
3279 <context-group purpose="location"> 3276 <context-group purpose="location">
3280 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3277 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
@@ -3501,7 +3498,13 @@ The link will expire within 1 hour.</target>
3501 3498
3502 3499
3503 3500
3504 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="4691552465058437520" datatype="html"> 3501 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3502 <source>Follower</source><target state="new">Follower</target>
3503 <context-group purpose="location">
3504 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3505 <context context-type="linenumber">24</context>
3506 </context-group>
3507 </trans-unit><trans-unit id="4691552465058437520" datatype="html">
3505 <source>Commented video</source><target state="new">Commented video</target> 3508 <source>Commented video</source><target state="new">Commented video</target>
3506 3509
3507 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html"> 3510 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html">
@@ -3823,7 +3826,7 @@ The link will expire within 1 hour.</target>
3823 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 3826 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
3824 </target> 3827 </target>
3825 3828
3826 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3829 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3827 <trans-unit id="4058814854824495833" datatype="html"> 3830 <trans-unit id="4058814854824495833" datatype="html">
3828 <source>Mute domains</source> 3831 <source>Mute domains</source>
3829 <target state="new">Mute domains</target> 3832 <target state="new">Mute domains</target>
@@ -5622,11 +5625,8 @@ color: red;
5622 5625
5623 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5626 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5624 <source>CHANNELS</source><target state="new">CHANNELS</target> 5627 <source>CHANNELS</source><target state="new">CHANNELS</target>
5625 <context-group purpose="location"> 5628
5626 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5629 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5627 <context context-type="linenumber">82</context>
5628 </context-group>
5629 </trans-unit>
5630 5630
5631 <trans-unit id="3666829335406793239"> 5631 <trans-unit id="3666829335406793239">
5632 <source>This account does not have channels.</source> 5632 <source>This account does not have channels.</source>
@@ -5664,7 +5664,13 @@ channel with the same name (<x id="PH_2"/>)!</source><target state="new">Do you
5664It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 5664It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
5665channel with the same name (<x id="PH_2"/>)!</target> 5665channel with the same name (<x id="PH_2"/>)!</target>
5666 5666
5667 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5667 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5668 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5669 <context-group purpose="location">
5670 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5671 <context context-type="linenumber">48</context>
5672 </context-group>
5673 </trans-unit>
5668 <trans-unit id="5387007581996837469" datatype="html"> 5674 <trans-unit id="5387007581996837469" datatype="html">
5669 <source>My Channels</source> 5675 <source>My Channels</source>
5670 <target state="new">My Channels</target> 5676 <target state="new">My Channels</target>
@@ -6263,12 +6269,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6263 <source>Your message has been sent.</source> 6269 <source>Your message has been sent.</source>
6264 <target>Viestisi on lähetetty.</target> 6270 <target>Viestisi on lähetetty.</target>
6265 6271
6266 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6272 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6267 <trans-unit id="2072135752262464360"> 6273 <trans-unit id="2072135752262464360">
6268 <source>You already sent this form recently</source> 6274 <source>You already sent this form recently</source>
6269 <target>Lähetit jo tämän lomakkeen vasta.</target> 6275 <target>Lähetit jo tämän lomakkeen vasta.</target>
6270 6276
6271 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6277 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6272 <trans-unit id="819067926858619041" datatype="html"> 6278 <trans-unit id="819067926858619041" datatype="html">
6273 <source>Account videos</source><target state="new">Account videos</target> 6279 <source>Account videos</source><target state="new">Account videos</target>
6274 6280
@@ -6303,10 +6309,10 @@ channel with the same name (<x id="PH_2"/>)!</target>
6303 <x id="PH"/> direct account followers 6309 <x id="PH"/> direct account followers
6304 </target> 6310 </target>
6305 6311
6306 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html"> 6312 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html">
6307 <source>Report this account</source><target state="new">Report this account</target> 6313 <source>Report this account</source><target state="new">Report this account</target>
6308 6314
6309 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6315 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6310 6316
6311 <trans-unit id="1504521795586863905" datatype="html"> 6317 <trans-unit id="1504521795586863905" datatype="html">
6312 <source>VIDEOS</source><target state="new">VIDEOS</target> 6318 <source>VIDEOS</source><target state="new">VIDEOS</target>
@@ -6318,24 +6324,16 @@ channel with the same name (<x id="PH_2"/>)!</target>
6318 <target>Käyttäjänimi kopioitu</target> 6324 <target>Käyttäjänimi kopioitu</target>
6319 6325
6320 6326
6321 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html"> 6327 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html">
6322 <source>1 subscriber</source><target state="new">1 subscriber</target> 6328 <source>1 subscriber</source><target state="new">1 subscriber</target>
6323 6329
6324 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html"> 6330 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html">
6325 <source><x id="PH"/> subscribers</source><target state="new"><x id="PH"/> subscribers</target> 6331 <source><x id="PH"/> subscribers</source><target state="new"><x id="PH"/> subscribers</target>
6326 6332
6327 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6333 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6334
6335
6328 6336
6329 <trans-unit id="4682675125751819107" datatype="html">
6330 <source>Instances you follow</source>
6331 <target state="new">Instances you follow</target>
6332
6333 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6334 <trans-unit id="8899833753704589712" datatype="html">
6335 <source>Instances following you</source>
6336 <target state="new">Instances following you</target>
6337
6338 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6339 <trans-unit id="1035838766454786107" datatype="html"> 6337 <trans-unit id="1035838766454786107" datatype="html">
6340 <source>Audio-only</source> 6338 <source>Audio-only</source>
6341 <target state="new">Audio-only</target> 6339 <target state="new">Audio-only</target>
@@ -6383,7 +6381,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6383 <source>Auto (via ffmpeg)</source> 6381 <source>Auto (via ffmpeg)</source>
6384 <target>Automaattinen (ffmpeg avulla)</target> 6382 <target>Automaattinen (ffmpeg avulla)</target>
6385 6383
6386 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="931255636742351800" datatype="html"> 6384 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="3642770981085338761" datatype="html">
6385 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6386 <context-group purpose="location">
6387 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6388 <context context-type="linenumber">3</context>
6389 </context-group>
6390 </trans-unit><trans-unit id="931255636742351800" datatype="html">
6387 <source>No limit</source><target state="new">No limit</target> 6391 <source>No limit</source><target state="new">No limit</target>
6388 6392
6389 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html"> 6393 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html">
@@ -6502,17 +6506,33 @@ channel with the same name (<x id="PH_2"/>)!</target>
6502 <source>Domain is required.</source> 6506 <source>Domain is required.</source>
6503 <target state="new">Domain is required.</target> 6507 <target state="new">Domain is required.</target>
6504 6508
6505 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group></trans-unit> 6509 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6506 <trans-unit id="6780793142903080663" datatype="html"> 6510 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6507 <source>Domains entered are invalid.</source> 6511 <context-group purpose="location">
6508 <target state="new">Domains entered are invalid.</target> 6512 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6509 6513 <context context-type="linenumber">93</context>
6510 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6514 </context-group>
6511 <trans-unit id="5886492514458202177" datatype="html"> 6515 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6512 <source>Domains entered contain duplicates.</source> 6516 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6513 <target state="new">Domains entered contain duplicates.</target> 6517 <context-group purpose="location">
6514 6518 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6515 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 6519 <context context-type="linenumber">94</context>
6520 </context-group>
6521 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6522 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6523 <context-group purpose="location">
6524 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6525 <context context-type="linenumber">102</context>
6526 </context-group>
6527 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6528 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6529 <context-group purpose="location">
6530 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6531 <context context-type="linenumber">103</context>
6532 </context-group>
6533 </trans-unit>
6534
6535
6516 <trans-unit id="240806681889331244"> 6536 <trans-unit id="240806681889331244">
6517 <source>Unlimited</source> 6537 <source>Unlimited</source>
6518 <target>Rajaton</target> 6538 <target>Rajaton</target>
@@ -6643,7 +6663,27 @@ channel with the same name (<x id="PH_2"/>)!</target>
6643 <x id="PH"/> removed from instance followers 6663 <x id="PH"/> removed from instance followers
6644 </target> 6664 </target>
6645 6665
6646 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit> 6666 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit><trans-unit id="6018246591673612412" datatype="html">
6667 <source>Follow</source><target state="new">Follow</target>
6668 <context-group purpose="location">
6669 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6670 <context context-type="linenumber">3</context>
6671 </context-group>
6672 <context-group purpose="location">
6673 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6674 <context context-type="linenumber">37</context>
6675 </context-group>
6676 <context-group purpose="location">
6677 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6678 <context context-type="linenumber">18</context>
6679 </context-group>
6680 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6681 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6682 <context-group purpose="location">
6683 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6684 <context context-type="linenumber">11</context>
6685 </context-group>
6686 </trans-unit>
6647 <trans-unit id="2740793005745065895"> 6687 <trans-unit id="2740793005745065895">
6648 <source> 6688 <source>
6649 <x id="PH"/> is not valid 6689 <x id="PH"/> is not valid
@@ -6652,19 +6692,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
6652 <x id="PH"/> ei ole sallittu 6692 <x id="PH"/> ei ole sallittu
6653 </target> 6693 </target>
6654 6694
6655 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group></trans-unit> 6695 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6656 <trans-unit id="2355066641781598196"> 6696 <trans-unit id="2355066641781598196">
6657 <source>Follow request(s) sent!</source> 6697 <source>Follow request(s) sent!</source>
6658 <target>Seurantapyynnöt lähetetty!</target> 6698 <target>Seurantapyynnöt lähetetty!</target>
6659 6699
6660 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit> 6700 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6701 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6702 <context-group purpose="location">
6703 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6704 <context context-type="linenumber">3</context>
6705 </context-group>
6706 </trans-unit>
6661 <trans-unit id="4245720728052819482" datatype="html"> 6707 <trans-unit id="4245720728052819482" datatype="html">
6662 <source>Do you really want to unfollow <x id="PH"/>?</source> 6708 <source>Do you really want to unfollow <x id="PH"/>?</source>
6663 <target state="new">Do you really want to unfollow 6709 <target state="new">Do you really want to unfollow
6664 <x id="PH"/>? 6710 <x id="PH"/>?
6665 </target> 6711 </target>
6666 6712
6667 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6713 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6668 <trans-unit id="9160510009013134726"> 6714 <trans-unit id="9160510009013134726">
6669 <source>Unfollow</source> 6715 <source>Unfollow</source>
6670 <target>Lopeta seuranta</target> 6716 <target>Lopeta seuranta</target>
@@ -6676,7 +6722,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
6676 <x id="PH"/> enään. 6722 <x id="PH"/> enään.
6677 </target> 6723 </target>
6678 6724
6679 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 6725 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6680 <trans-unit id="2593763089859685916"> 6726 <trans-unit id="2593763089859685916">
6681 <source>enabled</source> 6727 <source>enabled</source>
6682 <target>käytössä</target> 6728 <target>käytössä</target>
@@ -7134,7 +7180,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7134 7180
7135 7181
7136 7182
7137 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit> 7183 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7138 <trans-unit id="5076187961693950167" datatype="html"> 7184 <trans-unit id="5076187961693950167" datatype="html">
7139 <source>Standard logs</source> 7185 <source>Standard logs</source>
7140 <target state="new">Standard logs</target> 7186 <target state="new">Standard logs</target>
@@ -7172,13 +7218,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7172 <source>Update user password</source> 7218 <source>Update user password</source>
7173 <target>Päivitä tilin salasana</target> 7219 <target>Päivitä tilin salasana</target>
7174 7220
7175 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit><trans-unit id="177544274549739411" datatype="html"> 7221 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit>
7176 <source>Following list</source><target state="new">Following list</target>
7177
7178 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group></trans-unit><trans-unit id="8092429110007204784" datatype="html">
7179 <source>Followers list</source><target state="new">Followers list</target>
7180
7181 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group></trans-unit>
7182 <trans-unit id="780323526182667308" datatype="html"> 7222 <trans-unit id="780323526182667308" datatype="html">
7183 <source>User <x id="PH"/> updated.</source> 7223 <source>User <x id="PH"/> updated.</source>
7184 <target state="new">User 7224 <target state="new">User
@@ -7209,13 +7249,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7209 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html"> 7249 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html">
7210 <source>Federation</source><target state="new">Federation</target> 7250 <source>Federation</source><target state="new">Federation</target>
7211 7251
7212 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="4682675125751819107" datatype="html"> 7252 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit>
7213 <source>Instances you follow</source><target state="new">Instances you follow</target>
7214
7215 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group></trans-unit><trans-unit id="8899833753704589712" datatype="html">
7216 <source>Instances following you</source><target state="new">Instances following you</target>
7217
7218 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit>
7219 <trans-unit id="3767259920053407667" datatype="html"> 7253 <trans-unit id="3767259920053407667" datatype="html">
7220 <source>Videos will be deleted, comments will be tombstoned.</source> 7254 <source>Videos will be deleted, comments will be tombstoned.</source>
7221 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7255 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7243,7 +7277,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7243 <target>Aseta sähköpostiosoite vahvistetuksi</target> 7277 <target>Aseta sähköpostiosoite vahvistetuksi</target>
7244 7278
7245 7279
7246 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7280 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7281 <source>Created</source><target state="new">Created</target>
7282 <context-group purpose="location">
7283 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7284 <context context-type="linenumber">115</context>
7285 </context-group>
7286 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7287 <source>Daily quota</source><target state="new">Daily quota</target>
7288 <context-group purpose="location">
7289 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7290 <context context-type="linenumber">120</context>
7291 </context-group>
7292 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7293 <source>Last login</source><target state="new">Last login</target>
7294 <context-group purpose="location">
7295 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7296 <context context-type="linenumber">122</context>
7297 </context-group>
7298 </trans-unit>
7247 <trans-unit id="3403978719736970622"> 7299 <trans-unit id="3403978719736970622">
7248 <source>You cannot ban root.</source> 7300 <source>You cannot ban root.</source>
7249 <target>Et voi estää root-käyttäjää.</target> 7301 <target>Et voi estää root-käyttäjää.</target>
@@ -7548,12 +7600,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
7548 <x id="PH"/> luotiin. 7600 <x id="PH"/> luotiin.
7549 </target> 7601 </target>
7550 7602
7551 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7603 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7552 <trans-unit id="8723777130353305761"> 7604 <trans-unit id="8723777130353305761">
7553 <source>This name already exists on this instance.</source> 7605 <source>This name already exists on this instance.</source>
7554 <target>Tämä nimi on jo olemassa tässä instanssissa.</target> 7606 <target>Tämä nimi on jo olemassa tässä instanssissa.</target>
7555 7607
7556 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7608 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7557 <trans-unit id="7589345916094713536"> 7609 <trans-unit id="7589345916094713536">
7558 <source>Video channel <x id="PH"/> updated.</source> 7610 <source>Video channel <x id="PH"/> updated.</source>
7559 <target>Videokanava 7611 <target>Videokanava
@@ -7570,13 +7622,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7570 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 7622 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7571 7623
7572 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7624 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7573 <trans-unit id="2575302837003821736" datatype="html"> 7625
7574 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7575 <target state="new">Please type the display name of the video channel (
7576 <x id="PH"/>) to confirm
7577 </target>
7578
7579 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7580 <trans-unit id="624066830180032195" datatype="html"> 7626 <trans-unit id="624066830180032195" datatype="html">
7581 <source>Video channel <x id="PH"/> deleted.</source> 7627 <source>Video channel <x id="PH"/> deleted.</source>
7582 <target state="new">Video channel 7628 <target state="new">Video channel
@@ -7719,7 +7765,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
7719 <source>Ownership change request sent.</source> 7765 <source>Ownership change request sent.</source>
7720 <target>Omistajuudenvaihtopyyntö lähetetty.</target> 7766 <target>Omistajuudenvaihtopyyntö lähetetty.</target>
7721 7767
7722 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 7768 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7699622144571229146" datatype="html">
7769 <source>Sort by</source><target state="new">Sort by</target>
7770 <context-group purpose="location">
7771 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7772 <context context-type="linenumber">26</context>
7773 </context-group>
7774 </trans-unit>
7723 <trans-unit id="3245220240937722814"> 7775 <trans-unit id="3245220240937722814">
7724 <source>My channels</source> 7776 <source>My channels</source>
7725 <target>Minun kanavat</target> 7777 <target>Minun kanavat</target>
@@ -7814,7 +7866,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7814 <target>Tilaa käyttäjä</target> 7866 <target>Tilaa käyttäjä</target>
7815 7867
7816 7868
7817 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 7869 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
7818 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 7870 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
7819 <context-group purpose="location"> 7871 <context-group purpose="location">
7820 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 7872 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -7861,34 +7913,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
7861 <source>Go to my subscriptions</source> 7913 <source>Go to my subscriptions</source>
7862 <target>Mene tilauksiini</target> 7914 <target>Mene tilauksiini</target>
7863 7915
7864 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 7916 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
7865 <trans-unit id="1136469849928650779"> 7917 <trans-unit id="1136469849928650779">
7866 <source>Go to my videos</source> 7918 <source>Go to my videos</source>
7867 <target>Mene videoihini</target> 7919 <target>Mene videoihini</target>
7868 7920
7869 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit> 7921 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
7870 <trans-unit id="7836683738999600376"> 7922 <trans-unit id="7836683738999600376">
7871 <source>Go to my imports</source> 7923 <source>Go to my imports</source>
7872 <target>Mene tuonteihini</target> 7924 <target>Mene tuonteihini</target>
7873 7925
7874 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 7926 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
7875 <trans-unit id="7511292153332773503"> 7927 <trans-unit id="7511292153332773503">
7876 <source>Go to my channels</source> 7928 <source>Go to my channels</source>
7877 <target>Mene kanaviini</target> 7929 <target>Mene kanaviini</target>
7878 7930
7879 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html"> 7931 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html">
7880 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>. 7932 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7881Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 7933Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7882Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 7934Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
7883 7935
7884 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group></trans-unit> 7936 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
7885 7937
7886 7938
7887 <trans-unit id="375263728166936544"> 7939 <trans-unit id="375263728166936544">
7888 <source>You need to reconnect.</source> 7940 <source>You need to reconnect.</source>
7889 <target>Sinun pitää yhdistää udelleen.</target> 7941 <target>Sinun pitää yhdistää udelleen.</target>
7890 7942
7891 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group></trans-unit> 7943 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
7892 <trans-unit id="2206638022166154361"> 7944 <trans-unit id="2206638022166154361">
7893 <source>Keyboard Shortcuts:</source> 7945 <source>Keyboard Shortcuts:</source>
7894 <target>Pikanäppäimet:</target> 7946 <target>Pikanäppäimet:</target>
@@ -7899,6 +7951,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7899 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 7951 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7900 <context context-type="linenumber">98</context> 7952 <context context-type="linenumber">98</context>
7901 </context-group> 7953 </context-group>
7954 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
7955 <source>In my library</source><target state="new">In my library</target>
7956 <context-group purpose="location">
7957 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7958 <context context-type="linenumber">104</context>
7959 </context-group>
7902 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 7960 </trans-unit><trans-unit id="232050922346936574" datatype="html">
7903 <source>Trending</source><target state="new">Trending</target> 7961 <source>Trending</source><target state="new">Trending</target>
7904 7962
@@ -7922,38 +7980,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7922 <source>Incorrect username or password.</source> 7980 <source>Incorrect username or password.</source>
7923 <target>Virheellinen käyttäjänimi tai salasana.</target> 7981 <target>Virheellinen käyttäjänimi tai salasana.</target>
7924 7982
7925 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 7983 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
7926 <trans-unit id="6974874606619467663" datatype="html"> 7984 <trans-unit id="6974874606619467663" datatype="html">
7927 <source>Your account is blocked.</source> 7985 <source>Your account is blocked.</source>
7928 <target state="new">Your account is blocked.</target> 7986 <target state="new">Your account is blocked.</target>
7929 7987
7930 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 7988 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
7931 7989
7932 <trans-unit id="7939914198003891823" datatype="html"> 7990 <trans-unit id="7939914198003891823" datatype="html">
7933 <source>any language</source> 7991 <source>any language</source>
7934 <target state="new">any language</target> 7992 <target state="new">any language</target>
7935 7993
7936 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 7994 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
7937 <trans-unit id="5633144232269377096" datatype="html"> 7995 <trans-unit id="5633144232269377096" datatype="html">
7938 <source>hide</source> 7996 <source>hide</source>
7939 <target state="new">hide</target> 7997 <target state="new">hide</target>
7940 7998
7941 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 7999 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
7942 <trans-unit id="8603861867909474404" datatype="html"> 8000 <trans-unit id="8603861867909474404" datatype="html">
7943 <source>blur</source> 8001 <source>blur</source>
7944 <target state="new">blur</target> 8002 <target state="new">blur</target>
7945 8003
7946 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8004 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
7947 <trans-unit id="4534458451100881847" datatype="html"> 8005 <trans-unit id="4534458451100881847" datatype="html">
7948 <source>display</source> 8006 <source>display</source>
7949 <target state="new">display</target> 8007 <target state="new">display</target>
7950 8008
7951 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8009 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
7952 <trans-unit id="4467323362722952678" datatype="html"> 8010 <trans-unit id="4467323362722952678" datatype="html">
7953 <source>Unknown</source> 8011 <source>Unknown</source>
7954 <target state="new">Unknown</target> 8012 <target state="new">Unknown</target>
7955 8013
7956 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8014 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
7957 <trans-unit id="8781423666414310853" datatype="html"> 8015 <trans-unit id="8781423666414310853" datatype="html">
7958 <source>Your password has been successfully reset!</source> 8016 <source>Your password has been successfully reset!</source>
7959 <target state="new">Your password has been successfully reset!</target> 8017 <target state="new">Your password has been successfully reset!</target>
@@ -9497,17 +9555,17 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9497 <x id="PH"/> minutes. 9555 <x id="PH"/> minutes.
9498 </target> 9556 </target>
9499 9557
9500 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 9558 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9501 <trans-unit id="4965472196059235310"> 9559 <trans-unit id="4965472196059235310">
9502 <source>Too many attempts, please try again later.</source> 9560 <source>Too many attempts, please try again later.</source>
9503 <target>Liian monta yritystä, yritä myöhemmin uudelleen.</target> 9561 <target>Liian monta yritystä, yritä myöhemmin uudelleen.</target>
9504 9562
9505 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group></trans-unit> 9563 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9506 <trans-unit id="1693549688987384699"> 9564 <trans-unit id="1693549688987384699">
9507 <source>Server error. Please retry later.</source> 9565 <source>Server error. Please retry later.</source>
9508 <target>Palvelinvirhe. Yritä myöhemmin uudelleen.</target> 9566 <target>Palvelinvirhe. Yritä myöhemmin uudelleen.</target>
9509 9567
9510 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 9568 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9511 <trans-unit id="5927402622550505067" datatype="html"> 9569 <trans-unit id="5927402622550505067" datatype="html">
9512 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9570 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9513 <target state="new">Subscribed to all current channels of 9571 <target state="new">Subscribed to all current channels of
@@ -10005,20 +10063,20 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10005 <source>Your video was uploaded to your account and is private.</source> 10063 <source>Your video was uploaded to your account and is private.</source>
10006 <target state="new">Your video was uploaded to your account and is private.</target> 10064 <target state="new">Your video was uploaded to your account and is private.</target>
10007 10065
10008 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10066 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10009 <trans-unit id="5699822024600815733" datatype="html"> 10067 <trans-unit id="5699822024600815733" datatype="html">
10010 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10068 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10011 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target> 10069 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target>
10012 10070
10013 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10071 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10014 <trans-unit id="1219739004043110649"> 10072 <trans-unit id="1219739004043110649">
10015 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10073 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10016 <target>Videota ei ole vielä ladattu, haluatko varmasti poistua sivulta?</target> 10074 <target>Videota ei ole vielä ladattu, haluatko varmasti poistua sivulta?</target>
10017 10075
10018 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html"> 10076 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html">
10019 <source>Upload</source><target state="new">Upload</target> 10077 <source>Upload</source><target state="new">Upload</target>
10020 10078
10021 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10079 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10022 <trans-unit id="8278735427925094503"> 10080 <trans-unit id="8278735427925094503">
10023 <source>Upload 10081 <source>Upload
10024 <x id="PH"/> 10082 <x id="PH"/>
@@ -10027,13 +10085,13 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10027 <x id="PH"/> 10085 <x id="PH"/>
10028 </target> 10086 </target>
10029 10087
10030 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10088 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10031 10089
10032 <trans-unit id="5981816353437801748"> 10090 <trans-unit id="5981816353437801748">
10033 <source>Video published.</source> 10091 <source>Video published.</source>
10034 <target>Video julkaistu.</target> 10092 <target>Video julkaistu.</target>
10035 10093
10036 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10094 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10037 10095
10038 10096
10039 <trans-unit id="764164089183618119"> 10097 <trans-unit id="764164089183618119">
@@ -10096,26 +10154,26 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10096 <trans-unit id="961774488937452220" datatype="html"> 10154 <trans-unit id="961774488937452220" datatype="html">
10097 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10155 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10098 10156
10099 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html"> 10157 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html">
10100 <source>Redirection</source><target state="new">Redirection</target> 10158 <source>Redirection</source><target state="new">Redirection</target>
10101 10159
10102 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10160 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10103 10161
10104 <trans-unit id="8858527736400081688"> 10162 <trans-unit id="8858527736400081688">
10105 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10163 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10106 <target>Tämä video sisältää aikuisille tarkoitettua sisältöä. Haluatko varmasti jatkaa?</target> 10164 <target>Tämä video sisältää aikuisille tarkoitettua sisältöä. Haluatko varmasti jatkaa?</target>
10107 10165
10108 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10166 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10109 <trans-unit id="3937119019020041049"> 10167 <trans-unit id="3937119019020041049">
10110 <source>Mature or explicit content</source> 10168 <source>Mature or explicit content</source>
10111 <target>Aikuisille tarkoitettu sisältö</target> 10169 <target>Aikuisille tarkoitettu sisältö</target>
10112 10170
10113 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10171 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10114 <trans-unit id="1755474755114288376" datatype="html"> 10172 <trans-unit id="1755474755114288376" datatype="html">
10115 <source>Up Next</source> 10173 <source>Up Next</source>
10116 <target state="new">Up Next</target> 10174 <target state="new">Up Next</target>
10117 10175
10118 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html"> 10176 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html">
10119 <source>Cancel</source><target state="new">Cancel</target> 10177 <source>Cancel</source><target state="new">Cancel</target>
10120 10178
10121 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit> 10179 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit>
@@ -10123,62 +10181,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10123 <source>Autoplay is suspended</source> 10181 <source>Autoplay is suspended</source>
10124 <target state="new">Autoplay is suspended</target> 10182 <target state="new">Autoplay is suspended</target>
10125 10183
10126 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10184 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10127 <trans-unit id="7895294730547405228" datatype="html"> 10185 <trans-unit id="7895294730547405228" datatype="html">
10128 <source>Enter/exit fullscreen (requires player focus)</source> 10186 <source>Enter/exit fullscreen (requires player focus)</source>
10129 <target state="new">Enter/exit fullscreen (requires player focus)</target> 10187 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10130 10188
10131 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10189 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10132 <trans-unit id="7618388257165864759" datatype="html"> 10190 <trans-unit id="7618388257165864759" datatype="html">
10133 <source>Play/Pause the video (requires player focus)</source> 10191 <source>Play/Pause the video (requires player focus)</source>
10134 <target state="new">Play/Pause the video (requires player focus)</target> 10192 <target state="new">Play/Pause the video (requires player focus)</target>
10135 10193
10136 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10194 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10137 <trans-unit id="7761890399634216630" datatype="html"> 10195 <trans-unit id="7761890399634216630" datatype="html">
10138 <source>Mute/unmute the video (requires player focus)</source> 10196 <source>Mute/unmute the video (requires player focus)</source>
10139 <target state="new">Mute/unmute the video (requires player focus)</target> 10197 <target state="new">Mute/unmute the video (requires player focus)</target>
10140 10198
10141 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10199 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10142 <trans-unit id="5996585232248234904" datatype="html"> 10200 <trans-unit id="5996585232248234904" datatype="html">
10143 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10201 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10144 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10202 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10145 10203
10146 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10204 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10147 <trans-unit id="3748765405903319998" datatype="html"> 10205 <trans-unit id="3748765405903319998" datatype="html">
10148 <source>Increase the volume (requires player focus)</source> 10206 <source>Increase the volume (requires player focus)</source>
10149 <target state="new">Increase the volume (requires player focus)</target> 10207 <target state="new">Increase the volume (requires player focus)</target>
10150 10208
10151 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10209 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10152 <trans-unit id="5810704036407159982" datatype="html"> 10210 <trans-unit id="5810704036407159982" datatype="html">
10153 <source>Decrease the volume (requires player focus)</source> 10211 <source>Decrease the volume (requires player focus)</source>
10154 <target state="new">Decrease the volume (requires player focus)</target> 10212 <target state="new">Decrease the volume (requires player focus)</target>
10155 10213
10156 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10214 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10157 <trans-unit id="2622048822548065691" datatype="html"> 10215 <trans-unit id="2622048822548065691" datatype="html">
10158 <source>Seek the video forward (requires player focus)</source> 10216 <source>Seek the video forward (requires player focus)</source>
10159 <target state="new">Seek the video forward (requires player focus)</target> 10217 <target state="new">Seek the video forward (requires player focus)</target>
10160 10218
10161 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10219 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10162 <trans-unit id="6540078205109221153" datatype="html"> 10220 <trans-unit id="6540078205109221153" datatype="html">
10163 <source>Seek the video backward (requires player focus)</source> 10221 <source>Seek the video backward (requires player focus)</source>
10164 <target state="new">Seek the video backward (requires player focus)</target> 10222 <target state="new">Seek the video backward (requires player focus)</target>
10165 10223
10166 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10224 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10167 <trans-unit id="1956491957766210808" datatype="html"> 10225 <trans-unit id="1956491957766210808" datatype="html">
10168 <source>Increase playback rate (requires player focus)</source> 10226 <source>Increase playback rate (requires player focus)</source>
10169 <target state="new">Increase playback rate (requires player focus)</target> 10227 <target state="new">Increase playback rate (requires player focus)</target>
10170 10228
10171 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10229 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10172 <trans-unit id="5495529997674803186" datatype="html"> 10230 <trans-unit id="5495529997674803186" datatype="html">
10173 <source>Decrease playback rate (requires player focus)</source> 10231 <source>Decrease playback rate (requires player focus)</source>
10174 <target state="new">Decrease playback rate (requires player focus)</target> 10232 <target state="new">Decrease playback rate (requires player focus)</target>
10175 10233
10176 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10234 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10177 <trans-unit id="3178343147230721210" datatype="html"> 10235 <trans-unit id="3178343147230721210" datatype="html">
10178 <source>Navigate in the video frame by frame (requires player focus)</source> 10236 <source>Navigate in the video frame by frame (requires player focus)</source>
10179 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 10237 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10180 10238
10181 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10239 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10182 <trans-unit id="8025996572234182184"> 10240 <trans-unit id="8025996572234182184">
10183 <source>Like the video</source> 10241 <source>Like the video</source>
10184 <target>Tykkää videosta</target> 10242 <target>Tykkää videosta</target>
diff --git a/client/src/locale/angular.fr-FR.xlf b/client/src/locale/angular.fr-FR.xlf
index a092c252f..2a64674e7 100644
--- a/client/src/locale/angular.fr-FR.xlf
+++ b/client/src/locale/angular.fr-FR.xlf
@@ -162,19 +162,19 @@
162 <target state="translated"> 162 <target state="translated">
163 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 163 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
164 </target> 164 </target>
165 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group> 165
166 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group> 166
167 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group> 167
168 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group> 168
169 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group> 169
170 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 170
171 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group> 171
172 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group> 172
173 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group> 173
174 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group> 174
175 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group> 175
176 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group> 176
177 </trans-unit> 177 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
178 <trans-unit id="1486537403020619891" datatype="html"> 178 <trans-unit id="1486537403020619891" datatype="html">
179 <source>My watch history</source> 179 <source>My watch history</source>
180 <target state="translated">Mon historique de visionnage</target> 180 <target state="translated">Mon historique de visionnage</target>
@@ -243,22 +243,22 @@
243 <trans-unit id="5674286808255988565"> 243 <trans-unit id="5674286808255988565">
244 <source>Create</source> 244 <source>Create</source>
245 <target>Créer</target> 245 <target>Créer</target>
246 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 246
247 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 247
248 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 248
249 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 249
250 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 250
251 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 251
252 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 252
253 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 253
254 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 254
255 </trans-unit> 255 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
256 <trans-unit id="1006562256968398209" datatype="html"> 256 <trans-unit id="1006562256968398209" datatype="html">
257 <source>video</source> 257 <source>video</source>
258 <target state="translated">vidéo</target> 258 <target state="translated">vidéo</target>
259 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group> 259
260 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group> 260
261 </trans-unit> 261 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
262 <trans-unit id="6438815964972582865" datatype="html"> 262 <trans-unit id="6438815964972582865" datatype="html">
263 <source>The following link contains a private token and should not be shared with anyone.</source> 263 <source>The following link contains a private token and should not be shared with anyone.</source>
264 <target state="translated">Le lien suivant contient un jeton privé et ne doit être partagé avec personne.</target> 264 <target state="translated">Le lien suivant contient un jeton privé et ne doit être partagé avec personne.</target>
@@ -330,13 +330,13 @@
330 <trans-unit id="6995024616159044376" datatype="html"> 330 <trans-unit id="6995024616159044376" datatype="html">
331 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 331 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
332 <target state="translated">Votre quota est dépassé avec cette vidéo (taille de la vidéo : <x id="PH" equiv-text="videoSizeBytes"/>, utilisé : <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota : <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 332 <target state="translated">Votre quota est dépassé avec cette vidéo (taille de la vidéo : <x id="PH" equiv-text="videoSizeBytes"/>, utilisé : <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota : <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
333 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 333
334 </trans-unit> 334 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
335 <trans-unit id="7873395933409147217" datatype="html"> 335 <trans-unit id="7873395933409147217" datatype="html">
336 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 336 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
337 <target state="translated">Votre quota journalier est dépassé avec cette vidéo (taille de la vidéo : <x id="PH" equiv-text="videoSizeBytes"/>, utilisé : <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota : <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 337 <target state="translated">Votre quota journalier est dépassé avec cette vidéo (taille de la vidéo : <x id="PH" equiv-text="videoSizeBytes"/>, utilisé : <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota : <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
338 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 338
339 </trans-unit> 339 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
340 <trans-unit id="5235042777215655908" datatype="html"> 340 <trans-unit id="5235042777215655908" datatype="html">
341 <source>subtitles</source> 341 <source>subtitles</source>
342 <target state="translated">sous-titres</target> 342 <target state="translated">sous-titres</target>
@@ -776,10 +776,10 @@
776 <trans-unit id="2602586221576511475"> 776 <trans-unit id="2602586221576511475">
777 <source>Video quota</source> 777 <source>Video quota</source>
778 <target>Quota des vidéos</target> 778 <target>Quota des vidéos</target>
779 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 779
780 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 780
781 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 781
782 </trans-unit> 782 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
783 <trans-unit id="1502595455339510144"> 783 <trans-unit id="1502595455339510144">
784 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 784 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
785 <target>Illimité <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> par jour)<x id="CLOSE_TAG_NG_CONTAINER"/></target> 785 <target>Illimité <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> par jour)<x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -859,6 +859,30 @@
859 <target>Fédération</target> 859 <target>Fédération</target>
860 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 860 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
861 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 861 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
862 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
863 <source>Following</source><target state="new">Following</target>
864 <context-group purpose="location">
865 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
866 <context context-type="linenumber">29</context>
867 </context-group>
868 <context-group purpose="location">
869 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
870 <context context-type="linenumber">31</context>
871 </context-group>
872 <context-group purpose="location">
873 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
874 <context context-type="linenumber">28</context>
875 </context-group>
876 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
877 <source>Followers</source><target state="new">Followers</target>
878 <context-group purpose="location">
879 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
880 <context context-type="linenumber">34</context>
881 </context-group>
882 <context-group purpose="location">
883 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
884 <context context-type="linenumber">37</context>
885 </context-group>
862 </trans-unit> 886 </trans-unit>
863 <trans-unit id="3541687134897970106"> 887 <trans-unit id="3541687134897970106">
864 <source>followers</source> 888 <source>followers</source>
@@ -920,25 +944,25 @@
920 <trans-unit id="2159130950882492111"> 944 <trans-unit id="2159130950882492111">
921 <source>Cancel</source> 945 <source>Cancel</source>
922 <target>Annuler</target> 946 <target>Annuler</target>
923 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group> 947
924 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group> 948
925 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group> 949
926 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group> 950
927 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group> 951
928 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group> 952
929 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group> 953
930 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group> 954
931 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group> 955
932 <context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group> 956
933 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group> 957
934 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group> 958
935 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 959
936 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 960
937 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group> 961
938 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group> 962
939 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group> 963
940 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group> 964
941 </trans-unit> 965 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
942 <trans-unit id="3616223838716839702"> 966 <trans-unit id="3616223838716839702">
943 <source>Ban this user</source> 967 <source>Ban this user</source>
944 <target>Bannir cet·te utilisateur·rice</target> 968 <target>Bannir cet·te utilisateur·rice</target>
@@ -1003,19 +1027,13 @@
1003 <trans-unit id="7252854992688790751" datatype="html"> 1027 <trans-unit id="7252854992688790751" datatype="html">
1004 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1028 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1005 <target state="translated">Cette instance permet l'enregistrement. Toutefois, il faut veiller à vérifier les <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>conditions d'utilisation<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>conditions d'utilisation<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> avant de créer un compte. Vous pouvez également rechercher une autre instance correspondant exactement à vos besoins sur : <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1029 <target state="translated">Cette instance permet l'enregistrement. Toutefois, il faut veiller à vérifier les <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>conditions d'utilisation<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>conditions d'utilisation<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> avant de créer un compte. Vous pouvez également rechercher une autre instance correspondant exactement à vos besoins sur : <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1006 <context-group purpose="location"> 1030
1007 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1031 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1008 <context context-type="linenumber">60,62</context>
1009 </context-group>
1010 </trans-unit>
1011 <trans-unit id="7215649348148521605" datatype="html"> 1032 <trans-unit id="7215649348148521605" datatype="html">
1012 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1033 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1013 <target state="translated">Actuellement, cette instance ne permet pas l'enregistrement des utilisateurs, vous pouvez vérifier les <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>conditions d'utilisation<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> pour plus de détails ou trouvez une instance qui vous donne la possibilité de créer un compte et d'y télécharger vos vidéos. Trouvez la vôtre parmi plusieurs instances à l'adresse suivante : <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1034 <target state="translated">Actuellement, cette instance ne permet pas l'enregistrement des utilisateurs, vous pouvez vérifier les <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>conditions d'utilisation<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> pour plus de détails ou trouvez une instance qui vous donne la possibilité de créer un compte et d'y télécharger vos vidéos. Trouvez la vôtre parmi plusieurs instances à l'adresse suivante : <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1014 <context-group purpose="location"> 1035
1015 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1036 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1016 <context context-type="linenumber">65,67</context>
1017 </context-group>
1018 </trans-unit>
1019 <trans-unit id="2392488717875840729"> 1037 <trans-unit id="2392488717875840729">
1020 <source>User</source> 1038 <source>User</source>
1021 <target>Utilisateur·rice</target> 1039 <target>Utilisateur·rice</target>
@@ -1026,67 +1044,67 @@
1026 <source>Username or email address</source> 1044 <source>Username or email address</source>
1027 <target>Identifiant ou adresse de courriel</target> 1045 <target>Identifiant ou adresse de courriel</target>
1028 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1046 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1047 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1048 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1049 <context-group purpose="location">
1050 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1051 <context context-type="linenumber">33,34</context>
1052 </context-group>
1029 </trans-unit> 1053 </trans-unit>
1030 <trans-unit id="1431416938026210429"> 1054 <trans-unit id="1431416938026210429">
1031 <source>Password</source> 1055 <source>Password</source>
1032 <target>Mot de passe</target> 1056 <target>Mot de passe</target>
1033 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1057
1034 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1058
1035 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1059
1036 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1060
1037 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1061
1038 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1062
1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1063
1040 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1064
1041 </trans-unit> 1065 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1042 <trans-unit id="8715156686857791956" datatype="html"> 1066 <trans-unit id="8715156686857791956" datatype="html">
1043 <source>Click here to reset your password</source> 1067 <source>Click here to reset your password</source>
1044 <target state="translated">Cliquez ici pour réinitialiser votre mot de passe</target> 1068 <target state="translated">Cliquez ici pour réinitialiser votre mot de passe</target>
1045 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1069
1046 </trans-unit> 1070 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1047 <trans-unit id="892063502898494584" datatype="html"> 1071 <trans-unit id="892063502898494584" datatype="html">
1048 <source>I forgot my password</source> 1072 <source>I forgot my password</source>
1049 <target state="translated">J'ai oublié mon mot de passe</target> 1073 <target state="translated">J'ai oublié mon mot de passe</target>
1050 <context-group purpose="location"> 1074
1051 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1075 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1052 <context context-type="linenumber">47</context>
1053 </context-group>
1054 </trans-unit>
1055 <trans-unit id="2101170466365500913" datatype="html"> 1076 <trans-unit id="2101170466365500913" datatype="html">
1056 <source>Logging into an account lets you publish content</source> 1077 <source>Logging into an account lets you publish content</source>
1057 <target state="translated">La connexion à un compte vous permet de publier du contenu</target> 1078 <target state="translated">La connexion à un compte vous permet de publier du contenu</target>
1058 <context-group purpose="location"> 1079
1059 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1080 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1060 <context context-type="linenumber">56,57</context>
1061 </context-group>
1062 </trans-unit>
1063 <trans-unit id="2454050363478003966"> 1081 <trans-unit id="2454050363478003966">
1064 <source>Login</source> 1082 <source>Login</source>
1065 <target>Se connecter</target> 1083 <target>Se connecter</target>
1066 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1084
1067 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1085
1068 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1086
1069 </trans-unit> 1087 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1070 <trans-unit id="3183213940445113677" datatype="html"> 1088 <trans-unit id="3183213940445113677" datatype="html">
1071 <source>Or sign in with</source> 1089 <source>Or sign in with</source>
1072 <target state="translated">Ou connectez vous</target> 1090 <target state="translated">Ou connectez vous</target>
1073 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1091
1074 </trans-unit> 1092 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1075 <trans-unit id="3238209155172574367"> 1093 <trans-unit id="3238209155172574367">
1076 <source>Forgot your password</source> 1094 <source>Forgot your password</source>
1077 <target>Oubli de votre mot de passe</target> 1095 <target>Oubli de votre mot de passe</target>
1078 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1096
1079 </trans-unit> 1097 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1080 <trans-unit id="87327320394367488"> 1098 <trans-unit id="87327320394367488">
1081 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1099 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1082 <target>Nous sommes désolés, vous ne pouvez pas réinitialiser votre mot de passe car l'administrateur·rice de votre instance n'a pas configuré le système de courrier électronique de PeerTube.</target> 1100 <target>Nous sommes désolés, vous ne pouvez pas réinitialiser votre mot de passe car l'administrateur·rice de votre instance n'a pas configuré le système de courrier électronique de PeerTube.</target>
1083 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1101
1084 </trans-unit> 1102 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1085 <trans-unit id="3188014010833256853" datatype="html"> 1103 <trans-unit id="3188014010833256853" datatype="html">
1086 <source>Enter your email address and we will send you a link to reset your password.</source> 1104 <source>Enter your email address and we will send you a link to reset your password.</source>
1087 <target state="translated">Saisissez votre adresse électronique et nous vous enverrons un lien pour réinitialiser votre mot de passe.</target> 1105 <target state="translated">Saisissez votre adresse électronique et nous vous enverrons un lien pour réinitialiser votre mot de passe.</target>
1088 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1106
1089 </trans-unit> 1107 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1090 <trans-unit id="1190256911880544559" datatype="html"> 1108 <trans-unit id="1190256911880544559" datatype="html">
1091 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1109 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1092The link will expire within 1 hour.</source> 1110The link will expire within 1 hour.</source>
@@ -1096,26 +1114,26 @@ The link will expire within 1 hour.</source>
1096 <trans-unit id="4768749765465246664"> 1114 <trans-unit id="4768749765465246664">
1097 <source>Email</source> 1115 <source>Email</source>
1098 <target>Courriel</target> 1116 <target>Courriel</target>
1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1117
1100 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1118
1101 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1119
1102 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1120
1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1121
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1122
1105 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1123
1106 </trans-unit> 1124 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1107 <trans-unit id="3967269098753656610"> 1125 <trans-unit id="3967269098753656610">
1108 <source>Email address</source> 1126 <source>Email address</source>
1109 <target>Adresse de courriel</target> 1127 <target>Adresse de courriel</target>
1110 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1128
1111 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1129
1112 </trans-unit> 1130 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1113 <trans-unit id="7808756054397155068" datatype="html"> 1131 <trans-unit id="7808756054397155068" datatype="html">
1114 <source>Reset</source> 1132 <source>Reset</source>
1115 <target state="translated">Réinitialiser</target> 1133 <target state="translated">Réinitialiser</target>
1116 <note priority="1" from="description">Password reset button</note> 1134 <note priority="1" from="description">Password reset button</note>
1117 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1135
1118 </trans-unit> 1136 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1119 <trans-unit id="4319634264526091601" datatype="html"> 1137 <trans-unit id="4319634264526091601" datatype="html">
1120 <source>on this instance</source> 1138 <source>on this instance</source>
1121 <target state="translated">sur cette instance</target> 1139 <target state="translated">sur cette instance</target>
@@ -1445,9 +1463,9 @@ The link will expire within 1 hour.</source>
1445 <trans-unit id="2308975396733519902"> 1463 <trans-unit id="2308975396733519902">
1446 <source>Create an account</source> 1464 <source>Create an account</source>
1447 <target>Créer un compte</target> 1465 <target>Créer un compte</target>
1448 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1466
1449 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1467
1450 </trans-unit> 1468 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1451 <trans-unit id="3058024914967508975" datatype="html"> 1469 <trans-unit id="3058024914967508975" datatype="html">
1452 <source>My videos</source> 1470 <source>My videos</source>
1453 <target state="translated">Mes vidéos</target> 1471 <target state="translated">Mes vidéos</target>
@@ -1514,10 +1532,10 @@ The link will expire within 1 hour.</source>
1514 <trans-unit id="1504521795586863905" datatype="html"> 1532 <trans-unit id="1504521795586863905" datatype="html">
1515 <source>VIDEOS</source> 1533 <source>VIDEOS</source>
1516 <target state="translated">VIDÉOS</target> 1534 <target state="translated">VIDÉOS</target>
1517 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1535
1518 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1536
1519 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1537
1520 </trans-unit> 1538 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1521 <trans-unit id="667372110624203230" datatype="html"> 1539 <trans-unit id="667372110624203230" datatype="html">
1522 <source>Import jobs concurrency</source> 1540 <source>Import jobs concurrency</source>
1523 <target state="translated">Importer des travaux en même temps</target> 1541 <target state="translated">Importer des travaux en même temps</target>
@@ -1596,8 +1614,8 @@ The link will expire within 1 hour.</source>
1596 <trans-unit id="4424964105331349857" datatype="html"> 1614 <trans-unit id="4424964105331349857" datatype="html">
1597 <source>I'm a teapot</source> 1615 <source>I'm a teapot</source>
1598 <target state="translated">Je suis une théière</target> 1616 <target state="translated">Je suis une théière</target>
1599 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1617
1600 </trans-unit> 1618 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1601 <trans-unit id="1597262876035959248" datatype="html"> 1619 <trans-unit id="1597262876035959248" datatype="html">
1602 <source>That's an error.</source> 1620 <source>That's an error.</source>
1603 <target state="translated">C'est une erreur.</target> 1621 <target state="translated">C'est une erreur.</target>
@@ -1690,8 +1708,8 @@ The link will expire within 1 hour.</source>
1690 <trans-unit id="2971365540217107489" datatype="html"> 1708 <trans-unit id="2971365540217107489" datatype="html">
1691 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1709 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1692 <target state="translated">Ce média est trop gros pour le serveur. Merci de contacter votre administrateur pour augmenter cette limite.</target> 1710 <target state="translated">Ce média est trop gros pour le serveur. Merci de contacter votre administrateur pour augmenter cette limite.</target>
1693 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1711
1694 </trans-unit> 1712 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1695 <trans-unit id="5131854469652959713" datatype="html"> 1713 <trans-unit id="5131854469652959713" datatype="html">
1696 <source>GLOBAL SEARCH</source> 1714 <source>GLOBAL SEARCH</source>
1697 <target state="translated">RECHERCHE GLOBALE</target> 1715 <target state="translated">RECHERCHE GLOBALE</target>
@@ -2433,8 +2451,8 @@ The link will expire within 1 hour.</source>
2433 <trans-unit id="6161604372916832458" datatype="html"> 2451 <trans-unit id="6161604372916832458" datatype="html">
2434 <source>Upload on hold</source> 2452 <source>Upload on hold</source>
2435 <target state="translated">Téléversement en attente</target> 2453 <target state="translated">Téléversement en attente</target>
2436 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2454
2437 </trans-unit> 2455 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2438 <trans-unit id="285180972645018518" datatype="html"> 2456 <trans-unit id="285180972645018518" datatype="html">
2439 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2457 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2440 <target state="translated">Désolé, la fonction de téléchargement est désactivée pour votre compte. Si vous souhaitez ajouter des vidéos, un administrateur doit débloquer votre quota.</target> 2458 <target state="translated">Désolé, la fonction de téléchargement est désactivée pour votre compte. Si vous souhaitez ajouter des vidéos, un administrateur doit débloquer votre quota.</target>
@@ -3120,11 +3138,7 @@ The link will expire within 1 hour.</source>
3120 <target>ID</target> 3138 <target>ID</target>
3121 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3139 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3122 </trans-unit> 3140 </trans-unit>
3123 <trans-unit id="2265605798180116441"> 3141
3124 <source>Follower handle</source>
3125 <target>Identifiant d'abonné·e</target>
3126 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3127 </trans-unit>
3128 <trans-unit id="5911214550882917183"> 3142 <trans-unit id="5911214550882917183">
3129 <source>State</source> 3143 <source>State</source>
3130 <target>Statut</target> 3144 <target>Statut</target>
@@ -3192,11 +3206,7 @@ The link will expire within 1 hour.</source>
3192 </target> 3206 </target>
3193 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3207 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3194 </trans-unit> 3208 </trans-unit>
3195 <trans-unit id="6641024648411549335"> 3209
3196 <source>Host</source>
3197 <target>Hôte</target>
3198 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3199 </trans-unit>
3200 <trans-unit id="6571718060636962350" datatype="html"> 3210 <trans-unit id="6571718060636962350" datatype="html">
3201 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3211 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3202 <target state="translated">Redondance autorisée <x id="START_TAG_P-SORTICON"/><x id="CLOSE_TAG_P-SORTICON"/></target> 3212 <target state="translated">Redondance autorisée <x id="START_TAG_P-SORTICON"/><x id="CLOSE_TAG_P-SORTICON"/></target>
@@ -3205,9 +3215,9 @@ The link will expire within 1 hour.</source>
3205 <trans-unit id="9160510009013134726" datatype="html"> 3215 <trans-unit id="9160510009013134726" datatype="html">
3206 <source>Unfollow</source> 3216 <source>Unfollow</source>
3207 <target state="translated">Arrêter de suivre</target> 3217 <target state="translated">Arrêter de suivre</target>
3208 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3218
3209 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3219
3210 </trans-unit> 3220 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3211 <trans-unit id="8246779176913476983" datatype="html"> 3221 <trans-unit id="8246779176913476983" datatype="html">
3212 <source>Open instance in a new tab</source> 3222 <source>Open instance in a new tab</source>
3213 <target state="translated">Ouvrir l'instance dans une nouvelle fenêtre</target> 3223 <target state="translated">Ouvrir l'instance dans une nouvelle fenêtre</target>
@@ -3218,28 +3228,20 @@ The link will expire within 1 hour.</source>
3218 <trans-unit id="9132918641931433659" datatype="html"> 3228 <trans-unit id="9132918641931433659" datatype="html">
3219 <source>No host found matching current filters.</source> 3229 <source>No host found matching current filters.</source>
3220 <target state="translated">Impossible de trouver un hôte correspondant aux critères actuels.</target> 3230 <target state="translated">Impossible de trouver un hôte correspondant aux critères actuels.</target>
3221 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3231
3222 </trans-unit> 3232 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3223 <trans-unit id="7274241885665071790" datatype="html"> 3233 <trans-unit id="7274241885665071790" datatype="html">
3224 <source>Your instance is not following anyone.</source> 3234 <source>Your instance is not following anyone.</source>
3225 <target state="translated">Votre instance n'en suit aucune autre.</target> 3235 <target state="translated">Votre instance n'en suit aucune autre.</target>
3226 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3236
3227 </trans-unit> 3237 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3228 <trans-unit id="4774348799569692380" datatype="html"> 3238 <trans-unit id="4774348799569692380" datatype="html">
3229 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3239 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3230 <target state="translated">Affiche <x id="INTERPOLATION"/> à <x id="INTERPOLATION_1"/> sur <x id="INTERPOLATION_2"/>hôtes</target> 3240 <target state="translated">Affiche <x id="INTERPOLATION"/> à <x id="INTERPOLATION_1"/> sur <x id="INTERPOLATION_2"/>hôtes</target>
3231 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3241 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3232 </trans-unit> 3242 </trans-unit>
3233 <trans-unit id="6275803119759621687" datatype="html"> 3243
3234 <source>Follow domains</source> 3244
3235 <target state="translated">Suivre des domaines</target>
3236 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3237 </trans-unit>
3238 <trans-unit id="1268699198448750610" datatype="html">
3239 <source>Follow instances</source>
3240 <target state="translated">Suivre les instances</target>
3241 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3242 </trans-unit>
3243 <trans-unit id="9216117865911519658" datatype="html"> 3245 <trans-unit id="9216117865911519658" datatype="html">
3244 <source>Action</source> 3246 <source>Action</source>
3245 <target state="translated">Action</target> 3247 <target state="translated">Action</target>
@@ -3289,11 +3291,11 @@ The link will expire within 1 hour.</source>
3289 <trans-unit id="5248717555542428023"> 3291 <trans-unit id="5248717555542428023">
3290 <source>Username</source> 3292 <source>Username</source>
3291 <target>Identifiant</target> 3293 <target>Identifiant</target>
3292 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3294
3293 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3295
3294 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3296
3295 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3297
3296 </trans-unit> 3298 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3297 <trans-unit id="5428411040014095392" datatype="html"> 3299 <trans-unit id="5428411040014095392" datatype="html">
3298 <source>e.g. jane_doe</source> 3300 <source>e.g. jane_doe</source>
3299 <target state="translated">exemple : joel_dove</target> 3301 <target state="translated">exemple : joel_dove</target>
@@ -3321,9 +3323,9 @@ The link will expire within 1 hour.</source>
3321 <trans-unit id="4145496584631696119"> 3323 <trans-unit id="4145496584631696119">
3322 <source>Role</source> 3324 <source>Role</source>
3323 <target>Rôle</target> 3325 <target>Rôle</target>
3324 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3326
3325 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3327
3326 </trans-unit> 3328 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3327 <trans-unit id="7046347992315328430" datatype="html"> 3329 <trans-unit id="7046347992315328430" datatype="html">
3328 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3330 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3329 <target state="translated">Le transcodage est activé. Le quota de vidéos ne prend en compte que la taille du fichier <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/>.<x id="LINE_BREAK"/> L'utilisateur peut au plus téléverser ~ <x id="INTERPOLATION"/>. </target> 3331 <target state="translated">Le transcodage est activé. Le quota de vidéos ne prend en compte que la taille du fichier <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/>.<x id="LINE_BREAK"/> L'utilisateur peut au plus téléverser ~ <x id="INTERPOLATION"/>. </target>
@@ -3340,15 +3342,9 @@ The link will expire within 1 hour.</source>
3340 <trans-unit id="2622255144026150901" datatype="html"> 3342 <trans-unit id="2622255144026150901" datatype="html">
3341 <source>Auth plugin</source> 3343 <source>Auth plugin</source>
3342 <target state="translated">Plugin d'authentification</target> 3344 <target state="translated">Plugin d'authentification</target>
3343 <context-group purpose="location"> 3345
3344 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3346
3345 <context context-type="linenumber">188</context> 3347 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3346 </context-group>
3347 <context-group purpose="location">
3348 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3349 <context context-type="linenumber">188</context>
3350 </context-group>
3351 </trans-unit>
3352 <trans-unit id="588099657508661970" datatype="html"> 3348 <trans-unit id="588099657508661970" datatype="html">
3353 <source>None (local authentication)</source> 3349 <source>None (local authentication)</source>
3354 <target state="translated">Aucune (authentification locale)</target> 3350 <target state="translated">Aucune (authentification locale)</target>
@@ -3599,6 +3595,12 @@ The link will expire within 1 hour.</source>
3599 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3595 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3600 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3596 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3601 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3597 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3598 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3599 <source>Follower</source><target state="new">Follower</target>
3600 <context-group purpose="location">
3601 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3602 <context context-type="linenumber">24</context>
3603 </context-group>
3602 </trans-unit> 3604 </trans-unit>
3603 <trans-unit id="4691552465058437520" datatype="html"> 3605 <trans-unit id="4691552465058437520" datatype="html">
3604 <source>Commented video</source> 3606 <source>Commented video</source>
@@ -3900,8 +3902,8 @@ The link will expire within 1 hour.</source>
3900 <trans-unit id="4917252294930256268" datatype="html"> 3902 <trans-unit id="4917252294930256268" datatype="html">
3901 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3903 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3902 <target state="translated">Il semblerait que votre serveur n'utilise par le protocole HTTPS. Vous devez activer TLS sur votre serveur pour pouvoir en suivre d'autres.</target> 3904 <target state="translated">Il semblerait que votre serveur n'utilise par le protocole HTTPS. Vous devez activer TLS sur votre serveur pour pouvoir en suivre d'autres.</target>
3903 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 3905
3904 </trans-unit> 3906 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3905 <trans-unit id="4058814854824495833" datatype="html"> 3907 <trans-unit id="4058814854824495833" datatype="html">
3906 <source>Mute domains</source> 3908 <source>Mute domains</source>
3907 <target state="translated">Masquer des domaines</target> 3909 <target state="translated">Masquer des domaines</target>
@@ -5853,11 +5855,8 @@ color: red;
5853 <trans-unit id="5512878593724620692" datatype="html"> 5855 <trans-unit id="5512878593724620692" datatype="html">
5854 <source>CHANNELS</source> 5856 <source>CHANNELS</source>
5855 <target state="translated">CHAÎNES</target> 5857 <target state="translated">CHAÎNES</target>
5856 <context-group purpose="location"> 5858
5857 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5859 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5858 <context context-type="linenumber">82</context>
5859 </context-group>
5860 </trans-unit>
5861 <trans-unit id="3666829335406793239"> 5860 <trans-unit id="3666829335406793239">
5862 <source>This account does not have channels.</source> 5861 <source>This account does not have channels.</source>
5863 <target>Ce compte n'a pas de chaîne vidéo.</target> 5862 <target>Ce compte n'a pas de chaîne vidéo.</target>
@@ -5896,6 +5895,12 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
5896channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 5895channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
5897 <target state="translated">Voulez-vous vraiment supprimer <x id="PH" equiv-text="videoChannel.displayName"/> ? Cela supprimera <x id="PH_1" equiv-text="videoChannel.videosCount"/> les vidéos mises en ligne sur cette chaîne, et vous ne pourrez pas créer une autre chaine avec le même nom (<x id="PH_2" equiv-text="videoChannel.name"/>) !</target> 5896 <target state="translated">Voulez-vous vraiment supprimer <x id="PH" equiv-text="videoChannel.displayName"/> ? Cela supprimera <x id="PH_1" equiv-text="videoChannel.videosCount"/> les vidéos mises en ligne sur cette chaîne, et vous ne pourrez pas créer une autre chaine avec le même nom (<x id="PH_2" equiv-text="videoChannel.name"/>) !</target>
5898 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 5897 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
5898 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
5899 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5900 <context-group purpose="location">
5901 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5902 <context context-type="linenumber">48</context>
5903 </context-group>
5899 </trans-unit> 5904 </trans-unit>
5900 <trans-unit id="5387007581996837469" datatype="html"> 5905 <trans-unit id="5387007581996837469" datatype="html">
5901 <source>My Channels</source> 5906 <source>My Channels</source>
@@ -6415,13 +6420,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6415 <trans-unit id="6979021199788941693"> 6420 <trans-unit id="6979021199788941693">
6416 <source>Your message has been sent.</source> 6421 <source>Your message has been sent.</source>
6417 <target>Votre message a été envoyé.</target> 6422 <target>Votre message a été envoyé.</target>
6418 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group> 6423
6419 </trans-unit> 6424 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6420 <trans-unit id="2072135752262464360"> 6425 <trans-unit id="2072135752262464360">
6421 <source>You already sent this form recently</source> 6426 <source>You already sent this form recently</source>
6422 <target>Vous avez déjà rempli ce formulaire récemment</target> 6427 <target>Vous avez déjà rempli ce formulaire récemment</target>
6423 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group> 6428
6424 </trans-unit> 6429 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6425 <trans-unit id="819067926858619041" datatype="html"> 6430 <trans-unit id="819067926858619041" datatype="html">
6426 <source>Account videos</source> 6431 <source>Account videos</source>
6427 <target state="translated">Vidéos du compte</target> 6432 <target state="translated">Vidéos du compte</target>
@@ -6466,13 +6471,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6466 <target state="translated"> 6471 <target state="translated">
6467 <x id="PH"/> comptes abonnés 6472 <x id="PH"/> comptes abonnés
6468 </target> 6473 </target>
6469 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6474
6470 </trans-unit> 6475 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6471 <trans-unit id="6250999352462648289" datatype="html"> 6476 <trans-unit id="6250999352462648289" datatype="html">
6472 <source>Report this account</source> 6477 <source>Report this account</source>
6473 <target state="translated">Signaler ce compte</target> 6478 <target state="translated">Signaler ce compte</target>
6474 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6479
6475 </trans-unit> 6480 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6476 <trans-unit id="1504521795586863905" datatype="html"> 6481 <trans-unit id="1504521795586863905" datatype="html">
6477 <source>VIDEOS</source> 6482 <source>VIDEOS</source>
6478 <target state="translated">VIDÉOS</target> 6483 <target state="translated">VIDÉOS</target>
@@ -6482,31 +6487,21 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6482 <trans-unit id="25349740244798533"> 6487 <trans-unit id="25349740244798533">
6483 <source>Username copied</source> 6488 <source>Username copied</source>
6484 <target>Nom d'utilisateur·rice copié</target> 6489 <target>Nom d'utilisateur·rice copié</target>
6485 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6490
6486 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6491
6487 </trans-unit> 6492 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6488 <trans-unit id="9221735175659318025" datatype="html"> 6493 <trans-unit id="9221735175659318025" datatype="html">
6489 <source>1 subscriber</source> 6494 <source>1 subscriber</source>
6490 <target state="translated">1 abonné</target> 6495 <target state="translated">1 abonné</target>
6491 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6496
6492 </trans-unit> 6497 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6493 <trans-unit id="4097331874769079975" datatype="html"> 6498 <trans-unit id="4097331874769079975" datatype="html">
6494 <source><x id="PH"/> subscribers</source> 6499 <source><x id="PH"/> subscribers</source>
6495 <target state="translated"><x id="PH"/> abonnés</target> 6500 <target state="translated"><x id="PH"/> abonnés</target>
6496 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6501
6497 </trans-unit> 6502 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6498 <trans-unit id="4682675125751819107" datatype="html"> 6503
6499 <source>Instances you follow</source> 6504
6500 <target state="translated">Les instances que vous suivez</target>
6501 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6502 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6503 </trans-unit>
6504 <trans-unit id="8899833753704589712" datatype="html">
6505 <source>Instances following you</source>
6506 <target state="translated">Les instances qui vous suivent</target>
6507 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6508 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6509 </trans-unit>
6510 <trans-unit id="1035838766454786107" datatype="html"> 6505 <trans-unit id="1035838766454786107" datatype="html">
6511 <source>Audio-only</source> 6506 <source>Audio-only</source>
6512 <target state="translated">Audio seulement</target> 6507 <target state="translated">Audio seulement</target>
@@ -6556,6 +6551,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6556 <source>Auto (via ffmpeg)</source> 6551 <source>Auto (via ffmpeg)</source>
6557 <target>Auto (avec ffmpeg)</target> 6552 <target>Auto (avec ffmpeg)</target>
6558 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6553 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6554 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6555 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6556 <context-group purpose="location">
6557 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6558 <context context-type="linenumber">3</context>
6559 </context-group>
6559 </trans-unit> 6560 </trans-unit>
6560 <trans-unit id="931255636742351800" datatype="html"> 6561 <trans-unit id="931255636742351800" datatype="html">
6561 <source>No limit</source> 6562 <source>No limit</source>
@@ -6704,18 +6705,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6704 <trans-unit id="2127446333083057097" datatype="html"> 6705 <trans-unit id="2127446333083057097" datatype="html">
6705 <source>Domain is required.</source> 6706 <source>Domain is required.</source>
6706 <target state="translated">Un domaine est requis.</target> 6707 <target state="translated">Un domaine est requis.</target>
6707 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6708
6708 </trans-unit> 6709 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6709 <trans-unit id="6780793142903080663" datatype="html"> 6710 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6710 <source>Domains entered are invalid.</source> 6711 <context-group purpose="location">
6711 <target state="translated">Les domaines renseignés sont invalides.</target> 6712 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6712 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6713 <context context-type="linenumber">93</context>
6713 </trans-unit> 6714 </context-group>
6714 <trans-unit id="5886492514458202177" datatype="html"> 6715 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6715 <source>Domains entered contain duplicates.</source> 6716 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6716 <target state="translated">Les domaines renseignés contiennes des doublons.</target> 6717 <context-group purpose="location">
6717 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6718 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6719 <context context-type="linenumber">94</context>
6720 </context-group>
6721 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6722 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6723 <context-group purpose="location">
6724 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6725 <context context-type="linenumber">102</context>
6726 </context-group>
6727 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6728 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6729 <context-group purpose="location">
6730 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6731 <context context-type="linenumber">103</context>
6732 </context-group>
6718 </trans-unit> 6733 </trans-unit>
6734
6735
6719 <trans-unit id="240806681889331244"> 6736 <trans-unit id="240806681889331244">
6720 <source>Unlimited</source> 6737 <source>Unlimited</source>
6721 <target>Illimité</target> 6738 <target>Illimité</target>
@@ -6883,24 +6900,50 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6883 <x id="PH"/> supprimé des abonné·e·s de votre instance 6900 <x id="PH"/> supprimé des abonné·e·s de votre instance
6884 </target> 6901 </target>
6885 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6902 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6903 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6904 <source>Follow</source><target state="new">Follow</target>
6905 <context-group purpose="location">
6906 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6907 <context context-type="linenumber">3</context>
6908 </context-group>
6909 <context-group purpose="location">
6910 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6911 <context context-type="linenumber">37</context>
6912 </context-group>
6913 <context-group purpose="location">
6914 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6915 <context context-type="linenumber">18</context>
6916 </context-group>
6917 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6918 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6919 <context-group purpose="location">
6920 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6921 <context context-type="linenumber">11</context>
6922 </context-group>
6886 </trans-unit> 6923 </trans-unit>
6887 <trans-unit id="2740793005745065895"> 6924 <trans-unit id="2740793005745065895">
6888 <source><x id="PH"/> is not valid </source> 6925 <source><x id="PH"/> is not valid </source>
6889 <target> 6926 <target>
6890 <x id="PH"/> n'est pas valide 6927 <x id="PH"/> n'est pas valide
6891 </target> 6928 </target>
6892 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6929
6893 </trans-unit> 6930 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6894 <trans-unit id="2355066641781598196"> 6931 <trans-unit id="2355066641781598196">
6895 <source>Follow request(s) sent!</source> 6932 <source>Follow request(s) sent!</source>
6896 <target>Requête·s envoyée·s !</target> 6933 <target>Requête·s envoyée·s !</target>
6897 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6934
6935 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6936 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6937 <context-group purpose="location">
6938 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6939 <context context-type="linenumber">3</context>
6940 </context-group>
6898 </trans-unit> 6941 </trans-unit>
6899 <trans-unit id="4245720728052819482"> 6942 <trans-unit id="4245720728052819482">
6900 <source>Do you really want to unfollow <x id="PH"/>?</source> 6943 <source>Do you really want to unfollow <x id="PH"/>?</source>
6901 <target>Voulez-vous vraiment vous désabonner de <x id="PH"/> ?</target> 6944 <target>Voulez-vous vraiment vous désabonner de <x id="PH"/> ?</target>
6902 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6945
6903 </trans-unit> 6946 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6904 <trans-unit id="9160510009013134726"> 6947 <trans-unit id="9160510009013134726">
6905 <source>Unfollow</source> 6948 <source>Unfollow</source>
6906 <target>Arrêter le suivi</target> 6949 <target>Arrêter le suivi</target>
@@ -6909,8 +6952,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6909 <trans-unit id="3935234189109112926"> 6952 <trans-unit id="3935234189109112926">
6910 <source>You are not following <x id="PH"/> anymore.</source> 6953 <source>You are not following <x id="PH"/> anymore.</source>
6911 <target>Vous n'êtes plus abonné·e à <x id="PH"/>.</target> 6954 <target>Vous n'êtes plus abonné·e à <x id="PH"/>.</target>
6912 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6955
6913 </trans-unit> 6956 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6914 <trans-unit id="2593763089859685916"> 6957 <trans-unit id="2593763089859685916">
6915 <source>enabled</source> 6958 <source>enabled</source>
6916 <target>activé</target> 6959 <target>activé</target>
@@ -7382,9 +7425,9 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7382 <trans-unit id="1519954996184640001"> 7425 <trans-unit id="1519954996184640001">
7383 <source>Error</source> 7426 <source>Error</source>
7384 <target>Erreur</target> 7427 <target>Erreur</target>
7385 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7428
7386 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7429
7387 </trans-unit> 7430 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7388 <trans-unit id="5076187961693950167" datatype="html"> 7431 <trans-unit id="5076187961693950167" datatype="html">
7389 <source>Standard logs</source> 7432 <source>Standard logs</source>
7390 <target state="translated">Journaux standards</target> 7433 <target state="translated">Journaux standards</target>
@@ -7425,16 +7468,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7425 <target>Mettre à jour le mot de passe utilisateur·rice</target> 7468 <target>Mettre à jour le mot de passe utilisateur·rice</target>
7426 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7469 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7427 </trans-unit> 7470 </trans-unit>
7428 <trans-unit id="177544274549739411" datatype="html"> 7471
7429 <source>Following list</source> 7472
7430 <target state="translated">Instances suivies</target>
7431 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7432 </trans-unit>
7433 <trans-unit id="8092429110007204784" datatype="html">
7434 <source>Followers list</source>
7435 <target state="translated">Instances qui nous suivent</target>
7436 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7437 </trans-unit>
7438 <trans-unit id="780323526182667308" datatype="html"> 7473 <trans-unit id="780323526182667308" datatype="html">
7439 <source>User <x id="PH"/> updated.</source> 7474 <source>User <x id="PH"/> updated.</source>
7440 <target state="translated">Utilisateur·rice <x id="PH"/> mis·e à jour.</target> 7475 <target state="translated">Utilisateur·rice <x id="PH"/> mis·e à jour.</target>
@@ -7470,16 +7505,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7470 <target state="translated">Fédération</target> 7505 <target state="translated">Fédération</target>
7471 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7506 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7472 </trans-unit> 7507 </trans-unit>
7473 <trans-unit id="4682675125751819107" datatype="html"> 7508
7474 <source>Instances you follow</source> 7509
7475 <target state="translated">Instances que vous suivez</target>
7476 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7477 </trans-unit>
7478 <trans-unit id="8899833753704589712" datatype="html">
7479 <source>Instances following you</source>
7480 <target state="translated">Instances qui vous suivent</target>
7481 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7482 </trans-unit>
7483 <trans-unit id="3767259920053407667" datatype="html"> 7510 <trans-unit id="3767259920053407667" datatype="html">
7484 <source>Videos will be deleted, comments will be tombstoned.</source> 7511 <source>Videos will be deleted, comments will be tombstoned.</source>
7485 <target state="translated">Les vidéos seront supprimées, les commentaires seront marqués supprimés.</target> 7512 <target state="translated">Les vidéos seront supprimées, les commentaires seront marqués supprimés.</target>
@@ -7510,6 +7537,24 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7510 <target>Définir l'adresse de courriel comme vérifiée</target> 7537 <target>Définir l'adresse de courriel comme vérifiée</target>
7511 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7538 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7512 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7539 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7540 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7541 <source>Created</source><target state="new">Created</target>
7542 <context-group purpose="location">
7543 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7544 <context context-type="linenumber">115</context>
7545 </context-group>
7546 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7547 <source>Daily quota</source><target state="new">Daily quota</target>
7548 <context-group purpose="location">
7549 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7550 <context context-type="linenumber">120</context>
7551 </context-group>
7552 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7553 <source>Last login</source><target state="new">Last login</target>
7554 <context-group purpose="location">
7555 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7556 <context context-type="linenumber">122</context>
7557 </context-group>
7513 </trans-unit> 7558 </trans-unit>
7514 <trans-unit id="3403978719736970622"> 7559 <trans-unit id="3403978719736970622">
7515 <source>You cannot ban root.</source> 7560 <source>You cannot ban root.</source>
@@ -7814,13 +7859,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7814 <trans-unit id="1137937154872046253"> 7859 <trans-unit id="1137937154872046253">
7815 <source>Video channel <x id="PH"/> created.</source> 7860 <source>Video channel <x id="PH"/> created.</source>
7816 <target>Chaîne vidéo <x id="PH"/> créée.</target> 7861 <target>Chaîne vidéo <x id="PH"/> créée.</target>
7817 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 7862
7818 </trans-unit> 7863 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7819 <trans-unit id="8723777130353305761"> 7864 <trans-unit id="8723777130353305761">
7820 <source>This name already exists on this instance.</source> 7865 <source>This name already exists on this instance.</source>
7821 <target>Ce nom existe déjà sur cette instance.</target> 7866 <target>Ce nom existe déjà sur cette instance.</target>
7822 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 7867
7823 </trans-unit> 7868 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7824 <trans-unit id="7589345916094713536"> 7869 <trans-unit id="7589345916094713536">
7825 <source>Video channel <x id="PH"/> updated.</source> 7870 <source>Video channel <x id="PH"/> updated.</source>
7826 <target>Chaîne vidéo <x id="PH"/> mise à jour.</target> 7871 <target>Chaîne vidéo <x id="PH"/> mise à jour.</target>
@@ -7841,11 +7886,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7841 <target state="translated">Bannière supprimée.</target> 7886 <target state="translated">Bannière supprimée.</target>
7842 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 7887 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
7843 </trans-unit> 7888 </trans-unit>
7844 <trans-unit id="2575302837003821736"> 7889
7845 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7846 <target>Merci de saisir le nom de la chaîne vidéo ( <x id="PH"/>) pour confirmer</target>
7847 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
7848 </trans-unit>
7849 <trans-unit id="624066830180032195"> 7890 <trans-unit id="624066830180032195">
7850 <source>Video channel <x id="PH"/> deleted.</source> 7891 <source>Video channel <x id="PH"/> deleted.</source>
7851 <target>Chaîne vidéo <x id="PH"/> supprimée.</target> 7892 <target>Chaîne vidéo <x id="PH"/> supprimée.</target>
@@ -7997,6 +8038,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7997 <source>Ownership change request sent.</source> 8038 <source>Ownership change request sent.</source>
7998 <target>Requête de changement de propriété envoyée.</target> 8039 <target>Requête de changement de propriété envoyée.</target>
7999 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8040 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8041 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8042 <source>Sort by</source><target state="new">Sort by</target>
8043 <context-group purpose="location">
8044 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8045 <context context-type="linenumber">26</context>
8046 </context-group>
8000 </trans-unit> 8047 </trans-unit>
8001 <trans-unit id="3245220240937722814"> 8048 <trans-unit id="3245220240937722814">
8002 <source>My channels</source> 8049 <source>My channels</source>
@@ -8095,7 +8142,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8095 <target>S'abonner à ce compte</target> 8142 <target>S'abonner à ce compte</target>
8096 8143
8097 8144
8098 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8145 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8099 <trans-unit id="3131904093925601441" datatype="html"> 8146 <trans-unit id="3131904093925601441" datatype="html">
8100 <source>PLAYLISTS</source> 8147 <source>PLAYLISTS</source>
8101 <target state="translated">LISTES DE LECTURE</target> 8148 <target state="translated">LISTES DE LECTURE</target>
@@ -8142,34 +8189,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8142 <trans-unit id="3779524668013120370"> 8189 <trans-unit id="3779524668013120370">
8143 <source>Go to my subscriptions</source> 8190 <source>Go to my subscriptions</source>
8144 <target>Aller voir mes abonnements</target> 8191 <target>Aller voir mes abonnements</target>
8145 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8192
8146 </trans-unit> 8193 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8147 <trans-unit id="1136469849928650779"> 8194 <trans-unit id="1136469849928650779">
8148 <source>Go to my videos</source> 8195 <source>Go to my videos</source>
8149 <target>Aller voir mes vidéos</target> 8196 <target>Aller voir mes vidéos</target>
8150 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8197
8151 </trans-unit> 8198 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8152 <trans-unit id="7836683738999600376"> 8199 <trans-unit id="7836683738999600376">
8153 <source>Go to my imports</source> 8200 <source>Go to my imports</source>
8154 <target>Aller voir mes imports</target> 8201 <target>Aller voir mes imports</target>
8155 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8202
8156 </trans-unit> 8203 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8157 <trans-unit id="7511292153332773503"> 8204 <trans-unit id="7511292153332773503">
8158 <source>Go to my channels</source> 8205 <source>Go to my channels</source>
8159 <target>Aller voir mes chaînes</target> 8206 <target>Aller voir mes chaînes</target>
8160 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8207
8161 </trans-unit> 8208 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8162 <trans-unit id="2013324644839511073" datatype="html"> 8209 <trans-unit id="2013324644839511073" datatype="html">
8163 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8210 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8164Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8211Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8165 <target state="translated">Impossible de récupérer les identifiants du Client OAuth : <x id="PH" equiv-text="error.text"/>. Assurez-vous d'avoir correctement configuré PeerTube (dossier config/), en particulier la section "serveur web".</target> 8212 <target state="translated">Impossible de récupérer les identifiants du Client OAuth : <x id="PH" equiv-text="error.text"/>. Assurez-vous d'avoir correctement configuré PeerTube (dossier config/), en particulier la section "serveur web".</target>
8166 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8213
8167 </trans-unit> 8214 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8168 <trans-unit id="375263728166936544"> 8215 <trans-unit id="375263728166936544">
8169 <source>You need to reconnect.</source> 8216 <source>You need to reconnect.</source>
8170 <target>Vous devez vous reconnecter.</target> 8217 <target>Vous devez vous reconnecter.</target>
8171 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8218
8172 </trans-unit> 8219 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8173 <trans-unit id="2206638022166154361"> 8220 <trans-unit id="2206638022166154361">
8174 <source>Keyboard Shortcuts:</source> 8221 <source>Keyboard Shortcuts:</source>
8175 <target>Raccourcis clavier :</target> 8222 <target>Raccourcis clavier :</target>
@@ -8182,6 +8229,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8182 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8229 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8183 <context context-type="linenumber">98</context> 8230 <context context-type="linenumber">98</context>
8184 </context-group> 8231 </context-group>
8232 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8233 <source>In my library</source><target state="new">In my library</target>
8234 <context-group purpose="location">
8235 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8236 <context context-type="linenumber">104</context>
8237 </context-group>
8185 </trans-unit> 8238 </trans-unit>
8186 <trans-unit id="232050922346936574" datatype="html"> 8239 <trans-unit id="232050922346936574" datatype="html">
8187 <source>Trending</source> 8240 <source>Trending</source>
@@ -8210,38 +8263,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8210 <trans-unit id="1266887509445371246"> 8263 <trans-unit id="1266887509445371246">
8211 <source>Incorrect username or password.</source> 8264 <source>Incorrect username or password.</source>
8212 <target>Nom d'utilisateur ou mot de passe incorrects.</target> 8265 <target>Nom d'utilisateur ou mot de passe incorrects.</target>
8213 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8266
8214 </trans-unit> 8267 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8215 <trans-unit id="6974874606619467663" datatype="html"> 8268 <trans-unit id="6974874606619467663" datatype="html">
8216 <source>Your account is blocked.</source> 8269 <source>Your account is blocked.</source>
8217 <target state="translated">Votre compte est bloqué.</target> 8270 <target state="translated">Votre compte est bloqué.</target>
8218 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8271
8219 </trans-unit> 8272 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8220 <trans-unit id="7939914198003891823" datatype="html"> 8273 <trans-unit id="7939914198003891823" datatype="html">
8221 <source>any language</source> 8274 <source>any language</source>
8222 <target state="translated">toute langue</target> 8275 <target state="translated">toute langue</target>
8223 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8276
8224 </trans-unit> 8277 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8225 <trans-unit id="5633144232269377096" datatype="html"> 8278 <trans-unit id="5633144232269377096" datatype="html">
8226 <source>hide</source> 8279 <source>hide</source>
8227 <target state="translated">cacher</target> 8280 <target state="translated">cacher</target>
8228 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8281
8229 </trans-unit> 8282 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8230 <trans-unit id="8603861867909474404" datatype="html"> 8283 <trans-unit id="8603861867909474404" datatype="html">
8231 <source>blur</source> 8284 <source>blur</source>
8232 <target state="translated">flouter</target> 8285 <target state="translated">flouter</target>
8233 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8286
8234 </trans-unit> 8287 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8235 <trans-unit id="4534458451100881847" datatype="html"> 8288 <trans-unit id="4534458451100881847" datatype="html">
8236 <source>display</source> 8289 <source>display</source>
8237 <target state="translated">afficher</target> 8290 <target state="translated">afficher</target>
8238 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8291
8239 </trans-unit> 8292 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8240 <trans-unit id="4467323362722952678" datatype="html"> 8293 <trans-unit id="4467323362722952678" datatype="html">
8241 <source>Unknown</source> 8294 <source>Unknown</source>
8242 <target state="translated">Inconnu</target> 8295 <target state="translated">Inconnu</target>
8243 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8296
8244 </trans-unit> 8297 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8245 <trans-unit id="8781423666414310853"> 8298 <trans-unit id="8781423666414310853">
8246 <source>Your password has been successfully reset!</source> 8299 <source>Your password has been successfully reset!</source>
8247 <target>Votre mot de passe a été réinitialisé avec succès !</target> 8300 <target>Votre mot de passe a été réinitialisé avec succès !</target>
@@ -9825,18 +9878,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9825 <trans-unit id="968295009933361070"> 9878 <trans-unit id="968295009933361070">
9826 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9879 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9827 <target>Trop de tentatives, merci de réessayer dans <x id="PH"/> minutes.</target> 9880 <target>Trop de tentatives, merci de réessayer dans <x id="PH"/> minutes.</target>
9828 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9881
9829 </trans-unit> 9882 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9830 <trans-unit id="4965472196059235310"> 9883 <trans-unit id="4965472196059235310">
9831 <source>Too many attempts, please try again later.</source> 9884 <source>Too many attempts, please try again later.</source>
9832 <target>Trop d'essais. Merci de réessayer plus tard.</target> 9885 <target>Trop d'essais. Merci de réessayer plus tard.</target>
9833 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9886
9834 </trans-unit> 9887 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9835 <trans-unit id="1693549688987384699"> 9888 <trans-unit id="1693549688987384699">
9836 <source>Server error. Please retry later.</source> 9889 <source>Server error. Please retry later.</source>
9837 <target>Le serveur rencontre une erreur. Merci de réessayer plus tard.</target> 9890 <target>Le serveur rencontre une erreur. Merci de réessayer plus tard.</target>
9838 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9891
9839 </trans-unit> 9892 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9840 <trans-unit id="5927402622550505067" datatype="html"> 9893 <trans-unit id="5927402622550505067" datatype="html">
9841 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9894 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9842 <target state="translated">Abonné·e à toutes les chaînes actuelles de <x id="PH"/>. Vous serez avertis·es de toutes leurs nouvelles vidéos.</target> 9895 <target state="translated">Abonné·e à toutes les chaînes actuelles de <x id="PH"/>. Vous serez avertis·es de toutes leurs nouvelles vidéos.</target>
@@ -10419,35 +10472,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10419 <trans-unit id="3284171506518522275"> 10472 <trans-unit id="3284171506518522275">
10420 <source>Your video was uploaded to your account and is private.</source> 10473 <source>Your video was uploaded to your account and is private.</source>
10421 <target>Votre vidéo a été téléversée sur votre compte et elle est privée.</target> 10474 <target>Votre vidéo a été téléversée sur votre compte et elle est privée.</target>
10422 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10475
10423 </trans-unit> 10476 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10424 <trans-unit id="5699822024600815733"> 10477 <trans-unit id="5699822024600815733">
10425 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10478 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10426 <target>Les données associées (étiquettes, description, etc.) seront par contre perdues ; êtes-vous sûr·e de vouloir quitter cette page ?</target> 10479 <target>Les données associées (étiquettes, description, etc.) seront par contre perdues ; êtes-vous sûr·e de vouloir quitter cette page ?</target>
10427 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10480
10428 </trans-unit> 10481 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10429 <trans-unit id="1219739004043110649"> 10482 <trans-unit id="1219739004043110649">
10430 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10483 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10431 <target>Votre vidéo n'est pas encore téléversé ; êtes-vous sûr·e de vouloir quitter cette page ?</target> 10484 <target>Votre vidéo n'est pas encore téléversé ; êtes-vous sûr·e de vouloir quitter cette page ?</target>
10432 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10485
10433 </trans-unit> 10486 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10434 <trans-unit id="6932865105766151309" datatype="html"> 10487 <trans-unit id="6932865105766151309" datatype="html">
10435 <source>Upload</source> 10488 <source>Upload</source>
10436 <target state="translated">Mise en ligne</target> 10489 <target state="translated">Mise en ligne</target>
10437 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10490
10438 </trans-unit> 10491 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10439 <trans-unit id="8278735427925094503"> 10492 <trans-unit id="8278735427925094503">
10440 <source>Upload <x id="PH"/> </source> 10493 <source>Upload <x id="PH"/> </source>
10441 <target>Téléverser 10494 <target>Téléverser
10442 <x id="PH"/> 10495 <x id="PH"/>
10443 </target> 10496 </target>
10444 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10497
10445 </trans-unit> 10498 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10446 <trans-unit id="5981816353437801748"> 10499 <trans-unit id="5981816353437801748">
10447 <source>Video published.</source> 10500 <source>Video published.</source>
10448 <target>Vidéo publiée.</target> 10501 <target>Vidéo publiée.</target>
10449 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10502
10450 </trans-unit> 10503 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10451 <trans-unit id="764164089183618119"> 10504 <trans-unit id="764164089183618119">
10452 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10505 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10453 <target>Vous n'avez pas sauvegardé vos modifications ! Si vous quittez la page, vous les perdrez.</target> 10506 <target>Vous n'avez pas sauvegardé vos modifications ! Si vous quittez la page, vous les perdrez.</target>
@@ -10494,28 +10547,28 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10494 <trans-unit id="961774488937452220" datatype="html"> 10547 <trans-unit id="961774488937452220" datatype="html">
10495 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10548 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10496 <target state="translated">Cette vidéo n'est pas disponible sur cette instance ? Voulez-vous être redirigé sur l'instance d'origine : &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a> ?</target> 10549 <target state="translated">Cette vidéo n'est pas disponible sur cette instance ? Voulez-vous être redirigé sur l'instance d'origine : &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a> ?</target>
10497 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group> 10550
10498 </trans-unit> 10551 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10499 <trans-unit id="5761611056224181752" datatype="html"> 10552 <trans-unit id="5761611056224181752" datatype="html">
10500 <source>Redirection</source> 10553 <source>Redirection</source>
10501 <target state="translated">Redirection</target> 10554 <target state="translated">Redirection</target>
10502 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group> 10555
10503 </trans-unit> 10556 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10504 <trans-unit id="8858527736400081688"> 10557 <trans-unit id="8858527736400081688">
10505 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10558 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10506 <target>Cette vidéo contient du contenu sensible. Êtes-vous sûr·e de vouloir la regarder ?</target> 10559 <target>Cette vidéo contient du contenu sensible. Êtes-vous sûr·e de vouloir la regarder ?</target>
10507 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group> 10560
10508 </trans-unit> 10561 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10509 <trans-unit id="3937119019020041049"> 10562 <trans-unit id="3937119019020041049">
10510 <source>Mature or explicit content</source> 10563 <source>Mature or explicit content</source>
10511 <target>Contenu explicite ou sensible</target> 10564 <target>Contenu explicite ou sensible</target>
10512 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group> 10565
10513 </trans-unit> 10566 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10514 <trans-unit id="1755474755114288376" datatype="html"> 10567 <trans-unit id="1755474755114288376" datatype="html">
10515 <source>Up Next</source> 10568 <source>Up Next</source>
10516 <target state="translated">Suivant</target> 10569 <target state="translated">Suivant</target>
10517 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group> 10570
10518 </trans-unit> 10571 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10519 <trans-unit id="2159130950882492111" datatype="html"> 10572 <trans-unit id="2159130950882492111" datatype="html">
10520 <source>Cancel</source> 10573 <source>Cancel</source>
10521 <target state="translated">Annuler</target> 10574 <target state="translated">Annuler</target>
@@ -10524,63 +10577,63 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10524 <trans-unit id="3354816756665089864" datatype="html"> 10577 <trans-unit id="3354816756665089864" datatype="html">
10525 <source>Autoplay is suspended</source> 10578 <source>Autoplay is suspended</source>
10526 <target state="translated">La lecture automatique est suspendue</target> 10579 <target state="translated">La lecture automatique est suspendue</target>
10527 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group> 10580
10528 </trans-unit> 10581 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10529 <trans-unit id="7895294730547405228" datatype="html"> 10582 <trans-unit id="7895294730547405228" datatype="html">
10530 <source>Enter/exit fullscreen (requires player focus)</source> 10583 <source>Enter/exit fullscreen (requires player focus)</source>
10531 <target state="translated">Entrer/sortir du mode plein écran (nécessite le focus sur le lecteur)</target> 10584 <target state="translated">Entrer/sortir du mode plein écran (nécessite le focus sur le lecteur)</target>
10532 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group> 10585
10533 </trans-unit> 10586 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10534 <trans-unit id="7618388257165864759" datatype="html"> 10587 <trans-unit id="7618388257165864759" datatype="html">
10535 <source>Play/Pause the video (requires player focus)</source> 10588 <source>Play/Pause the video (requires player focus)</source>
10536 <target state="translated">Lecture/Pause de la vidéo (nécessite le focus sur le lecteur)</target> 10589 <target state="translated">Lecture/Pause de la vidéo (nécessite le focus sur le lecteur)</target>
10537 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group> 10590
10538 </trans-unit> 10591 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10539 <trans-unit id="7761890399634216630" datatype="html"> 10592 <trans-unit id="7761890399634216630" datatype="html">
10540 <source>Mute/unmute the video (requires player focus)</source> 10593 <source>Mute/unmute the video (requires player focus)</source>
10541 <target state="translated">Désactiver/Activer le son de la vidéo (nécessite le focus sur le lecteur)</target> 10594 <target state="translated">Désactiver/Activer le son de la vidéo (nécessite le focus sur le lecteur)</target>
10542 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group> 10595
10543 </trans-unit> 10596 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10544 <trans-unit id="5996585232248234904" datatype="html"> 10597 <trans-unit id="5996585232248234904" datatype="html">
10545 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10598 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10546 <target state="translated">Sauter à un pourcentage de la vidéo : 0 est 0 % et 9 est 90 % (nécessite le focus sur le lecteur)</target> 10599 <target state="translated">Sauter à un pourcentage de la vidéo : 0 est 0 % et 9 est 90 % (nécessite le focus sur le lecteur)</target>
10547 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group> 10600
10548 </trans-unit> 10601 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10549 <trans-unit id="3748765405903319998" datatype="html"> 10602 <trans-unit id="3748765405903319998" datatype="html">
10550 <source>Increase the volume (requires player focus)</source> 10603 <source>Increase the volume (requires player focus)</source>
10551 <target state="translated">Augmenter le volume (nécessite le focus sur le lecteur)</target> 10604 <target state="translated">Augmenter le volume (nécessite le focus sur le lecteur)</target>
10552 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group> 10605
10553 </trans-unit> 10606 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10554 <trans-unit id="5810704036407159982" datatype="html"> 10607 <trans-unit id="5810704036407159982" datatype="html">
10555 <source>Decrease the volume (requires player focus)</source> 10608 <source>Decrease the volume (requires player focus)</source>
10556 <target state="translated">Diminuer le volume (nécessite le focus sur le lecteur)</target> 10609 <target state="translated">Diminuer le volume (nécessite le focus sur le lecteur)</target>
10557 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group> 10610
10558 </trans-unit> 10611 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10559 <trans-unit id="2622048822548065691" datatype="html"> 10612 <trans-unit id="2622048822548065691" datatype="html">
10560 <source>Seek the video forward (requires player focus)</source> 10613 <source>Seek the video forward (requires player focus)</source>
10561 <target state="translated">Avancer la vidéo (nécessite le focus sur le lecteur)</target> 10614 <target state="translated">Avancer la vidéo (nécessite le focus sur le lecteur)</target>
10562 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group> 10615
10563 </trans-unit> 10616 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10564 <trans-unit id="6540078205109221153" datatype="html"> 10617 <trans-unit id="6540078205109221153" datatype="html">
10565 <source>Seek the video backward (requires player focus)</source> 10618 <source>Seek the video backward (requires player focus)</source>
10566 <target state="translated">Reculer la vidéo (nécessite le focus sur le lecteur)</target> 10619 <target state="translated">Reculer la vidéo (nécessite le focus sur le lecteur)</target>
10567 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group> 10620
10568 </trans-unit> 10621 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10569 <trans-unit id="1956491957766210808" datatype="html"> 10622 <trans-unit id="1956491957766210808" datatype="html">
10570 <source>Increase playback rate (requires player focus)</source> 10623 <source>Increase playback rate (requires player focus)</source>
10571 <target state="translated">Augmenter la vitesse de lecture (nécessite le focus sur le lecteur)</target> 10624 <target state="translated">Augmenter la vitesse de lecture (nécessite le focus sur le lecteur)</target>
10572 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group> 10625
10573 </trans-unit> 10626 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10574 <trans-unit id="5495529997674803186" datatype="html"> 10627 <trans-unit id="5495529997674803186" datatype="html">
10575 <source>Decrease playback rate (requires player focus)</source> 10628 <source>Decrease playback rate (requires player focus)</source>
10576 <target state="translated">Diminuer la vitesse de lecture (nécessite le focus sur le lecteur)</target> 10629 <target state="translated">Diminuer la vitesse de lecture (nécessite le focus sur le lecteur)</target>
10577 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group> 10630
10578 </trans-unit> 10631 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10579 <trans-unit id="3178343147230721210" datatype="html"> 10632 <trans-unit id="3178343147230721210" datatype="html">
10580 <source>Navigate in the video frame by frame (requires player focus)</source> 10633 <source>Navigate in the video frame by frame (requires player focus)</source>
10581 <target state="translated">Naviguer dans la vidéo image par image (nécessite le focus sur le lecteur)</target> 10634 <target state="translated">Naviguer dans la vidéo image par image (nécessite le focus sur le lecteur)</target>
10582 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group> 10635
10583 </trans-unit> 10636 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10584 <trans-unit id="8025996572234182184"> 10637 <trans-unit id="8025996572234182184">
10585 <source>Like the video</source> 10638 <source>Like the video</source>
10586 <target>J’aime cette vidéo</target> 10639 <target>J’aime cette vidéo</target>
diff --git a/client/src/locale/angular.gd.xlf b/client/src/locale/angular.gd.xlf
index 2f5aee9c3..671c35c0e 100644
--- a/client/src/locale/angular.gd.xlf
+++ b/client/src/locale/angular.gd.xlf
@@ -159,19 +159,19 @@
159 <trans-unit id="187187500641108332" datatype="html"> 159 <trans-unit id="187187500641108332" datatype="html">
160 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source> 160 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source>
161 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target> 161 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target>
162 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group> 162
163 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group> 163
164 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group> 164
165 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group> 165
166 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group> 166
167 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 167
168 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group> 168
169 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group> 169
170 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group> 170
171 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group> 171
172 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group> 172
173 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group> 173
174 </trans-unit> 174 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
175 <trans-unit id="1486537403020619891" datatype="html"> 175 <trans-unit id="1486537403020619891" datatype="html">
176 <source>My watch history</source> 176 <source>My watch history</source>
177 <target state="translated">Eachdraidh dhe na choimhead mi air</target> 177 <target state="translated">Eachdraidh dhe na choimhead mi air</target>
@@ -240,22 +240,22 @@
240 <trans-unit id="5674286808255988565" datatype="html"> 240 <trans-unit id="5674286808255988565" datatype="html">
241 <source>Create</source> 241 <source>Create</source>
242 <target state="translated">Cruthaich</target> 242 <target state="translated">Cruthaich</target>
243 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 243
244 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 244
245 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 245
246 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 246
247 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 247
248 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 248
249 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 249
250 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 250
251 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 251
252 </trans-unit> 252 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
253 <trans-unit id="1006562256968398209" datatype="html"> 253 <trans-unit id="1006562256968398209" datatype="html">
254 <source>video</source> 254 <source>video</source>
255 <target state="translated">video</target> 255 <target state="translated">video</target>
256 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group> 256
257 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group> 257
258 </trans-unit> 258 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
259 <trans-unit id="6438815964972582865" datatype="html"> 259 <trans-unit id="6438815964972582865" datatype="html">
260 <source>The following link contains a private token and should not be shared with anyone.</source> 260 <source>The following link contains a private token and should not be shared with anyone.</source>
261 <target state="translated">Tha tòcan prìobhaideach am broinn a’ cheangail a leanas agus cha bu chòir dhut a cho-roinneadh le duine sam bith.</target> 261 <target state="translated">Tha tòcan prìobhaideach am broinn a’ cheangail a leanas agus cha bu chòir dhut a cho-roinneadh le duine sam bith.</target>
@@ -325,13 +325,13 @@
325 <trans-unit id="6995024616159044376" datatype="html"> 325 <trans-unit id="6995024616159044376" datatype="html">
326 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 326 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
327 <target state="translated">Chaidh thu thar cuota nam videothan agad leis a’ video seo (meud a’ video: <x id="PH" equiv-text="videoSizeBytes"/>, ’ga chleachdadh: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, cuota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 327 <target state="translated">Chaidh thu thar cuota nam videothan agad leis a’ video seo (meud a’ video: <x id="PH" equiv-text="videoSizeBytes"/>, ’ga chleachdadh: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, cuota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
328 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 328
329 </trans-unit> 329 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
330 <trans-unit id="7873395933409147217" datatype="html"> 330 <trans-unit id="7873395933409147217" datatype="html">
331 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 331 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
332 <target state="translated">Bheir a’ video seo thar cuota làitheil nam videothan agad thu (meud a’ video: <x id="PH" equiv-text="videoSizeBytes"/>, ’ga chleachdadh: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, cuota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 332 <target state="translated">Bheir a’ video seo thar cuota làitheil nam videothan agad thu (meud a’ video: <x id="PH" equiv-text="videoSizeBytes"/>, ’ga chleachdadh: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, cuota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
333 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 333
334 </trans-unit> 334 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
335 <trans-unit id="5235042777215655908" datatype="html"> 335 <trans-unit id="5235042777215655908" datatype="html">
336 <source>subtitles</source> 336 <source>subtitles</source>
337 <target state="translated">fo-thiotalan</target> 337 <target state="translated">fo-thiotalan</target>
@@ -765,10 +765,10 @@
765 <trans-unit id="2602586221576511475" datatype="html"> 765 <trans-unit id="2602586221576511475" datatype="html">
766 <source>Video quota</source> 766 <source>Video quota</source>
767 <target state="translated">Cuota de videothan</target> 767 <target state="translated">Cuota de videothan</target>
768 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 768
769 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 769
770 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 770
771 </trans-unit> 771 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
772 <trans-unit id="1502595455339510144" datatype="html"> 772 <trans-unit id="1502595455339510144" datatype="html">
773 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 773 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
774 <target state="translated">Gun chuingeachadh <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> gach latha)<x id="CLOSE_TAG_NG_CONTAINER"/></target> 774 <target state="translated">Gun chuingeachadh <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> gach latha)<x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -848,6 +848,30 @@
848 <target state="translated">Co-nasgadh</target> 848 <target state="translated">Co-nasgadh</target>
849 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 849 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
850 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 850 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
851 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
852 <source>Following</source><target state="new">Following</target>
853 <context-group purpose="location">
854 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
855 <context context-type="linenumber">29</context>
856 </context-group>
857 <context-group purpose="location">
858 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
859 <context context-type="linenumber">31</context>
860 </context-group>
861 <context-group purpose="location">
862 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
863 <context context-type="linenumber">28</context>
864 </context-group>
865 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
866 <source>Followers</source><target state="new">Followers</target>
867 <context-group purpose="location">
868 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
869 <context context-type="linenumber">34</context>
870 </context-group>
871 <context-group purpose="location">
872 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
873 <context context-type="linenumber">37</context>
874 </context-group>
851 </trans-unit> 875 </trans-unit>
852 <trans-unit id="3541687134897970106" datatype="html"> 876 <trans-unit id="3541687134897970106" datatype="html">
853 <source>followers</source> 877 <source>followers</source>
@@ -909,25 +933,25 @@
909 <trans-unit id="2159130950882492111" datatype="html"> 933 <trans-unit id="2159130950882492111" datatype="html">
910 <source>Cancel</source> 934 <source>Cancel</source>
911 <target state="translated">Sguir dheth</target> 935 <target state="translated">Sguir dheth</target>
912 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group> 936
913 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group> 937
914 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group> 938
915 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group> 939
916 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group> 940
917 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group> 941
918 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group> 942
919 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group> 943
920 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group> 944
921 <context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group> 945
922 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group> 946
923 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group> 947
924 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 948
925 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 949
926 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group> 950
927 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group> 951
928 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group> 952
929 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group> 953
930 </trans-unit> 954 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
931 <trans-unit id="3616223838716839702" datatype="html"> 955 <trans-unit id="3616223838716839702" datatype="html">
932 <source>Ban this user</source> 956 <source>Ban this user</source>
933 <target state="translated">Toirmisg an cleachdaiche seo</target> 957 <target state="translated">Toirmisg an cleachdaiche seo</target>
@@ -991,19 +1015,13 @@
991 <trans-unit id="7252854992688790751" datatype="html"> 1015 <trans-unit id="7252854992688790751" datatype="html">
992 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1016 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
993 <target state="translated">Faodaidh tu clàradh air an ionstans seo.Gidheadh, dèan cinnteach gun leugh thu na <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Teirmichean<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Teirmichean<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> mus cruthaich thu cunntas. ’S urrainn dhut cuideachd ionstans eile a lorg a fhreagras ris na feumalachdan agad-sa: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1017 <target state="translated">Faodaidh tu clàradh air an ionstans seo.Gidheadh, dèan cinnteach gun leugh thu na <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Teirmichean<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Teirmichean<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> mus cruthaich thu cunntas. ’S urrainn dhut cuideachd ionstans eile a lorg a fhreagras ris na feumalachdan agad-sa: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
994 <context-group purpose="location"> 1018
995 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1019 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
996 <context context-type="linenumber">60,62</context>
997 </context-group>
998 </trans-unit>
999 <trans-unit id="7215649348148521605" datatype="html"> 1020 <trans-unit id="7215649348148521605" datatype="html">
1000 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1021 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1001 <target state="translated">Cha cheadaich an t-ionstans seo gun clàraich cleachdaichean ùra aig an àm seo, ’s urrainn dhut sùil a thoirt air na <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Teirmichean<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> airson barrachd fiosrachaidh no ionstans a lorg a bheir comas clàraidh cunntais dhut agus na videothan agad a luchdadh suas an-siud. Lorg an t-ionstans agad fhèin am measg càich: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1022 <target state="translated">Cha cheadaich an t-ionstans seo gun clàraich cleachdaichean ùra aig an àm seo, ’s urrainn dhut sùil a thoirt air na <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Teirmichean<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> airson barrachd fiosrachaidh no ionstans a lorg a bheir comas clàraidh cunntais dhut agus na videothan agad a luchdadh suas an-siud. Lorg an t-ionstans agad fhèin am measg càich: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1002 <context-group purpose="location"> 1023
1003 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1024 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1004 <context context-type="linenumber">65,67</context>
1005 </context-group>
1006 </trans-unit>
1007 <trans-unit id="2392488717875840729" datatype="html"> 1025 <trans-unit id="2392488717875840729" datatype="html">
1008 <source>User</source> 1026 <source>User</source>
1009 <target state="translated">Cleachdaiche</target> 1027 <target state="translated">Cleachdaiche</target>
@@ -1014,67 +1032,67 @@
1014 <source>Username or email address</source> 1032 <source>Username or email address</source>
1015 <target state="translated">Ainm-cleachdaiche no seòladh puist-d</target> 1033 <target state="translated">Ainm-cleachdaiche no seòladh puist-d</target>
1016 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1034 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1035 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1036 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1037 <context-group purpose="location">
1038 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1039 <context context-type="linenumber">33,34</context>
1040 </context-group>
1017 </trans-unit> 1041 </trans-unit>
1018 <trans-unit id="1431416938026210429" datatype="html"> 1042 <trans-unit id="1431416938026210429" datatype="html">
1019 <source>Password</source> 1043 <source>Password</source>
1020 <target state="translated">Facal-faire</target> 1044 <target state="translated">Facal-faire</target>
1021 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1045
1022 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1046
1023 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1047
1024 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1048
1025 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1049
1026 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1050
1027 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1051
1028 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1052
1029 </trans-unit> 1053 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1030 <trans-unit id="8715156686857791956" datatype="html"> 1054 <trans-unit id="8715156686857791956" datatype="html">
1031 <source>Click here to reset your password</source> 1055 <source>Click here to reset your password</source>
1032 <target state="translated">Briog an-seo airson am facal-faire agad ath-shuidheachadh</target> 1056 <target state="translated">Briog an-seo airson am facal-faire agad ath-shuidheachadh</target>
1033 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1057
1034 </trans-unit> 1058 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1035 <trans-unit id="892063502898494584" datatype="html"> 1059 <trans-unit id="892063502898494584" datatype="html">
1036 <source>I forgot my password</source> 1060 <source>I forgot my password</source>
1037 <target state="translated">Dìochuimhnich mi am facal-faire agam</target> 1061 <target state="translated">Dìochuimhnich mi am facal-faire agam</target>
1038 <context-group purpose="location"> 1062
1039 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1063 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1040 <context context-type="linenumber">47</context>
1041 </context-group>
1042 </trans-unit>
1043 <trans-unit id="2101170466365500913" datatype="html"> 1064 <trans-unit id="2101170466365500913" datatype="html">
1044 <source>Logging into an account lets you publish content</source> 1065 <source>Logging into an account lets you publish content</source>
1045 <target state="translated">Ma nì thu clàradh a-steach air cunntas, ’s urrainn dhut susbaint fhoillseachadh</target> 1066 <target state="translated">Ma nì thu clàradh a-steach air cunntas, ’s urrainn dhut susbaint fhoillseachadh</target>
1046 <context-group purpose="location"> 1067
1047 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1068 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1048 <context context-type="linenumber">56,57</context>
1049 </context-group>
1050 </trans-unit>
1051 <trans-unit id="2454050363478003966" datatype="html"> 1069 <trans-unit id="2454050363478003966" datatype="html">
1052 <source>Login</source> 1070 <source>Login</source>
1053 <target state="translated">Clàraich a-steach</target> 1071 <target state="translated">Clàraich a-steach</target>
1054 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1072
1055 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1073
1056 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1074
1057 </trans-unit> 1075 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1058 <trans-unit id="3183213940445113677" datatype="html"> 1076 <trans-unit id="3183213940445113677" datatype="html">
1059 <source>Or sign in with</source> 1077 <source>Or sign in with</source>
1060 <target state="translated">No clàraich a-steach le</target> 1078 <target state="translated">No clàraich a-steach le</target>
1061 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1079
1062 </trans-unit> 1080 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1063 <trans-unit id="3238209155172574367" datatype="html"> 1081 <trans-unit id="3238209155172574367" datatype="html">
1064 <source>Forgot your password</source> 1082 <source>Forgot your password</source>
1065 <target state="translated">Na dhìochuimhnich thu am facal-faire agad?</target> 1083 <target state="translated">Na dhìochuimhnich thu am facal-faire agad?</target>
1066 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1084
1067 </trans-unit> 1085 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1068 <trans-unit id="87327320394367488" datatype="html"> 1086 <trans-unit id="87327320394367488" datatype="html">
1069 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1087 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1070 <target state="translated">Tha sinn duilich ach chan urrainn dhut am facal-faire agad aiseag air sgàth ’s nach do rèitich rianaire an ionstans seo siostam puist-d PeerTube.</target> 1088 <target state="translated">Tha sinn duilich ach chan urrainn dhut am facal-faire agad aiseag air sgàth ’s nach do rèitich rianaire an ionstans seo siostam puist-d PeerTube.</target>
1071 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1089
1072 </trans-unit> 1090 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1073 <trans-unit id="3188014010833256853" datatype="html"> 1091 <trans-unit id="3188014010833256853" datatype="html">
1074 <source>Enter your email address and we will send you a link to reset your password.</source> 1092 <source>Enter your email address and we will send you a link to reset your password.</source>
1075 <target state="translated">Cuir a-steach an seòladh puist-d agad agus cuiridh sinn ceangal thugad gus am facal-faire agad ath-shuidheachadh.</target> 1093 <target state="translated">Cuir a-steach an seòladh puist-d agad agus cuiridh sinn ceangal thugad gus am facal-faire agad ath-shuidheachadh.</target>
1076 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1094
1077 </trans-unit> 1095 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1078 <trans-unit id="1190256911880544559" datatype="html"> 1096 <trans-unit id="1190256911880544559" datatype="html">
1079 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1097 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1080The link will expire within 1 hour.</source> 1098The link will expire within 1 hour.</source>
@@ -1084,26 +1102,26 @@ The link will expire within 1 hour.</source>
1084 <trans-unit id="4768749765465246664" datatype="html"> 1102 <trans-unit id="4768749765465246664" datatype="html">
1085 <source>Email</source> 1103 <source>Email</source>
1086 <target state="translated">Post-d</target> 1104 <target state="translated">Post-d</target>
1087 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1105
1088 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1106
1089 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1107
1090 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1108
1091 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1109
1092 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1110
1093 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1111
1094 </trans-unit> 1112 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1095 <trans-unit id="3967269098753656610" datatype="html"> 1113 <trans-unit id="3967269098753656610" datatype="html">
1096 <source>Email address</source> 1114 <source>Email address</source>
1097 <target state="translated">Seòladh puist-d</target> 1115 <target state="translated">Seòladh puist-d</target>
1098 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1116
1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1117
1100 </trans-unit> 1118 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1101 <trans-unit id="7808756054397155068" datatype="html"> 1119 <trans-unit id="7808756054397155068" datatype="html">
1102 <source>Reset</source> 1120 <source>Reset</source>
1103 <target state="translated">Ath-shuidhich</target> 1121 <target state="translated">Ath-shuidhich</target>
1104 <note priority="1" from="description">Password reset button</note> 1122 <note priority="1" from="description">Password reset button</note>
1105 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1123
1106 </trans-unit> 1124 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1107 <trans-unit id="4319634264526091601" datatype="html"> 1125 <trans-unit id="4319634264526091601" datatype="html">
1108 <source>on this instance</source> 1126 <source>on this instance</source>
1109 <target state="translated">air an ionstans seo</target> 1127 <target state="translated">air an ionstans seo</target>
@@ -1427,9 +1445,9 @@ The link will expire within 1 hour.</source>
1427 <trans-unit id="2308975396733519902" datatype="html"> 1445 <trans-unit id="2308975396733519902" datatype="html">
1428 <source>Create an account</source> 1446 <source>Create an account</source>
1429 <target state="translated">Cruthaich cunntas</target> 1447 <target state="translated">Cruthaich cunntas</target>
1430 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1448
1431 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1449
1432 </trans-unit> 1450 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1433 <trans-unit id="3058024914967508975" datatype="html"> 1451 <trans-unit id="3058024914967508975" datatype="html">
1434 <source>My videos</source> 1452 <source>My videos</source>
1435 <target state="translated">Na videothan agam</target> 1453 <target state="translated">Na videothan agam</target>
@@ -1496,10 +1514,10 @@ The link will expire within 1 hour.</source>
1496 <trans-unit id="1504521795586863905" datatype="html"> 1514 <trans-unit id="1504521795586863905" datatype="html">
1497 <source>VIDEOS</source> 1515 <source>VIDEOS</source>
1498 <target state="translated">VIDEOTHAN</target> 1516 <target state="translated">VIDEOTHAN</target>
1499 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1517
1500 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1518
1501 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1519
1502 </trans-unit> 1520 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1503 <trans-unit id="667372110624203230" datatype="html"> 1521 <trans-unit id="667372110624203230" datatype="html">
1504 <source>Import jobs concurrency</source> 1522 <source>Import jobs concurrency</source>
1505 <target state="translated">Co-ruith nan obraichean ion-phortaidh</target> 1523 <target state="translated">Co-ruith nan obraichean ion-phortaidh</target>
@@ -1578,8 +1596,8 @@ The link will expire within 1 hour.</source>
1578 <trans-unit id="4424964105331349857" datatype="html"> 1596 <trans-unit id="4424964105331349857" datatype="html">
1579 <source>I'm a teapot</source> 1597 <source>I'm a teapot</source>
1580 <target state="translated">’S e poit-tì a th’ annam</target> 1598 <target state="translated">’S e poit-tì a th’ annam</target>
1581 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1599
1582 </trans-unit> 1600 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1583 <trans-unit id="1597262876035959248" datatype="html"> 1601 <trans-unit id="1597262876035959248" datatype="html">
1584 <source>That's an error.</source> 1602 <source>That's an error.</source>
1585 <target state="translated">Seo mearachd.</target> 1603 <target state="translated">Seo mearachd.</target>
@@ -1672,8 +1690,8 @@ The link will expire within 1 hour.</source>
1672 <trans-unit id="2971365540217107489" datatype="html"> 1690 <trans-unit id="2971365540217107489" datatype="html">
1673 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1691 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1674 <target state="translated">Tha am meadhan ro mhòr airson an fhrithealaiche. Cuir fios gun rianaire agad ma tha thu airson crìoch a’ mheud a mheudachadh.</target> 1692 <target state="translated">Tha am meadhan ro mhòr airson an fhrithealaiche. Cuir fios gun rianaire agad ma tha thu airson crìoch a’ mheud a mheudachadh.</target>
1675 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1693
1676 </trans-unit> 1694 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1677 <trans-unit id="5131854469652959713" datatype="html"> 1695 <trans-unit id="5131854469652959713" datatype="html">
1678 <source>GLOBAL SEARCH</source> 1696 <source>GLOBAL SEARCH</source>
1679 <target state="translated">LORG UILE-CHOITCHEANN</target> 1697 <target state="translated">LORG UILE-CHOITCHEANN</target>
@@ -2409,8 +2427,8 @@ The link will expire within 1 hour.</source>
2409 <trans-unit id="6161604372916832458" datatype="html"> 2427 <trans-unit id="6161604372916832458" datatype="html">
2410 <source>Upload on hold</source> 2428 <source>Upload on hold</source>
2411 <target state="translated">Tha an luchdadh suas ’ga cumail air ais</target> 2429 <target state="translated">Tha an luchdadh suas ’ga cumail air ais</target>
2412 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2430
2413 </trans-unit> 2431 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2414 <trans-unit id="285180972645018518" datatype="html"> 2432 <trans-unit id="285180972645018518" datatype="html">
2415 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2433 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2416 <target state="translated">Tha sinn duilich ach chaidh gleus an luchdaidh suas a chur à comas dhan chunntas agad. Ma tha thu airson videothan a chur ris, feumaidh rianaire an glas a thoirt far a’ chuota agad.</target> 2434 <target state="translated">Tha sinn duilich ach chaidh gleus an luchdaidh suas a chur à comas dhan chunntas agad. Ma tha thu airson videothan a chur ris, feumaidh rianaire an glas a thoirt far a’ chuota agad.</target>
@@ -3078,11 +3096,7 @@ The link will expire within 1 hour.</source>
3078 <target state="translated">ID</target> 3096 <target state="translated">ID</target>
3079 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3097 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3080 </trans-unit> 3098 </trans-unit>
3081 <trans-unit id="2265605798180116441" datatype="html"> 3099
3082 <source>Follower handle</source>
3083 <target state="translated">Làimhsichear neach-leantainn</target>
3084 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3085 </trans-unit>
3086 <trans-unit id="5911214550882917183" datatype="html"> 3100 <trans-unit id="5911214550882917183" datatype="html">
3087 <source>State</source> 3101 <source>State</source>
3088 <target state="translated">Staid</target> 3102 <target state="translated">Staid</target>
@@ -3148,11 +3162,7 @@ The link will expire within 1 hour.</source>
3148 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target> 3162 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target>
3149 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3163 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3150 </trans-unit> 3164 </trans-unit>
3151 <trans-unit id="6641024648411549335" datatype="html"> 3165
3152 <source>Host</source>
3153 <target state="translated">Òstair</target>
3154 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3155 </trans-unit>
3156 <trans-unit id="6571718060636962350" datatype="html"> 3166 <trans-unit id="6571718060636962350" datatype="html">
3157 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3167 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3158 <target state="translated">Tha anabarrachd ceadaichte <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 3168 <target state="translated">Tha anabarrachd ceadaichte <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -3161,9 +3171,9 @@ The link will expire within 1 hour.</source>
3161 <trans-unit id="9160510009013134726" datatype="html"> 3171 <trans-unit id="9160510009013134726" datatype="html">
3162 <source>Unfollow</source> 3172 <source>Unfollow</source>
3163 <target state="translated">Na lean tuilleadh</target> 3173 <target state="translated">Na lean tuilleadh</target>
3164 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3174
3165 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3175
3166 </trans-unit> 3176 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3167 <trans-unit id="8246779176913476983" datatype="html"> 3177 <trans-unit id="8246779176913476983" datatype="html">
3168 <source>Open instance in a new tab</source> 3178 <source>Open instance in a new tab</source>
3169 <target state="translated">Fosgail an t-ionstans ann an taba ùr</target> 3179 <target state="translated">Fosgail an t-ionstans ann an taba ùr</target>
@@ -3174,28 +3184,20 @@ The link will expire within 1 hour.</source>
3174 <trans-unit id="9132918641931433659" datatype="html"> 3184 <trans-unit id="9132918641931433659" datatype="html">
3175 <source>No host found matching current filters.</source> 3185 <source>No host found matching current filters.</source>
3176 <target state="translated">Cha deach òstair a lorg a fhreagras ris na criathragan làithreach.</target> 3186 <target state="translated">Cha deach òstair a lorg a fhreagras ris na criathragan làithreach.</target>
3177 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3187
3178 </trans-unit> 3188 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3179 <trans-unit id="7274241885665071790" datatype="html"> 3189 <trans-unit id="7274241885665071790" datatype="html">
3180 <source>Your instance is not following anyone.</source> 3190 <source>Your instance is not following anyone.</source>
3181 <target state="translated">Chan eil an t-ionstans agad a’ leantainn air dad sam bith.</target> 3191 <target state="translated">Chan eil an t-ionstans agad a’ leantainn air dad sam bith.</target>
3182 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3192
3183 </trans-unit> 3193 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3184 <trans-unit id="4774348799569692380" datatype="html"> 3194 <trans-unit id="4774348799569692380" datatype="html">
3185 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3195 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3186 <target state="translated">A’ sealltainn <x id="INTERPOLATION"/> gu <x id="INTERPOLATION_1"/> à <x id="INTERPOLATION_2"/> òstair(ean)</target> 3196 <target state="translated">A’ sealltainn <x id="INTERPOLATION"/> gu <x id="INTERPOLATION_1"/> à <x id="INTERPOLATION_2"/> òstair(ean)</target>
3187 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3197 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3188 </trans-unit> 3198 </trans-unit>
3189 <trans-unit id="6275803119759621687" datatype="html"> 3199
3190 <source>Follow domains</source> 3200
3191 <target state="translated">Lean air àrainnean</target>
3192 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3193 </trans-unit>
3194 <trans-unit id="1268699198448750610" datatype="html">
3195 <source>Follow instances</source>
3196 <target state="translated">Lean air ionstansan</target>
3197 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3198 </trans-unit>
3199 <trans-unit id="9216117865911519658" datatype="html"> 3201 <trans-unit id="9216117865911519658" datatype="html">
3200 <source>Action</source> 3202 <source>Action</source>
3201 <target state="translated">Gnìomh</target> 3203 <target state="translated">Gnìomh</target>
@@ -3245,11 +3247,11 @@ The link will expire within 1 hour.</source>
3245 <trans-unit id="5248717555542428023" datatype="html"> 3247 <trans-unit id="5248717555542428023" datatype="html">
3246 <source>Username</source> 3248 <source>Username</source>
3247 <target state="translated">Ainm-cleachdaiche</target> 3249 <target state="translated">Ainm-cleachdaiche</target>
3248 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3250
3249 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3251
3250 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3252
3251 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3253
3252 </trans-unit> 3254 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3253 <trans-unit id="5428411040014095392" datatype="html"> 3255 <trans-unit id="5428411040014095392" datatype="html">
3254 <source>e.g. jane_doe</source> 3256 <source>e.g. jane_doe</source>
3255 <target state="translated">m.e.: mairi_mhor</target> 3257 <target state="translated">m.e.: mairi_mhor</target>
@@ -3277,9 +3279,9 @@ The link will expire within 1 hour.</source>
3277 <trans-unit id="4145496584631696119" datatype="html"> 3279 <trans-unit id="4145496584631696119" datatype="html">
3278 <source>Role</source> 3280 <source>Role</source>
3279 <target state="translated">Dreuchd</target> 3281 <target state="translated">Dreuchd</target>
3280 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3282
3281 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3283
3282 </trans-unit> 3284 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3283 <trans-unit id="7046347992315328430" datatype="html"> 3285 <trans-unit id="7046347992315328430" datatype="html">
3284 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3286 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3285 <target state="translated">Tha an tar-chòdachadh an comas. Cha dèid ach meud <x id="START_TAG_STRONG"/>tùsail<x id="CLOSE_TAG_STRONG"/> nam videothan a chunntadh mu choinneamh a’ chuota. <x id="LINE_BREAK"/> B’ urrainn dhan chleachdaiche seo mu <x id="INTERPOLATION"/> a luchdadh suas air a char as motha. </target> 3287 <target state="translated">Tha an tar-chòdachadh an comas. Cha dèid ach meud <x id="START_TAG_STRONG"/>tùsail<x id="CLOSE_TAG_STRONG"/> nam videothan a chunntadh mu choinneamh a’ chuota. <x id="LINE_BREAK"/> B’ urrainn dhan chleachdaiche seo mu <x id="INTERPOLATION"/> a luchdadh suas air a char as motha. </target>
@@ -3296,15 +3298,9 @@ The link will expire within 1 hour.</source>
3296 <trans-unit id="2622255144026150901" datatype="html"> 3298 <trans-unit id="2622255144026150901" datatype="html">
3297 <source>Auth plugin</source> 3299 <source>Auth plugin</source>
3298 <target state="translated">Plugan dearbh-aithneachaidh</target> 3300 <target state="translated">Plugan dearbh-aithneachaidh</target>
3299 <context-group purpose="location"> 3301
3300 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3302
3301 <context context-type="linenumber">188</context> 3303 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3302 </context-group>
3303 <context-group purpose="location">
3304 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3305 <context context-type="linenumber">188</context>
3306 </context-group>
3307 </trans-unit>
3308 <trans-unit id="588099657508661970" datatype="html"> 3304 <trans-unit id="588099657508661970" datatype="html">
3309 <source>None (local authentication)</source> 3305 <source>None (local authentication)</source>
3310 <target state="translated">Chan eil gin (dearbh-aithneachadh ionadail)</target> 3306 <target state="translated">Chan eil gin (dearbh-aithneachadh ionadail)</target>
@@ -3555,6 +3551,12 @@ The link will expire within 1 hour.</source>
3555 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3551 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3556 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3552 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3557 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3553 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3554 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3555 <source>Follower</source><target state="new">Follower</target>
3556 <context-group purpose="location">
3557 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3558 <context context-type="linenumber">24</context>
3559 </context-group>
3558 </trans-unit> 3560 </trans-unit>
3559 <trans-unit id="4691552465058437520" datatype="html"> 3561 <trans-unit id="4691552465058437520" datatype="html">
3560 <source>Commented video</source> 3562 <source>Commented video</source>
@@ -3854,8 +3856,8 @@ The link will expire within 1 hour.</source>
3854 <trans-unit id="4917252294930256268" datatype="html"> 3856 <trans-unit id="4917252294930256268" datatype="html">
3855 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3857 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3856 <target state="translated">Tha coltas nach eil thu air frithealaiche HTTPS. Feumaidh am frithealaiche agad TLS a chur an comas mus lean e air frithealaichean eile.</target> 3858 <target state="translated">Tha coltas nach eil thu air frithealaiche HTTPS. Feumaidh am frithealaiche agad TLS a chur an comas mus lean e air frithealaichean eile.</target>
3857 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 3859
3858 </trans-unit> 3860 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3859 <trans-unit id="4058814854824495833" datatype="html"> 3861 <trans-unit id="4058814854824495833" datatype="html">
3860 <source>Mute domains</source> 3862 <source>Mute domains</source>
3861 <target state="translated">Mùch àrainnean</target> 3863 <target state="translated">Mùch àrainnean</target>
@@ -5794,11 +5796,8 @@ color: red;
5794 <trans-unit id="5512878593724620692" datatype="html"> 5796 <trans-unit id="5512878593724620692" datatype="html">
5795 <source>CHANNELS</source> 5797 <source>CHANNELS</source>
5796 <target state="translated">SEANAILEAN</target> 5798 <target state="translated">SEANAILEAN</target>
5797 <context-group purpose="location"> 5799
5798 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5800 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5799 <context context-type="linenumber">82</context>
5800 </context-group>
5801 </trans-unit>
5802 <trans-unit id="3666829335406793239" datatype="html"> 5801 <trans-unit id="3666829335406793239" datatype="html">
5803 <source>This account does not have channels.</source> 5802 <source>This account does not have channels.</source>
5804 <target state="translated">Chan eil seanail aig a’ chunntas seo.</target> 5803 <target state="translated">Chan eil seanail aig a’ chunntas seo.</target>
@@ -5837,6 +5836,12 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
5837channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 5836channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
5838 <target state="translated">A bheil thu cinnteach gu bheil thu airson <x id="PH" equiv-text="videoChannel.displayName"/> a sguabadh às?Sguabaidh seo às <x id="PH_1" equiv-text="videoChannel.videosCount"/> video(than) a chaidh a luchdadh suas dhan t-seanail seo ’s chan urrainn dhut seanail eile air a bheil an t-aon ainm (<x id="PH_2" equiv-text="videoChannel.name"/>) a chruthachadh!</target> 5837 <target state="translated">A bheil thu cinnteach gu bheil thu airson <x id="PH" equiv-text="videoChannel.displayName"/> a sguabadh às?Sguabaidh seo às <x id="PH_1" equiv-text="videoChannel.videosCount"/> video(than) a chaidh a luchdadh suas dhan t-seanail seo ’s chan urrainn dhut seanail eile air a bheil an t-aon ainm (<x id="PH_2" equiv-text="videoChannel.name"/>) a chruthachadh!</target>
5839 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 5838 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
5839 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
5840 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5841 <context-group purpose="location">
5842 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5843 <context context-type="linenumber">48</context>
5844 </context-group>
5840 </trans-unit> 5845 </trans-unit>
5841 <trans-unit id="5387007581996837469" datatype="html"> 5846 <trans-unit id="5387007581996837469" datatype="html">
5842 <source>My Channels</source> 5847 <source>My Channels</source>
@@ -6350,13 +6355,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6350 <trans-unit id="6979021199788941693" datatype="html"> 6355 <trans-unit id="6979021199788941693" datatype="html">
6351 <source>Your message has been sent.</source> 6356 <source>Your message has been sent.</source>
6352 <target state="translated">Chaidh an teachdaireachd agad a chur.</target> 6357 <target state="translated">Chaidh an teachdaireachd agad a chur.</target>
6353 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group> 6358
6354 </trans-unit> 6359 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6355 <trans-unit id="2072135752262464360" datatype="html"> 6360 <trans-unit id="2072135752262464360" datatype="html">
6356 <source>You already sent this form recently</source> 6361 <source>You already sent this form recently</source>
6357 <target state="translated">Chuir thu am foirm seo a-null o chionn greis mu thràth</target> 6362 <target state="translated">Chuir thu am foirm seo a-null o chionn greis mu thràth</target>
6358 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group> 6363
6359 </trans-unit> 6364 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6360 <trans-unit id="819067926858619041" datatype="html"> 6365 <trans-unit id="819067926858619041" datatype="html">
6361 <source>Account videos</source> 6366 <source>Account videos</source>
6362 <target state="translated">Videothan a’ chunntais</target> 6367 <target state="translated">Videothan a’ chunntais</target>
@@ -6399,13 +6404,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6399 <trans-unit id="4856575356061361269" datatype="html"> 6404 <trans-unit id="4856575356061361269" datatype="html">
6400 <source><x id="PH"/> direct account followers </source> 6405 <source><x id="PH"/> direct account followers </source>
6401 <target state="translated"><x id="PH"/> luchd-leantainn dìreach a’ chunntais </target> 6406 <target state="translated"><x id="PH"/> luchd-leantainn dìreach a’ chunntais </target>
6402 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6407
6403 </trans-unit> 6408 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6404 <trans-unit id="6250999352462648289" datatype="html"> 6409 <trans-unit id="6250999352462648289" datatype="html">
6405 <source>Report this account</source> 6410 <source>Report this account</source>
6406 <target state="translated">Dèan gearan mun chunntas seo</target> 6411 <target state="translated">Dèan gearan mun chunntas seo</target>
6407 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6412
6408 </trans-unit> 6413 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6409 <trans-unit id="1504521795586863905" datatype="html"> 6414 <trans-unit id="1504521795586863905" datatype="html">
6410 <source>VIDEOS</source> 6415 <source>VIDEOS</source>
6411 <target state="translated">VIDEOTHAN</target> 6416 <target state="translated">VIDEOTHAN</target>
@@ -6415,31 +6420,21 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6415 <trans-unit id="25349740244798533" datatype="html"> 6420 <trans-unit id="25349740244798533" datatype="html">
6416 <source>Username copied</source> 6421 <source>Username copied</source>
6417 <target state="translated">Chaidh lethbhreac a dhèanamh dhen ainm-chleachdaiche</target> 6422 <target state="translated">Chaidh lethbhreac a dhèanamh dhen ainm-chleachdaiche</target>
6418 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6423
6419 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6424
6420 </trans-unit> 6425 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6421 <trans-unit id="9221735175659318025" datatype="html"> 6426 <trans-unit id="9221735175659318025" datatype="html">
6422 <source>1 subscriber</source> 6427 <source>1 subscriber</source>
6423 <target state="translated">Rinn 1 fo-sgrìobhadh air</target> 6428 <target state="translated">Rinn 1 fo-sgrìobhadh air</target>
6424 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6429
6425 </trans-unit> 6430 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6426 <trans-unit id="4097331874769079975" datatype="html"> 6431 <trans-unit id="4097331874769079975" datatype="html">
6427 <source><x id="PH"/> subscribers</source> 6432 <source><x id="PH"/> subscribers</source>
6428 <target state="translated">Rinn <x id="PH"/> fo-sgrìobhadh air</target> 6433 <target state="translated">Rinn <x id="PH"/> fo-sgrìobhadh air</target>
6429 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6434
6430 </trans-unit> 6435 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6431 <trans-unit id="4682675125751819107" datatype="html"> 6436
6432 <source>Instances you follow</source> 6437
6433 <target state="translated">Na h-ionstansan air a leanas tu</target>
6434 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6435 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6436 </trans-unit>
6437 <trans-unit id="8899833753704589712" datatype="html">
6438 <source>Instances following you</source>
6439 <target state="translated">Na h-ionstansan a tha a’ leantainn ort</target>
6440 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6441 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6442 </trans-unit>
6443 <trans-unit id="1035838766454786107" datatype="html"> 6438 <trans-unit id="1035838766454786107" datatype="html">
6444 <source>Audio-only</source> 6439 <source>Audio-only</source>
6445 <target state="translated">Fuaim a-mhàin</target> 6440 <target state="translated">Fuaim a-mhàin</target>
@@ -6489,6 +6484,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6489 <source>Auto (via ffmpeg)</source> 6484 <source>Auto (via ffmpeg)</source>
6490 <target state="translated">Fèin-obrachail (slighe ffmpeg)</target> 6485 <target state="translated">Fèin-obrachail (slighe ffmpeg)</target>
6491 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6486 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6487 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6488 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6489 <context-group purpose="location">
6490 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6491 <context context-type="linenumber">3</context>
6492 </context-group>
6492 </trans-unit> 6493 </trans-unit>
6493 <trans-unit id="931255636742351800" datatype="html"> 6494 <trans-unit id="931255636742351800" datatype="html">
6494 <source>No limit</source> 6495 <source>No limit</source>
@@ -6637,18 +6638,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6637 <trans-unit id="2127446333083057097" datatype="html"> 6638 <trans-unit id="2127446333083057097" datatype="html">
6638 <source>Domain is required.</source> 6639 <source>Domain is required.</source>
6639 <target state="translated">Tha àrainn-lìn riatanach.</target> 6640 <target state="translated">Tha àrainn-lìn riatanach.</target>
6640 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6641
6641 </trans-unit> 6642 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6642 <trans-unit id="6780793142903080663" datatype="html"> 6643 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6643 <source>Domains entered are invalid.</source> 6644 <context-group purpose="location">
6644 <target state="translated">Chaidh àrainn mhì-dhligheach a chur a-steach.</target> 6645 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6645 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6646 <context context-type="linenumber">93</context>
6646 </trans-unit> 6647 </context-group>
6647 <trans-unit id="5886492514458202177" datatype="html"> 6648 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6648 <source>Domains entered contain duplicates.</source> 6649 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6649 <target state="translated">Tha dùblachadh am measg nan àrainnean a chaidh a chur a-steach.</target> 6650 <context-group purpose="location">
6650 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6651 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6652 <context context-type="linenumber">94</context>
6653 </context-group>
6654 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6655 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6656 <context-group purpose="location">
6657 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6658 <context context-type="linenumber">102</context>
6659 </context-group>
6660 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6661 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6662 <context-group purpose="location">
6663 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6664 <context context-type="linenumber">103</context>
6665 </context-group>
6651 </trans-unit> 6666 </trans-unit>
6667
6668
6652 <trans-unit id="240806681889331244" datatype="html"> 6669 <trans-unit id="240806681889331244" datatype="html">
6653 <source>Unlimited</source> 6670 <source>Unlimited</source>
6654 <target state="translated">Gun chrìoch</target> 6671 <target state="translated">Gun chrìoch</target>
@@ -6802,22 +6819,48 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6802 <source><x id="PH"/> removed from instance followers </source> 6819 <source><x id="PH"/> removed from instance followers </source>
6803 <target state="translated">Chaidh <x id="PH"/> a thoirt air falbh ach nach lean e air an ionstans tuilleadh </target> 6820 <target state="translated">Chaidh <x id="PH"/> a thoirt air falbh ach nach lean e air an ionstans tuilleadh </target>
6804 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6821 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6822 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6823 <source>Follow</source><target state="new">Follow</target>
6824 <context-group purpose="location">
6825 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6826 <context context-type="linenumber">3</context>
6827 </context-group>
6828 <context-group purpose="location">
6829 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6830 <context context-type="linenumber">37</context>
6831 </context-group>
6832 <context-group purpose="location">
6833 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6834 <context context-type="linenumber">18</context>
6835 </context-group>
6836 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6837 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6838 <context-group purpose="location">
6839 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6840 <context context-type="linenumber">11</context>
6841 </context-group>
6805 </trans-unit> 6842 </trans-unit>
6806 <trans-unit id="2740793005745065895" datatype="html"> 6843 <trans-unit id="2740793005745065895" datatype="html">
6807 <source><x id="PH"/> is not valid </source> 6844 <source><x id="PH"/> is not valid </source>
6808 <target state="translated">Chan eil <x id="PH"/> dligheach </target> 6845 <target state="translated">Chan eil <x id="PH"/> dligheach </target>
6809 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6846
6810 </trans-unit> 6847 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6811 <trans-unit id="2355066641781598196" datatype="html"> 6848 <trans-unit id="2355066641781598196" datatype="html">
6812 <source>Follow request(s) sent!</source> 6849 <source>Follow request(s) sent!</source>
6813 <target state="translated">Chaidh iarrtas(an) leantainn a chur!</target> 6850 <target state="translated">Chaidh iarrtas(an) leantainn a chur!</target>
6814 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6851
6852 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6853 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6854 <context-group purpose="location">
6855 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6856 <context context-type="linenumber">3</context>
6857 </context-group>
6815 </trans-unit> 6858 </trans-unit>
6816 <trans-unit id="4245720728052819482" datatype="html"> 6859 <trans-unit id="4245720728052819482" datatype="html">
6817 <source>Do you really want to unfollow <x id="PH"/>?</source> 6860 <source>Do you really want to unfollow <x id="PH"/>?</source>
6818 <target state="translated">A bheil thu cinnteach gu bheil thu airson sgur de leantainn air <x id="PH"/>?</target> 6861 <target state="translated">A bheil thu cinnteach gu bheil thu airson sgur de leantainn air <x id="PH"/>?</target>
6819 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6862
6820 </trans-unit> 6863 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6821 <trans-unit id="9160510009013134726" datatype="html"> 6864 <trans-unit id="9160510009013134726" datatype="html">
6822 <source>Unfollow</source> 6865 <source>Unfollow</source>
6823 <target state="translated">Na lean tuilleadh</target> 6866 <target state="translated">Na lean tuilleadh</target>
@@ -6826,8 +6869,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6826 <trans-unit id="3935234189109112926" datatype="html"> 6869 <trans-unit id="3935234189109112926" datatype="html">
6827 <source>You are not following <x id="PH"/> anymore.</source> 6870 <source>You are not following <x id="PH"/> anymore.</source>
6828 <target state="translated">Chan eil thu a’ leantainn air <x id="PH"/> tuilleadh.</target> 6871 <target state="translated">Chan eil thu a’ leantainn air <x id="PH"/> tuilleadh.</target>
6829 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6872
6830 </trans-unit> 6873 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6831 <trans-unit id="2593763089859685916" datatype="html"> 6874 <trans-unit id="2593763089859685916" datatype="html">
6832 <source>enabled</source> 6875 <source>enabled</source>
6833 <target state="translated">an comas</target> 6876 <target state="translated">an comas</target>
@@ -7291,9 +7334,9 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7291 <trans-unit id="1519954996184640001" datatype="html"> 7334 <trans-unit id="1519954996184640001" datatype="html">
7292 <source>Error</source> 7335 <source>Error</source>
7293 <target state="translated">Mearachd</target> 7336 <target state="translated">Mearachd</target>
7294 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7337
7295 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7338
7296 </trans-unit> 7339 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7297 <trans-unit id="5076187961693950167" datatype="html"> 7340 <trans-unit id="5076187961693950167" datatype="html">
7298 <source>Standard logs</source> 7341 <source>Standard logs</source>
7299 <target state="translated">Logaichean àbhaisteach</target> 7342 <target state="translated">Logaichean àbhaisteach</target>
@@ -7334,16 +7377,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7334 <target state="translated">Ùraich facal-faire a’ chleachdaiche</target> 7377 <target state="translated">Ùraich facal-faire a’ chleachdaiche</target>
7335 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7378 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7336 </trans-unit> 7379 </trans-unit>
7337 <trans-unit id="177544274549739411" datatype="html"> 7380
7338 <source>Following list</source> 7381
7339 <target state="translated">Liosta dhen fheadhainn air a leanas tu</target>
7340 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7341 </trans-unit>
7342 <trans-unit id="8092429110007204784" datatype="html">
7343 <source>Followers list</source>
7344 <target state="translated">Liosta dhen fheadhainn a leanas ort</target>
7345 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7346 </trans-unit>
7347 <trans-unit id="780323526182667308" datatype="html"> 7382 <trans-unit id="780323526182667308" datatype="html">
7348 <source>User <x id="PH"/> updated.</source> 7383 <source>User <x id="PH"/> updated.</source>
7349 <target state="translated">Chaidh an cleachdaiche <x id="PH"/> ùrachadh.</target> 7384 <target state="translated">Chaidh an cleachdaiche <x id="PH"/> ùrachadh.</target>
@@ -7379,16 +7414,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7379 <target state="translated">Co-nasgadh</target> 7414 <target state="translated">Co-nasgadh</target>
7380 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7415 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7381 </trans-unit> 7416 </trans-unit>
7382 <trans-unit id="4682675125751819107" datatype="html"> 7417
7383 <source>Instances you follow</source> 7418
7384 <target state="translated">Na h-ionstansan air a leanas tu</target>
7385 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7386 </trans-unit>
7387 <trans-unit id="8899833753704589712" datatype="html">
7388 <source>Instances following you</source>
7389 <target state="translated">Na h-ionstansan a tha a’ leantainn ort</target>
7390 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7391 </trans-unit>
7392 <trans-unit id="3767259920053407667" datatype="html"> 7419 <trans-unit id="3767259920053407667" datatype="html">
7393 <source>Videos will be deleted, comments will be tombstoned.</source> 7420 <source>Videos will be deleted, comments will be tombstoned.</source>
7394 <target state="translated">Thèid na videothan a sguabadh às agus comharra sguabaidh às a chur ris na beachdan.</target> 7421 <target state="translated">Thèid na videothan a sguabadh às agus comharra sguabaidh às a chur ris na beachdan.</target>
@@ -7419,6 +7446,24 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7419 <target state="translated">Suidhich gun deach am post-d a dhearbhadh</target> 7446 <target state="translated">Suidhich gun deach am post-d a dhearbhadh</target>
7420 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7447 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7421 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7448 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7449 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7450 <source>Created</source><target state="new">Created</target>
7451 <context-group purpose="location">
7452 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7453 <context context-type="linenumber">115</context>
7454 </context-group>
7455 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7456 <source>Daily quota</source><target state="new">Daily quota</target>
7457 <context-group purpose="location">
7458 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7459 <context context-type="linenumber">120</context>
7460 </context-group>
7461 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7462 <source>Last login</source><target state="new">Last login</target>
7463 <context-group purpose="location">
7464 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7465 <context context-type="linenumber">122</context>
7466 </context-group>
7422 </trans-unit> 7467 </trans-unit>
7423 <trans-unit id="3403978719736970622" datatype="html"> 7468 <trans-unit id="3403978719736970622" datatype="html">
7424 <source>You cannot ban root.</source> 7469 <source>You cannot ban root.</source>
@@ -7717,13 +7762,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7717 <trans-unit id="1137937154872046253" datatype="html"> 7762 <trans-unit id="1137937154872046253" datatype="html">
7718 <source>Video channel <x id="PH"/> created.</source> 7763 <source>Video channel <x id="PH"/> created.</source>
7719 <target state="translated">Chaidh seanail video <x id="PH"/> a chruthachadh.</target> 7764 <target state="translated">Chaidh seanail video <x id="PH"/> a chruthachadh.</target>
7720 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 7765
7721 </trans-unit> 7766 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7722 <trans-unit id="8723777130353305761" datatype="html"> 7767 <trans-unit id="8723777130353305761" datatype="html">
7723 <source>This name already exists on this instance.</source> 7768 <source>This name already exists on this instance.</source>
7724 <target state="translated">Tha an t-ainm seo ann air an ionstans seo mu thràth.</target> 7769 <target state="translated">Tha an t-ainm seo ann air an ionstans seo mu thràth.</target>
7725 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 7770
7726 </trans-unit> 7771 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7727 <trans-unit id="7589345916094713536" datatype="html"> 7772 <trans-unit id="7589345916094713536" datatype="html">
7728 <source>Video channel <x id="PH"/> updated.</source> 7773 <source>Video channel <x id="PH"/> updated.</source>
7729 <target state="translated">Chaidh seanail video <x id="PH"/> ùrachadh.</target> 7774 <target state="translated">Chaidh seanail video <x id="PH"/> ùrachadh.</target>
@@ -7744,11 +7789,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7744 <target state="translated">Chaidh a’ bhratach a sguabadh às.</target> 7789 <target state="translated">Chaidh a’ bhratach a sguabadh às.</target>
7745 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 7790 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
7746 </trans-unit> 7791 </trans-unit>
7747 <trans-unit id="2575302837003821736" datatype="html"> 7792
7748 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7749 <target state="translated">Sgrìobh ainm-taisbeanaidh na seanail video (<x id="PH"/>) gus a dhearbhadh</target>
7750 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
7751 </trans-unit>
7752 <trans-unit id="624066830180032195" datatype="html"> 7793 <trans-unit id="624066830180032195" datatype="html">
7753 <source>Video channel <x id="PH"/> deleted.</source> 7794 <source>Video channel <x id="PH"/> deleted.</source>
7754 <target state="translated">Chaidh seanail video <x id="PH"/> a sguabadh às.</target> 7795 <target state="translated">Chaidh seanail video <x id="PH"/> a sguabadh às.</target>
@@ -7896,6 +7937,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7896 <source>Ownership change request sent.</source> 7937 <source>Ownership change request sent.</source>
7897 <target state="translated">Chaidh iarrtas a chur air atharrachadh an t-seilbh.</target> 7938 <target state="translated">Chaidh iarrtas a chur air atharrachadh an t-seilbh.</target>
7898 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 7939 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
7940 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
7941 <source>Sort by</source><target state="new">Sort by</target>
7942 <context-group purpose="location">
7943 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7944 <context context-type="linenumber">26</context>
7945 </context-group>
7899 </trans-unit> 7946 </trans-unit>
7900 <trans-unit id="3245220240937722814" datatype="html"> 7947 <trans-unit id="3245220240937722814" datatype="html">
7901 <source>My channels</source> 7948 <source>My channels</source>
@@ -7994,7 +8041,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7994 <target state="translated">Fo-sgrìobh air a’ chunntas</target> 8041 <target state="translated">Fo-sgrìobh air a’ chunntas</target>
7995 8042
7996 8043
7997 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8044 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
7998 <trans-unit id="3131904093925601441" datatype="html"> 8045 <trans-unit id="3131904093925601441" datatype="html">
7999 <source>PLAYLISTS</source> 8046 <source>PLAYLISTS</source>
8000 <target state="translated">LIOSTAICHEAN-CLUICH</target> 8047 <target state="translated">LIOSTAICHEAN-CLUICH</target>
@@ -8041,34 +8088,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8041 <trans-unit id="3779524668013120370" datatype="html"> 8088 <trans-unit id="3779524668013120370" datatype="html">
8042 <source>Go to my subscriptions</source> 8089 <source>Go to my subscriptions</source>
8043 <target state="translated">Tadhail air na fo-sgrìobhaidhean agam</target> 8090 <target state="translated">Tadhail air na fo-sgrìobhaidhean agam</target>
8044 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8091
8045 </trans-unit> 8092 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8046 <trans-unit id="1136469849928650779" datatype="html"> 8093 <trans-unit id="1136469849928650779" datatype="html">
8047 <source>Go to my videos</source> 8094 <source>Go to my videos</source>
8048 <target state="translated">Tadhail air na videothan agam</target> 8095 <target state="translated">Tadhail air na videothan agam</target>
8049 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8096
8050 </trans-unit> 8097 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8051 <trans-unit id="7836683738999600376" datatype="html"> 8098 <trans-unit id="7836683738999600376" datatype="html">
8052 <source>Go to my imports</source> 8099 <source>Go to my imports</source>
8053 <target state="translated">Tadhail air na h-ion-phortaidhean agam</target> 8100 <target state="translated">Tadhail air na h-ion-phortaidhean agam</target>
8054 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8101
8055 </trans-unit> 8102 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8056 <trans-unit id="7511292153332773503" datatype="html"> 8103 <trans-unit id="7511292153332773503" datatype="html">
8057 <source>Go to my channels</source> 8104 <source>Go to my channels</source>
8058 <target state="translated">Tadhail air na seanailean agam</target> 8105 <target state="translated">Tadhail air na seanailean agam</target>
8059 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8106
8060 </trans-unit> 8107 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8061 <trans-unit id="2013324644839511073" datatype="html"> 8108 <trans-unit id="2013324644839511073" datatype="html">
8062 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8109 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8063Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8110Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8064 <target state="translated">Cha b’ urrainn dhuinn an teisteas cliant OAuth fhaighinn: <x id="PH" equiv-text="error.text"/>. Dèan cinnteach gun do rèitich thu PeerTube mar bu chòir (sa phasgan config/) ’s gu sònraichte an earrann "webserver".</target> 8111 <target state="translated">Cha b’ urrainn dhuinn an teisteas cliant OAuth fhaighinn: <x id="PH" equiv-text="error.text"/>. Dèan cinnteach gun do rèitich thu PeerTube mar bu chòir (sa phasgan config/) ’s gu sònraichte an earrann "webserver".</target>
8065 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8112
8066 </trans-unit> 8113 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8067 <trans-unit id="375263728166936544" datatype="html"> 8114 <trans-unit id="375263728166936544" datatype="html">
8068 <source>You need to reconnect.</source> 8115 <source>You need to reconnect.</source>
8069 <target state="translated">Feumaidh tu ceangal ris a-rithist.</target> 8116 <target state="translated">Feumaidh tu ceangal ris a-rithist.</target>
8070 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8117
8071 </trans-unit> 8118 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8072 <trans-unit id="2206638022166154361" datatype="html"> 8119 <trans-unit id="2206638022166154361" datatype="html">
8073 <source>Keyboard Shortcuts:</source> 8120 <source>Keyboard Shortcuts:</source>
8074 <target state="translated">Ath-ghoiridean a’ mheur-chlàir:</target> 8121 <target state="translated">Ath-ghoiridean a’ mheur-chlàir:</target>
@@ -8081,6 +8128,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8081 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8128 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8082 <context context-type="linenumber">98</context> 8129 <context context-type="linenumber">98</context>
8083 </context-group> 8130 </context-group>
8131 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8132 <source>In my library</source><target state="new">In my library</target>
8133 <context-group purpose="location">
8134 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8135 <context context-type="linenumber">104</context>
8136 </context-group>
8084 </trans-unit> 8137 </trans-unit>
8085 <trans-unit id="232050922346936574" datatype="html"> 8138 <trans-unit id="232050922346936574" datatype="html">
8086 <source>Trending</source> 8139 <source>Trending</source>
@@ -8109,38 +8162,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8109 <trans-unit id="1266887509445371246" datatype="html"> 8162 <trans-unit id="1266887509445371246" datatype="html">
8110 <source>Incorrect username or password.</source> 8163 <source>Incorrect username or password.</source>
8111 <target state="translated">Chan eil an t-ainm-cleachdaiche no chan eil am facal-faire mar bu chòir.</target> 8164 <target state="translated">Chan eil an t-ainm-cleachdaiche no chan eil am facal-faire mar bu chòir.</target>
8112 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8165
8113 </trans-unit> 8166 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8114 <trans-unit id="6974874606619467663" datatype="html"> 8167 <trans-unit id="6974874606619467663" datatype="html">
8115 <source>Your account is blocked.</source> 8168 <source>Your account is blocked.</source>
8116 <target state="translated">Chaidh an cunntas agad a bhacadh.</target> 8169 <target state="translated">Chaidh an cunntas agad a bhacadh.</target>
8117 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8170
8118 </trans-unit> 8171 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8119 <trans-unit id="7939914198003891823" datatype="html"> 8172 <trans-unit id="7939914198003891823" datatype="html">
8120 <source>any language</source> 8173 <source>any language</source>
8121 <target state="translated">cànan sam bith</target> 8174 <target state="translated">cànan sam bith</target>
8122 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8175
8123 </trans-unit> 8176 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8124 <trans-unit id="5633144232269377096" datatype="html"> 8177 <trans-unit id="5633144232269377096" datatype="html">
8125 <source>hide</source> 8178 <source>hide</source>
8126 <target state="translated">falaich</target> 8179 <target state="translated">falaich</target>
8127 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8180
8128 </trans-unit> 8181 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8129 <trans-unit id="8603861867909474404" datatype="html"> 8182 <trans-unit id="8603861867909474404" datatype="html">
8130 <source>blur</source> 8183 <source>blur</source>
8131 <target state="translated">sgleò</target> 8184 <target state="translated">sgleò</target>
8132 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8185
8133 </trans-unit> 8186 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8134 <trans-unit id="4534458451100881847" datatype="html"> 8187 <trans-unit id="4534458451100881847" datatype="html">
8135 <source>display</source> 8188 <source>display</source>
8136 <target state="translated">taisbean</target> 8189 <target state="translated">taisbean</target>
8137 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8190
8138 </trans-unit> 8191 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8139 <trans-unit id="4467323362722952678" datatype="html"> 8192 <trans-unit id="4467323362722952678" datatype="html">
8140 <source>Unknown</source> 8193 <source>Unknown</source>
8141 <target state="translated">Chan eil fhios</target> 8194 <target state="translated">Chan eil fhios</target>
8142 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8195
8143 </trans-unit> 8196 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8144 <trans-unit id="8781423666414310853" datatype="html"> 8197 <trans-unit id="8781423666414310853" datatype="html">
8145 <source>Your password has been successfully reset!</source> 8198 <source>Your password has been successfully reset!</source>
8146 <target state="translated">Chaidh am facal-faire agad ath-shuidheachadh!</target> 8199 <target state="translated">Chaidh am facal-faire agad ath-shuidheachadh!</target>
@@ -9698,18 +9751,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9698 <trans-unit id="968295009933361070" datatype="html"> 9751 <trans-unit id="968295009933361070" datatype="html">
9699 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9752 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9700 <target state="translated">Cus oidhirpean, feuch ris a-rithist an ceann <x id="PH"/> mionaid(ean).</target> 9753 <target state="translated">Cus oidhirpean, feuch ris a-rithist an ceann <x id="PH"/> mionaid(ean).</target>
9701 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9754
9702 </trans-unit> 9755 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9703 <trans-unit id="4965472196059235310" datatype="html"> 9756 <trans-unit id="4965472196059235310" datatype="html">
9704 <source>Too many attempts, please try again later.</source> 9757 <source>Too many attempts, please try again later.</source>
9705 <target state="translated">Cus oidhirpean, feuch ris a-rithist an ceann greis.</target> 9758 <target state="translated">Cus oidhirpean, feuch ris a-rithist an ceann greis.</target>
9706 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9759
9707 </trans-unit> 9760 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9708 <trans-unit id="1693549688987384699" datatype="html"> 9761 <trans-unit id="1693549688987384699" datatype="html">
9709 <source>Server error. Please retry later.</source> 9762 <source>Server error. Please retry later.</source>
9710 <target state="translated">Mearachd an fhrithealaiche. Feuch ris a-rithist an ceann greis.</target> 9763 <target state="translated">Mearachd an fhrithealaiche. Feuch ris a-rithist an ceann greis.</target>
9711 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9764
9712 </trans-unit> 9765 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9713 <trans-unit id="5927402622550505067" datatype="html"> 9766 <trans-unit id="5927402622550505067" datatype="html">
9714 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9767 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9715 <target state="translated">Fhuair thu fo-sgrìobhadh air na seanailean làithreach uile aig <x id="PH"/>. Gheibh thu brathan-naidheachd mu na videothan ùra aca uile.</target> 9768 <target state="translated">Fhuair thu fo-sgrìobhadh air na seanailean làithreach uile aig <x id="PH"/>. Gheibh thu brathan-naidheachd mu na videothan ùra aca uile.</target>
@@ -10294,33 +10347,33 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10294 <trans-unit id="3284171506518522275" datatype="html"> 10347 <trans-unit id="3284171506518522275" datatype="html">
10295 <source>Your video was uploaded to your account and is private.</source> 10348 <source>Your video was uploaded to your account and is private.</source>
10296 <target state="translated">Chaidh a’ video agad a luchdadh suas dhan chunntas agad ’s tha e prìobhaideach.</target> 10349 <target state="translated">Chaidh a’ video agad a luchdadh suas dhan chunntas agad ’s tha e prìobhaideach.</target>
10297 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10350
10298 </trans-unit> 10351 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10299 <trans-unit id="5699822024600815733" datatype="html"> 10352 <trans-unit id="5699822024600815733" datatype="html">
10300 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10353 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10301 <target state="translated">Ach thèid dàta sam bith nach deach a shàbhaladh (tagaichean, tuairisgeulan…) air chall, a bheil thu cinnteach gu bheil thu airson an duilleag seo fhàgail?</target> 10354 <target state="translated">Ach thèid dàta sam bith nach deach a shàbhaladh (tagaichean, tuairisgeulan…) air chall, a bheil thu cinnteach gu bheil thu airson an duilleag seo fhàgail?</target>
10302 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10355
10303 </trans-unit> 10356 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10304 <trans-unit id="1219739004043110649" datatype="html"> 10357 <trans-unit id="1219739004043110649" datatype="html">
10305 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10358 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10306 <target state="translated">Cha deach a’ video agad a luchdadh suas fhathast, a bheil thu cinnteach gu bheil thu airson an duilleag seo fhàgail?</target> 10359 <target state="translated">Cha deach a’ video agad a luchdadh suas fhathast, a bheil thu cinnteach gu bheil thu airson an duilleag seo fhàgail?</target>
10307 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10360
10308 </trans-unit> 10361 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10309 <trans-unit id="6932865105766151309" datatype="html"> 10362 <trans-unit id="6932865105766151309" datatype="html">
10310 <source>Upload</source> 10363 <source>Upload</source>
10311 <target state="translated">Luchdaich suas</target> 10364 <target state="translated">Luchdaich suas</target>
10312 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10365
10313 </trans-unit> 10366 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10314 <trans-unit id="8278735427925094503" datatype="html"> 10367 <trans-unit id="8278735427925094503" datatype="html">
10315 <source>Upload <x id="PH"/> </source> 10368 <source>Upload <x id="PH"/> </source>
10316 <target state="translated">Luchdaich suas <x id="PH"/> </target> 10369 <target state="translated">Luchdaich suas <x id="PH"/> </target>
10317 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10370
10318 </trans-unit> 10371 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10319 <trans-unit id="5981816353437801748" datatype="html"> 10372 <trans-unit id="5981816353437801748" datatype="html">
10320 <source>Video published.</source> 10373 <source>Video published.</source>
10321 <target state="translated">Chaidh a’ video fhoillseachadh.</target> 10374 <target state="translated">Chaidh a’ video fhoillseachadh.</target>
10322 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10375
10323 </trans-unit> 10376 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10324 <trans-unit id="764164089183618119" datatype="html"> 10377 <trans-unit id="764164089183618119" datatype="html">
10325 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10378 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10326 <target state="translated">Tha atharraichean gun sàbhaladh agad! Ma dh’fhalbhas tu, thèid na h-atharraichean agad air chall.</target> 10379 <target state="translated">Tha atharraichean gun sàbhaladh agad! Ma dh’fhalbhas tu, thèid na h-atharraichean agad air chall.</target>
@@ -10387,28 +10440,28 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10387 <trans-unit id="961774488937452220" datatype="html"> 10440 <trans-unit id="961774488937452220" datatype="html">
10388 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10441 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10389 <target state="translated">Chan eil a’ video seo ri fhaighinn air an ionstans seo. A bheil thu airson ’s gun dèid d’ ath-stiùireadh dhan ionstans thùsail: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10442 <target state="translated">Chan eil a’ video seo ri fhaighinn air an ionstans seo. A bheil thu airson ’s gun dèid d’ ath-stiùireadh dhan ionstans thùsail: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10390 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group> 10443
10391 </trans-unit> 10444 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10392 <trans-unit id="5761611056224181752" datatype="html"> 10445 <trans-unit id="5761611056224181752" datatype="html">
10393 <source>Redirection</source> 10446 <source>Redirection</source>
10394 <target state="translated">Ath-stiùireadh</target> 10447 <target state="translated">Ath-stiùireadh</target>
10395 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group> 10448
10396 </trans-unit> 10449 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10397 <trans-unit id="8858527736400081688" datatype="html"> 10450 <trans-unit id="8858527736400081688" datatype="html">
10398 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10451 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10399 <target state="translated">Tha susbaint sa video seo a tha iomchaidh do dh’inbhich a-mhàin. A bheil thu cinnteach gu bheil thu airson coimhead air?</target> 10452 <target state="translated">Tha susbaint sa video seo a tha iomchaidh do dh’inbhich a-mhàin. A bheil thu cinnteach gu bheil thu airson coimhead air?</target>
10400 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group> 10453
10401 </trans-unit> 10454 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10402 <trans-unit id="3937119019020041049" datatype="html"> 10455 <trans-unit id="3937119019020041049" datatype="html">
10403 <source>Mature or explicit content</source> 10456 <source>Mature or explicit content</source>
10404 <target state="translated">Susbaint do dh’inbhich</target> 10457 <target state="translated">Susbaint do dh’inbhich</target>
10405 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group> 10458
10406 </trans-unit> 10459 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10407 <trans-unit id="1755474755114288376" datatype="html"> 10460 <trans-unit id="1755474755114288376" datatype="html">
10408 <source>Up Next</source> 10461 <source>Up Next</source>
10409 <target state="translated">Ri thighinn</target> 10462 <target state="translated">Ri thighinn</target>
10410 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group> 10463
10411 </trans-unit> 10464 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10412 <trans-unit id="2159130950882492111" datatype="html"> 10465 <trans-unit id="2159130950882492111" datatype="html">
10413 <source>Cancel</source> 10466 <source>Cancel</source>
10414 <target state="translated">Sguir dheth</target> 10467 <target state="translated">Sguir dheth</target>
@@ -10417,63 +10470,63 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10417 <trans-unit id="3354816756665089864" datatype="html"> 10470 <trans-unit id="3354816756665089864" datatype="html">
10418 <source>Autoplay is suspended</source> 10471 <source>Autoplay is suspended</source>
10419 <target state="translated">Chaidh a’ chluiche fhèin-obrachail a chur à rèim</target> 10472 <target state="translated">Chaidh a’ chluiche fhèin-obrachail a chur à rèim</target>
10420 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group> 10473
10421 </trans-unit> 10474 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10422 <trans-unit id="7895294730547405228" datatype="html"> 10475 <trans-unit id="7895294730547405228" datatype="html">
10423 <source>Enter/exit fullscreen (requires player focus)</source> 10476 <source>Enter/exit fullscreen (requires player focus)</source>
10424 <target state="translated">A-steach/a-mach às a’ mhodh làn-sgrìn (bidh an cluicheadair feumach air an fhòcas)</target> 10477 <target state="translated">A-steach/a-mach às a’ mhodh làn-sgrìn (bidh an cluicheadair feumach air an fhòcas)</target>
10425 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group> 10478
10426 </trans-unit> 10479 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10427 <trans-unit id="7618388257165864759" datatype="html"> 10480 <trans-unit id="7618388257165864759" datatype="html">
10428 <source>Play/Pause the video (requires player focus)</source> 10481 <source>Play/Pause the video (requires player focus)</source>
10429 <target state="translated">Cluich a’ video/Cuir ’na stad e (bidh an cluicheadair feumach air an fhòcas)</target> 10482 <target state="translated">Cluich a’ video/Cuir ’na stad e (bidh an cluicheadair feumach air an fhòcas)</target>
10430 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group> 10483
10431 </trans-unit> 10484 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10432 <trans-unit id="7761890399634216630" datatype="html"> 10485 <trans-unit id="7761890399634216630" datatype="html">
10433 <source>Mute/unmute the video (requires player focus)</source> 10486 <source>Mute/unmute the video (requires player focus)</source>
10434 <target state="translated">Mùch/Dì-mhùch a’ video (bidh an cluicheadair feumach air an fhòcas)</target> 10487 <target state="translated">Mùch/Dì-mhùch a’ video (bidh an cluicheadair feumach air an fhòcas)</target>
10435 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group> 10488
10436 </trans-unit> 10489 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10437 <trans-unit id="5996585232248234904" datatype="html"> 10490 <trans-unit id="5996585232248234904" datatype="html">
10438 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10491 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10439 <target state="translated">Thoir leum gu ceudad dhen video: Bheir 0 gu 0% thu agus 9 gu 90% (bidh an cluicheadair feumach air an fhòcas)</target> 10492 <target state="translated">Thoir leum gu ceudad dhen video: Bheir 0 gu 0% thu agus 9 gu 90% (bidh an cluicheadair feumach air an fhòcas)</target>
10440 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group> 10493
10441 </trans-unit> 10494 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10442 <trans-unit id="3748765405903319998" datatype="html"> 10495 <trans-unit id="3748765405903319998" datatype="html">
10443 <source>Increase the volume (requires player focus)</source> 10496 <source>Increase the volume (requires player focus)</source>
10444 <target state="translated">Meudaich àirde na fuaime (bidh an cluicheadair feumach air an fhòcas)</target> 10497 <target state="translated">Meudaich àirde na fuaime (bidh an cluicheadair feumach air an fhòcas)</target>
10445 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group> 10498
10446 </trans-unit> 10499 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10447 <trans-unit id="5810704036407159982" datatype="html"> 10500 <trans-unit id="5810704036407159982" datatype="html">
10448 <source>Decrease the volume (requires player focus)</source> 10501 <source>Decrease the volume (requires player focus)</source>
10449 <target state="translated">Lùghdaich àirde na fuaime (bidh an cluicheadair feumach air an fhòcas)</target> 10502 <target state="translated">Lùghdaich àirde na fuaime (bidh an cluicheadair feumach air an fhòcas)</target>
10450 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group> 10503
10451 </trans-unit> 10504 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10452 <trans-unit id="2622048822548065691" datatype="html"> 10505 <trans-unit id="2622048822548065691" datatype="html">
10453 <source>Seek the video forward (requires player focus)</source> 10506 <source>Seek the video forward (requires player focus)</source>
10454 <target state="translated">Sir air adhart sa video (bidh an cluicheadair feumach air an fhòcas)</target> 10507 <target state="translated">Sir air adhart sa video (bidh an cluicheadair feumach air an fhòcas)</target>
10455 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group> 10508
10456 </trans-unit> 10509 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10457 <trans-unit id="6540078205109221153" datatype="html"> 10510 <trans-unit id="6540078205109221153" datatype="html">
10458 <source>Seek the video backward (requires player focus)</source> 10511 <source>Seek the video backward (requires player focus)</source>
10459 <target state="translated">Sir air ais sa video (bidh an cluicheadair feumach air an fhòcas)</target> 10512 <target state="translated">Sir air ais sa video (bidh an cluicheadair feumach air an fhòcas)</target>
10460 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group> 10513
10461 </trans-unit> 10514 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10462 <trans-unit id="1956491957766210808" datatype="html"> 10515 <trans-unit id="1956491957766210808" datatype="html">
10463 <source>Increase playback rate (requires player focus)</source> 10516 <source>Increase playback rate (requires player focus)</source>
10464 <target state="translated">Dèan a’ chluich nas luaithe (bidh an cluicheadair feumach air an fhòcas)</target> 10517 <target state="translated">Dèan a’ chluich nas luaithe (bidh an cluicheadair feumach air an fhòcas)</target>
10465 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group> 10518
10466 </trans-unit> 10519 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10467 <trans-unit id="5495529997674803186" datatype="html"> 10520 <trans-unit id="5495529997674803186" datatype="html">
10468 <source>Decrease playback rate (requires player focus)</source> 10521 <source>Decrease playback rate (requires player focus)</source>
10469 <target state="translated">Dèan a’ chluich nas maille (bidh an cluicheadair feumach air an fhòcas)</target> 10522 <target state="translated">Dèan a’ chluich nas maille (bidh an cluicheadair feumach air an fhòcas)</target>
10470 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group> 10523
10471 </trans-unit> 10524 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10472 <trans-unit id="3178343147230721210" datatype="html"> 10525 <trans-unit id="3178343147230721210" datatype="html">
10473 <source>Navigate in the video frame by frame (requires player focus)</source> 10526 <source>Navigate in the video frame by frame (requires player focus)</source>
10474 <target state="translated">Seòl tron video frèam air fhrèam (bidh an cluicheadair feumach air an fhòcas)</target> 10527 <target state="translated">Seòl tron video frèam air fhrèam (bidh an cluicheadair feumach air an fhòcas)</target>
10475 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group> 10528
10476 </trans-unit> 10529 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10477 <trans-unit id="8025996572234182184" datatype="html"> 10530 <trans-unit id="8025996572234182184" datatype="html">
10478 <source>Like the video</source> 10531 <source>Like the video</source>
10479 <target state="translated">Comharraich gur toigh leat a’ video</target> 10532 <target state="translated">Comharraich gur toigh leat a’ video</target>
diff --git a/client/src/locale/angular.gl-ES.xlf b/client/src/locale/angular.gl-ES.xlf
index 3a4a121be..751265c37 100644
--- a/client/src/locale/angular.gl-ES.xlf
+++ b/client/src/locale/angular.gl-ES.xlf
@@ -162,19 +162,19 @@
162 <target state="translated"> 162 <target state="translated">
163 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 163 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
164 </target> 164 </target>
165 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group> 165
166 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group> 166
167 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group> 167
168 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group> 168
169 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group> 169
170 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 170
171 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group> 171
172 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group> 172
173 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group> 173
174 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group> 174
175 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group> 175
176 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group> 176
177 </trans-unit> 177 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
178 <trans-unit id="1486537403020619891" datatype="html"> 178 <trans-unit id="1486537403020619891" datatype="html">
179 <source>My watch history</source> 179 <source>My watch history</source>
180 <target state="translated">Historial de visualizacións</target> 180 <target state="translated">Historial de visualizacións</target>
@@ -243,22 +243,22 @@
243 <trans-unit id="5674286808255988565"> 243 <trans-unit id="5674286808255988565">
244 <source>Create</source> 244 <source>Create</source>
245 <target>Crear</target> 245 <target>Crear</target>
246 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 246
247 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 247
248 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 248
249 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 249
250 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 250
251 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 251
252 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 252
253 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 253
254 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 254
255 </trans-unit> 255 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
256 <trans-unit id="1006562256968398209" datatype="html"> 256 <trans-unit id="1006562256968398209" datatype="html">
257 <source>video</source> 257 <source>video</source>
258 <target state="translated">vídeo</target> 258 <target state="translated">vídeo</target>
259 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group> 259
260 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group> 260
261 </trans-unit> 261 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
262 <trans-unit id="6438815964972582865" datatype="html"> 262 <trans-unit id="6438815964972582865" datatype="html">
263 <source>The following link contains a private token and should not be shared with anyone.</source> 263 <source>The following link contains a private token and should not be shared with anyone.</source>
264 <target state="translated">A seguinte ligazón contén un token privado e non deberías compartilo con ninguén.</target> 264 <target state="translated">A seguinte ligazón contén un token privado e non deberías compartilo con ninguén.</target>
@@ -330,13 +330,13 @@
330 <trans-unit id="6995024616159044376" datatype="html"> 330 <trans-unit id="6995024616159044376" datatype="html">
331 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 331 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
332 <target state="translated">Este vídeo fai que superes a túa cota de vídeo (tamaño do vídeo: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, cota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 332 <target state="translated">Este vídeo fai que superes a túa cota de vídeo (tamaño do vídeo: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, cota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
333 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 333
334 </trans-unit> 334 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
335 <trans-unit id="7873395933409147217" datatype="html"> 335 <trans-unit id="7873395933409147217" datatype="html">
336 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 336 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
337 <target state="translated">Con este vídeo superas a túa cota diaria de vídeo (tamaño do vídeo: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, cota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 337 <target state="translated">Con este vídeo superas a túa cota diaria de vídeo (tamaño do vídeo: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, cota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
338 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 338
339 </trans-unit> 339 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
340 <trans-unit id="5235042777215655908" datatype="html"> 340 <trans-unit id="5235042777215655908" datatype="html">
341 <source>subtitles</source> 341 <source>subtitles</source>
342 <target state="translated">subtítulos</target> 342 <target state="translated">subtítulos</target>
@@ -776,10 +776,10 @@
776 <trans-unit id="2602586221576511475"> 776 <trans-unit id="2602586221576511475">
777 <source>Video quota</source> 777 <source>Video quota</source>
778 <target>Cota de vídeo</target> 778 <target>Cota de vídeo</target>
779 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 779
780 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 780
781 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 781
782 </trans-unit> 782 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
783 <trans-unit id="1502595455339510144"> 783 <trans-unit id="1502595455339510144">
784 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 784 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
785 <target>Sen límite <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> diario)<x id="CLOSE_TAG_NG_CONTAINER"/></target> 785 <target>Sen límite <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> diario)<x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -859,6 +859,30 @@
859 <target state="translated">Federación</target> 859 <target state="translated">Federación</target>
860 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 860 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
861 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 861 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
862 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
863 <source>Following</source><target state="new">Following</target>
864 <context-group purpose="location">
865 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
866 <context context-type="linenumber">29</context>
867 </context-group>
868 <context-group purpose="location">
869 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
870 <context context-type="linenumber">31</context>
871 </context-group>
872 <context-group purpose="location">
873 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
874 <context context-type="linenumber">28</context>
875 </context-group>
876 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
877 <source>Followers</source><target state="new">Followers</target>
878 <context-group purpose="location">
879 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
880 <context context-type="linenumber">34</context>
881 </context-group>
882 <context-group purpose="location">
883 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
884 <context context-type="linenumber">37</context>
885 </context-group>
862 </trans-unit> 886 </trans-unit>
863 <trans-unit id="3541687134897970106" datatype="html"> 887 <trans-unit id="3541687134897970106" datatype="html">
864 <source>followers</source> 888 <source>followers</source>
@@ -922,25 +946,25 @@
922 <trans-unit id="2159130950882492111"> 946 <trans-unit id="2159130950882492111">
923 <source>Cancel</source> 947 <source>Cancel</source>
924 <target>Cancelar</target> 948 <target>Cancelar</target>
925 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group> 949
926 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group> 950
927 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group> 951
928 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group> 952
929 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group> 953
930 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group> 954
931 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group> 955
932 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group> 956
933 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group> 957
934 <context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group> 958
935 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group> 959
936 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group> 960
937 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 961
938 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 962
939 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group> 963
940 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group> 964
941 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group> 965
942 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group> 966
943 </trans-unit> 967 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
944 <trans-unit id="3616223838716839702"> 968 <trans-unit id="3616223838716839702">
945 <source>Ban this user</source> 969 <source>Ban this user</source>
946 <target>Vetar esta usuaria</target> 970 <target>Vetar esta usuaria</target>
@@ -1006,19 +1030,13 @@
1006 <trans-unit id="7252854992688790751" datatype="html"> 1030 <trans-unit id="7252854992688790751" datatype="html">
1007 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1031 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1008 <target state="translated">Esta instancia ten o rexistro aberto. Non obstante, pon tino en comprobar <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Termos<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Os Termos<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> antes de crear unha conta. Podes atopar outra instancia máis acorde ás túas necesidades en: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1032 <target state="translated">Esta instancia ten o rexistro aberto. Non obstante, pon tino en comprobar <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Termos<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Os Termos<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> antes de crear unha conta. Podes atopar outra instancia máis acorde ás túas necesidades en: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1009 <context-group purpose="location"> 1033
1010 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1034 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1011 <context context-type="linenumber">60,62</context>
1012 </context-group>
1013 </trans-unit>
1014 <trans-unit id="7215649348148521605" datatype="html"> 1035 <trans-unit id="7215649348148521605" datatype="html">
1015 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1036 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1016 <target state="translated">Actualmente esta instancia non permite abrir unha conta, podes ler os <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Termos<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> para saber máis ou atopar outra instancia co rexistro aberto e poder subir alí os teus vídeos. Atopa a túa entre varias opcións en: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1037 <target state="translated">Actualmente esta instancia non permite abrir unha conta, podes ler os <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Termos<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> para saber máis ou atopar outra instancia co rexistro aberto e poder subir alí os teus vídeos. Atopa a túa entre varias opcións en: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1017 <context-group purpose="location"> 1038
1018 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1019 <context context-type="linenumber">65,67</context>
1020 </context-group>
1021 </trans-unit>
1022 <trans-unit id="2392488717875840729"> 1040 <trans-unit id="2392488717875840729">
1023 <source>User</source> 1041 <source>User</source>
1024 <target>Usuaria</target> 1042 <target>Usuaria</target>
@@ -1029,67 +1047,67 @@
1029 <source>Username or email address</source> 1047 <source>Username or email address</source>
1030 <target>Nome de usuaria ou enderezo de correo</target> 1048 <target>Nome de usuaria ou enderezo de correo</target>
1031 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1049 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1050 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1051 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1052 <context-group purpose="location">
1053 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1054 <context context-type="linenumber">33,34</context>
1055 </context-group>
1032 </trans-unit> 1056 </trans-unit>
1033 <trans-unit id="1431416938026210429"> 1057 <trans-unit id="1431416938026210429">
1034 <source>Password</source> 1058 <source>Password</source>
1035 <target>Contrasinal</target> 1059 <target>Contrasinal</target>
1036 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1060
1037 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1061
1038 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1062
1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1063
1040 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1064
1041 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1065
1042 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1066
1043 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1067
1044 </trans-unit> 1068 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1045 <trans-unit id="8715156686857791956" datatype="html"> 1069 <trans-unit id="8715156686857791956" datatype="html">
1046 <source>Click here to reset your password</source> 1070 <source>Click here to reset your password</source>
1047 <target state="translated">Preme aquí e restablece o contrasinal</target> 1071 <target state="translated">Preme aquí e restablece o contrasinal</target>
1048 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1072
1049 </trans-unit> 1073 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1050 <trans-unit id="892063502898494584" datatype="html"> 1074 <trans-unit id="892063502898494584" datatype="html">
1051 <source>I forgot my password</source> 1075 <source>I forgot my password</source>
1052 <target state="translated">Esquecín o contrasinal</target> 1076 <target state="translated">Esquecín o contrasinal</target>
1053 <context-group purpose="location"> 1077
1054 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1078 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1055 <context context-type="linenumber">47</context>
1056 </context-group>
1057 </trans-unit>
1058 <trans-unit id="2101170466365500913" datatype="html"> 1079 <trans-unit id="2101170466365500913" datatype="html">
1059 <source>Logging into an account lets you publish content</source> 1080 <source>Logging into an account lets you publish content</source>
1060 <target state="translated">Se estás conectada poderás publicar o teu contido</target> 1081 <target state="translated">Se estás conectada poderás publicar o teu contido</target>
1061 <context-group purpose="location"> 1082
1062 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1083 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1063 <context context-type="linenumber">56,57</context>
1064 </context-group>
1065 </trans-unit>
1066 <trans-unit id="2454050363478003966"> 1084 <trans-unit id="2454050363478003966">
1067 <source>Login</source> 1085 <source>Login</source>
1068 <target>Conectar</target> 1086 <target>Conectar</target>
1069 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1087
1070 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1088
1071 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1089
1072 </trans-unit> 1090 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1073 <trans-unit id="3183213940445113677" datatype="html"> 1091 <trans-unit id="3183213940445113677" datatype="html">
1074 <source>Or sign in with</source> 1092 <source>Or sign in with</source>
1075 <target state="translated">Ou conéctate con</target> 1093 <target state="translated">Ou conéctate con</target>
1076 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1094
1077 </trans-unit> 1095 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1078 <trans-unit id="3238209155172574367"> 1096 <trans-unit id="3238209155172574367">
1079 <source>Forgot your password</source> 1097 <source>Forgot your password</source>
1080 <target>Esqueceu o contrasinal</target> 1098 <target>Esqueceu o contrasinal</target>
1081 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1099
1082 </trans-unit> 1100 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1083 <trans-unit id="87327320394367488" datatype="html"> 1101 <trans-unit id="87327320394367488" datatype="html">
1084 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1102 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1085 <target state="translated">Lamentámolo, non podes recuperar o contrasinal porque a administración da instancia non configurou o sistema de email de PeerTube.</target> 1103 <target state="translated">Lamentámolo, non podes recuperar o contrasinal porque a administración da instancia non configurou o sistema de email de PeerTube.</target>
1086 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1104
1087 </trans-unit> 1105 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1088 <trans-unit id="3188014010833256853" datatype="html"> 1106 <trans-unit id="3188014010833256853" datatype="html">
1089 <source>Enter your email address and we will send you a link to reset your password.</source> 1107 <source>Enter your email address and we will send you a link to reset your password.</source>
1090 <target state="translated">Escribe o teu email e enviarémosche unha ligazón para restablecer o contrasinal.</target> 1108 <target state="translated">Escribe o teu email e enviarémosche unha ligazón para restablecer o contrasinal.</target>
1091 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1109
1092 </trans-unit> 1110 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1093 <trans-unit id="1190256911880544559" datatype="html"> 1111 <trans-unit id="1190256911880544559" datatype="html">
1094 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1112 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1095The link will expire within 1 hour.</source> 1113The link will expire within 1 hour.</source>
@@ -1099,26 +1117,26 @@ The link will expire within 1 hour.</source>
1099 <trans-unit id="4768749765465246664"> 1117 <trans-unit id="4768749765465246664">
1100 <source>Email</source> 1118 <source>Email</source>
1101 <target>Correo electrónico</target> 1119 <target>Correo electrónico</target>
1102 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1120
1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1121
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1122
1105 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1123
1106 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1124
1107 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1125
1108 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1126
1109 </trans-unit> 1127 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1110 <trans-unit id="3967269098753656610"> 1128 <trans-unit id="3967269098753656610">
1111 <source>Email address</source> 1129 <source>Email address</source>
1112 <target>Enderezo de correo electrónico</target> 1130 <target>Enderezo de correo electrónico</target>
1113 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1131
1114 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1132
1115 </trans-unit> 1133 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1116 <trans-unit id="7808756054397155068" datatype="html"> 1134 <trans-unit id="7808756054397155068" datatype="html">
1117 <source>Reset</source> 1135 <source>Reset</source>
1118 <target state="translated">Restablecer</target> 1136 <target state="translated">Restablecer</target>
1119 <note priority="1" from="description">Password reset button</note> 1137 <note priority="1" from="description">Password reset button</note>
1120 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1138
1121 </trans-unit> 1139 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1122 <trans-unit id="4319634264526091601" datatype="html"> 1140 <trans-unit id="4319634264526091601" datatype="html">
1123 <source>on this instance</source> 1141 <source>on this instance</source>
1124 <target state="translated">nesta instancia</target> 1142 <target state="translated">nesta instancia</target>
@@ -1448,9 +1466,9 @@ The link will expire within 1 hour.</source>
1448 <trans-unit id="2308975396733519902"> 1466 <trans-unit id="2308975396733519902">
1449 <source>Create an account</source> 1467 <source>Create an account</source>
1450 <target>Crear unha conta</target> 1468 <target>Crear unha conta</target>
1451 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1469
1452 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1470
1453 </trans-unit> 1471 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1454 <trans-unit id="3058024914967508975" datatype="html"> 1472 <trans-unit id="3058024914967508975" datatype="html">
1455 <source>My videos</source> 1473 <source>My videos</source>
1456 <target state="translated">Vídeos</target> 1474 <target state="translated">Vídeos</target>
@@ -1517,10 +1535,10 @@ The link will expire within 1 hour.</source>
1517 <trans-unit id="1504521795586863905" datatype="html"> 1535 <trans-unit id="1504521795586863905" datatype="html">
1518 <source>VIDEOS</source> 1536 <source>VIDEOS</source>
1519 <target state="translated">VÍDEOS</target> 1537 <target state="translated">VÍDEOS</target>
1520 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1538
1521 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1539
1522 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1540
1523 </trans-unit> 1541 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1524 <trans-unit id="667372110624203230" datatype="html"> 1542 <trans-unit id="667372110624203230" datatype="html">
1525 <source>Import jobs concurrency</source> 1543 <source>Import jobs concurrency</source>
1526 <target state="translated">Concurrencia de tarefas de importación</target> 1544 <target state="translated">Concurrencia de tarefas de importación</target>
@@ -1599,8 +1617,8 @@ The link will expire within 1 hour.</source>
1599 <trans-unit id="4424964105331349857" datatype="html"> 1617 <trans-unit id="4424964105331349857" datatype="html">
1600 <source>I'm a teapot</source> 1618 <source>I'm a teapot</source>
1601 <target state="translated">Son unha teteira</target> 1619 <target state="translated">Son unha teteira</target>
1602 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1620
1603 </trans-unit> 1621 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1604 <trans-unit id="1597262876035959248" datatype="html"> 1622 <trans-unit id="1597262876035959248" datatype="html">
1605 <source>That's an error.</source> 1623 <source>That's an error.</source>
1606 <target state="translated">É un erro.</target> 1624 <target state="translated">É un erro.</target>
@@ -1693,8 +1711,8 @@ The link will expire within 1 hour.</source>
1693 <trans-unit id="2971365540217107489" datatype="html"> 1711 <trans-unit id="2971365540217107489" datatype="html">
1694 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1712 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1695 <target state="translated">O multimedia é demasiado grande para o servidor. Contacta coa administración se desexas que aumenten o límite.</target> 1713 <target state="translated">O multimedia é demasiado grande para o servidor. Contacta coa administración se desexas que aumenten o límite.</target>
1696 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1714
1697 </trans-unit> 1715 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1698 <trans-unit id="5131854469652959713" datatype="html"> 1716 <trans-unit id="5131854469652959713" datatype="html">
1699 <source>GLOBAL SEARCH</source> 1717 <source>GLOBAL SEARCH</source>
1700 <target state="translated">BUSCA GLOBAL</target> 1718 <target state="translated">BUSCA GLOBAL</target>
@@ -2434,8 +2452,8 @@ The link will expire within 1 hour.</source>
2434 <trans-unit id="6161604372916832458" datatype="html"> 2452 <trans-unit id="6161604372916832458" datatype="html">
2435 <source>Upload on hold</source> 2453 <source>Upload on hold</source>
2436 <target state="translated">Subida agardando</target> 2454 <target state="translated">Subida agardando</target>
2437 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2455
2438 </trans-unit> 2456 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2439 <trans-unit id="285180972645018518" datatype="html"> 2457 <trans-unit id="285180972645018518" datatype="html">
2440 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2458 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2441 <target state="translated">Lamentámolo, a túa conta non permite subir contidos. Se queres engadir vídeos, unha administradora debe aumentar a túa cota.</target> 2459 <target state="translated">Lamentámolo, a túa conta non permite subir contidos. Se queres engadir vídeos, unha administradora debe aumentar a túa cota.</target>
@@ -3123,11 +3141,7 @@ The link will expire within 1 hour.</source>
3123 <target state="translated">ID</target> 3141 <target state="translated">ID</target>
3124 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3142 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3125 </trans-unit> 3143 </trans-unit>
3126 <trans-unit id="2265605798180116441" datatype="html"> 3144
3127 <source>Follower handle</source>
3128 <target state="translated">Xestión da seguidora</target>
3129 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3130 </trans-unit>
3131 <trans-unit id="5911214550882917183" datatype="html"> 3145 <trans-unit id="5911214550882917183" datatype="html">
3132 <source>State</source> 3146 <source>State</source>
3133 <target state="translated">Estado</target> 3147 <target state="translated">Estado</target>
@@ -3193,11 +3207,7 @@ The link will expire within 1 hour.</source>
3193 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target> 3207 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target>
3194 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3208 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3195 </trans-unit> 3209 </trans-unit>
3196 <trans-unit id="6641024648411549335" datatype="html"> 3210
3197 <source>Host</source>
3198 <target state="translated">Servidor</target>
3199 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3200 </trans-unit>
3201 <trans-unit id="6571718060636962350" datatype="html"> 3211 <trans-unit id="6571718060636962350" datatype="html">
3202 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3212 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3203 <target state="translated">Redundancia permitida <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 3213 <target state="translated">Redundancia permitida <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -3206,9 +3216,9 @@ The link will expire within 1 hour.</source>
3206 <trans-unit id="9160510009013134726" datatype="html"> 3216 <trans-unit id="9160510009013134726" datatype="html">
3207 <source>Unfollow</source> 3217 <source>Unfollow</source>
3208 <target state="translated">Deixar de seguir</target> 3218 <target state="translated">Deixar de seguir</target>
3209 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3219
3210 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3220
3211 </trans-unit> 3221 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3212 <trans-unit id="8246779176913476983" datatype="html"> 3222 <trans-unit id="8246779176913476983" datatype="html">
3213 <source>Open instance in a new tab</source> 3223 <source>Open instance in a new tab</source>
3214 <target state="translated">Abrir instancia en nova lapela</target> 3224 <target state="translated">Abrir instancia en nova lapela</target>
@@ -3219,28 +3229,20 @@ The link will expire within 1 hour.</source>
3219 <trans-unit id="9132918641931433659" datatype="html"> 3229 <trans-unit id="9132918641931433659" datatype="html">
3220 <source>No host found matching current filters.</source> 3230 <source>No host found matching current filters.</source>
3221 <target state="translated">Non se atoparon servidores co criterio do filtro.</target> 3231 <target state="translated">Non se atoparon servidores co criterio do filtro.</target>
3222 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3232
3223 </trans-unit> 3233 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3224 <trans-unit id="7274241885665071790" datatype="html"> 3234 <trans-unit id="7274241885665071790" datatype="html">
3225 <source>Your instance is not following anyone.</source> 3235 <source>Your instance is not following anyone.</source>
3226 <target state="translated">A túa instancia non segue a ninguén.</target> 3236 <target state="translated">A túa instancia non segue a ninguén.</target>
3227 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3237
3228 </trans-unit> 3238 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3229 <trans-unit id="4774348799569692380" datatype="html"> 3239 <trans-unit id="4774348799569692380" datatype="html">
3230 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3240 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3231 <target state="translated">Mostrando <x id="INTERPOLATION"/> a <x id="INTERPOLATION_1"/> de <x id="INTERPOLATION_2"/> servidores</target> 3241 <target state="translated">Mostrando <x id="INTERPOLATION"/> a <x id="INTERPOLATION_1"/> de <x id="INTERPOLATION_2"/> servidores</target>
3232 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3242 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3233 </trans-unit> 3243 </trans-unit>
3234 <trans-unit id="6275803119759621687" datatype="html"> 3244
3235 <source>Follow domains</source> 3245
3236 <target state="translated">Seguir dominios</target>
3237 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3238 </trans-unit>
3239 <trans-unit id="1268699198448750610" datatype="html">
3240 <source>Follow instances</source>
3241 <target state="translated">Seguir instancias</target>
3242 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3243 </trans-unit>
3244 <trans-unit id="9216117865911519658" datatype="html"> 3246 <trans-unit id="9216117865911519658" datatype="html">
3245 <source>Action</source> 3247 <source>Action</source>
3246 <target state="translated">Acción</target> 3248 <target state="translated">Acción</target>
@@ -3290,11 +3292,11 @@ The link will expire within 1 hour.</source>
3290 <trans-unit id="5248717555542428023"> 3292 <trans-unit id="5248717555542428023">
3291 <source>Username</source> 3293 <source>Username</source>
3292 <target>Nome de usuaria</target> 3294 <target>Nome de usuaria</target>
3293 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3295
3294 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3296
3295 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3297
3296 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3298
3297 </trans-unit> 3299 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3298 <trans-unit id="5428411040014095392" datatype="html"> 3300 <trans-unit id="5428411040014095392" datatype="html">
3299 <source>e.g. jane_doe</source> 3301 <source>e.g. jane_doe</source>
3300 <target state="translated">ex. ugio_ben</target> 3302 <target state="translated">ex. ugio_ben</target>
@@ -3322,9 +3324,9 @@ The link will expire within 1 hour.</source>
3322 <trans-unit id="4145496584631696119" datatype="html"> 3324 <trans-unit id="4145496584631696119" datatype="html">
3323 <source>Role</source> 3325 <source>Role</source>
3324 <target state="translated">Rol</target> 3326 <target state="translated">Rol</target>
3325 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3327
3326 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3328
3327 </trans-unit> 3329 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3328 <trans-unit id="7046347992315328430" datatype="html"> 3330 <trans-unit id="7046347992315328430" datatype="html">
3329 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3331 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3330 <target state="translated">Recodificación activada. A cota de vídeo só ten en conta o tamaño <x id="START_TAG_STRONG"/>orixinal<x id="CLOSE_TAG_STRONG"/> do vídeo. <x id="LINE_BREAK"/> Como moito, esta usuaria podería subir ~ <x id="INTERPOLATION"/>. </target> 3332 <target state="translated">Recodificación activada. A cota de vídeo só ten en conta o tamaño <x id="START_TAG_STRONG"/>orixinal<x id="CLOSE_TAG_STRONG"/> do vídeo. <x id="LINE_BREAK"/> Como moito, esta usuaria podería subir ~ <x id="INTERPOLATION"/>. </target>
@@ -3341,15 +3343,9 @@ The link will expire within 1 hour.</source>
3341 <trans-unit id="2622255144026150901" datatype="html"> 3343 <trans-unit id="2622255144026150901" datatype="html">
3342 <source>Auth plugin</source> 3344 <source>Auth plugin</source>
3343 <target state="translated">Complemento Auth</target> 3345 <target state="translated">Complemento Auth</target>
3344 <context-group purpose="location"> 3346
3345 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3347
3346 <context context-type="linenumber">188</context> 3348 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3347 </context-group>
3348 <context-group purpose="location">
3349 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3350 <context context-type="linenumber">188</context>
3351 </context-group>
3352 </trans-unit>
3353 <trans-unit id="588099657508661970" datatype="html"> 3349 <trans-unit id="588099657508661970" datatype="html">
3354 <source>None (local authentication)</source> 3350 <source>None (local authentication)</source>
3355 <target state="translated">Ningún (autenticación local)</target> 3351 <target state="translated">Ningún (autenticación local)</target>
@@ -3600,6 +3596,12 @@ The link will expire within 1 hour.</source>
3600 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3596 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3601 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3597 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3602 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3598 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3599 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3600 <source>Follower</source><target state="new">Follower</target>
3601 <context-group purpose="location">
3602 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3603 <context context-type="linenumber">24</context>
3604 </context-group>
3603 </trans-unit> 3605 </trans-unit>
3604 <trans-unit id="4691552465058437520" datatype="html"> 3606 <trans-unit id="4691552465058437520" datatype="html">
3605 <source>Commented video</source> 3607 <source>Commented video</source>
@@ -3899,8 +3901,8 @@ The link will expire within 1 hour.</source>
3899 <trans-unit id="4917252294930256268" datatype="html"> 3901 <trans-unit id="4917252294930256268" datatype="html">
3900 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3902 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3901 <target state="translated">Semella que non estás nun servidor HTTPS. O teu servidor web precisa ter TLS activado para poder seguir servidores.</target> 3903 <target state="translated">Semella que non estás nun servidor HTTPS. O teu servidor web precisa ter TLS activado para poder seguir servidores.</target>
3902 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 3904
3903 </trans-unit> 3905 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3904 <trans-unit id="4058814854824495833" datatype="html"> 3906 <trans-unit id="4058814854824495833" datatype="html">
3905 <source>Mute domains</source> 3907 <source>Mute domains</source>
3906 <target state="translated">Acalar dominios</target> 3908 <target state="translated">Acalar dominios</target>
@@ -5837,11 +5839,8 @@ color: red;
5837 <trans-unit id="5512878593724620692" datatype="html"> 5839 <trans-unit id="5512878593724620692" datatype="html">
5838 <source>CHANNELS</source> 5840 <source>CHANNELS</source>
5839 <target state="translated">CANLES</target> 5841 <target state="translated">CANLES</target>
5840 <context-group purpose="location"> 5842
5841 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5843 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5842 <context context-type="linenumber">82</context>
5843 </context-group>
5844 </trans-unit>
5845 <trans-unit id="3666829335406793239" datatype="html"> 5844 <trans-unit id="3666829335406793239" datatype="html">
5846 <source>This account does not have channels.</source> 5845 <source>This account does not have channels.</source>
5847 <target state="translated">Esta conta non ten canles.</target> 5846 <target state="translated">Esta conta non ten canles.</target>
@@ -5880,6 +5879,12 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
5880channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 5879channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
5881 <target state="translated">Desexas eliminar <x id="PH" equiv-text="videoChannel.displayName"/>? Así eliminarás <x id="PH_1" equiv-text="videoChannel.videosCount"/> vídeos subidos a esta canle, e non poderás volver a crear outra canle co mesmo nome (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target> 5880 <target state="translated">Desexas eliminar <x id="PH" equiv-text="videoChannel.displayName"/>? Así eliminarás <x id="PH_1" equiv-text="videoChannel.videosCount"/> vídeos subidos a esta canle, e non poderás volver a crear outra canle co mesmo nome (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target>
5882 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 5881 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
5882 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
5883 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5884 <context-group purpose="location">
5885 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5886 <context context-type="linenumber">48</context>
5887 </context-group>
5883 </trans-unit> 5888 </trans-unit>
5884 <trans-unit id="5387007581996837469" datatype="html"> 5889 <trans-unit id="5387007581996837469" datatype="html">
5885 <source>My Channels</source> 5890 <source>My Channels</source>
@@ -6395,13 +6400,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6395 <trans-unit id="6979021199788941693" datatype="html"> 6400 <trans-unit id="6979021199788941693" datatype="html">
6396 <source>Your message has been sent.</source> 6401 <source>Your message has been sent.</source>
6397 <target state="translated">A túa mensaxe foi enviada.</target> 6402 <target state="translated">A túa mensaxe foi enviada.</target>
6398 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group> 6403
6399 </trans-unit> 6404 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6400 <trans-unit id="2072135752262464360" datatype="html"> 6405 <trans-unit id="2072135752262464360" datatype="html">
6401 <source>You already sent this form recently</source> 6406 <source>You already sent this form recently</source>
6402 <target state="translated">Xa enviaras este formulario recentemente</target> 6407 <target state="translated">Xa enviaras este formulario recentemente</target>
6403 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group> 6408
6404 </trans-unit> 6409 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6405 <trans-unit id="819067926858619041" datatype="html"> 6410 <trans-unit id="819067926858619041" datatype="html">
6406 <source>Account videos</source> 6411 <source>Account videos</source>
6407 <target state="translated">Vídeos da conta</target> 6412 <target state="translated">Vídeos da conta</target>
@@ -6444,13 +6449,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6444 <trans-unit id="4856575356061361269" datatype="html"> 6449 <trans-unit id="4856575356061361269" datatype="html">
6445 <source><x id="PH"/> direct account followers </source> 6450 <source><x id="PH"/> direct account followers </source>
6446 <target state="translated"><x id="PH"/> seguidoras directas da conta </target> 6451 <target state="translated"><x id="PH"/> seguidoras directas da conta </target>
6447 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6452
6448 </trans-unit> 6453 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6449 <trans-unit id="6250999352462648289" datatype="html"> 6454 <trans-unit id="6250999352462648289" datatype="html">
6450 <source>Report this account</source> 6455 <source>Report this account</source>
6451 <target state="translated">Denunciar esta conta</target> 6456 <target state="translated">Denunciar esta conta</target>
6452 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6457
6453 </trans-unit> 6458 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6454 <trans-unit id="1504521795586863905" datatype="html"> 6459 <trans-unit id="1504521795586863905" datatype="html">
6455 <source>VIDEOS</source> 6460 <source>VIDEOS</source>
6456 <target state="translated">VÍDEOS</target> 6461 <target state="translated">VÍDEOS</target>
@@ -6460,31 +6465,21 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6460 <trans-unit id="25349740244798533" datatype="html"> 6465 <trans-unit id="25349740244798533" datatype="html">
6461 <source>Username copied</source> 6466 <source>Username copied</source>
6462 <target state="translated">Nome de usuaria copiado</target> 6467 <target state="translated">Nome de usuaria copiado</target>
6463 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6468
6464 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6469
6465 </trans-unit> 6470 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6466 <trans-unit id="9221735175659318025" datatype="html"> 6471 <trans-unit id="9221735175659318025" datatype="html">
6467 <source>1 subscriber</source> 6472 <source>1 subscriber</source>
6468 <target state="translated">1 subscritora</target> 6473 <target state="translated">1 subscritora</target>
6469 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6474
6470 </trans-unit> 6475 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6471 <trans-unit id="4097331874769079975" datatype="html"> 6476 <trans-unit id="4097331874769079975" datatype="html">
6472 <source><x id="PH"/> subscribers</source> 6477 <source><x id="PH"/> subscribers</source>
6473 <target state="translated"><x id="PH"/> subscritoras</target> 6478 <target state="translated"><x id="PH"/> subscritoras</target>
6474 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6479
6475 </trans-unit> 6480 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6476 <trans-unit id="4682675125751819107" datatype="html"> 6481
6477 <source>Instances you follow</source> 6482
6478 <target state="translated">Instancias que segues</target>
6479 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6480 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6481 </trans-unit>
6482 <trans-unit id="8899833753704589712" datatype="html">
6483 <source>Instances following you</source>
6484 <target state="translated">Instancias que te seguen</target>
6485 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6486 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6487 </trans-unit>
6488 <trans-unit id="1035838766454786107" datatype="html"> 6483 <trans-unit id="1035838766454786107" datatype="html">
6489 <source>Audio-only</source> 6484 <source>Audio-only</source>
6490 <target state="translated">Só audio</target> 6485 <target state="translated">Só audio</target>
@@ -6534,6 +6529,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6534 <source>Auto (via ffmpeg)</source> 6529 <source>Auto (via ffmpeg)</source>
6535 <target state="translated">Auto (vía ffmpeg)</target> 6530 <target state="translated">Auto (vía ffmpeg)</target>
6536 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6531 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6532 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6533 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6534 <context-group purpose="location">
6535 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6536 <context context-type="linenumber">3</context>
6537 </context-group>
6537 </trans-unit> 6538 </trans-unit>
6538 <trans-unit id="931255636742351800" datatype="html"> 6539 <trans-unit id="931255636742351800" datatype="html">
6539 <source>No limit</source> 6540 <source>No limit</source>
@@ -6682,18 +6683,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6682 <trans-unit id="2127446333083057097" datatype="html"> 6683 <trans-unit id="2127446333083057097" datatype="html">
6683 <source>Domain is required.</source> 6684 <source>Domain is required.</source>
6684 <target state="translated">Requírese un dominio.</target> 6685 <target state="translated">Requírese un dominio.</target>
6685 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6686
6686 </trans-unit> 6687 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6687 <trans-unit id="6780793142903080663" datatype="html"> 6688 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6688 <source>Domains entered are invalid.</source> 6689 <context-group purpose="location">
6689 <target state="translated">O dominio escrito non é válido.</target> 6690 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6690 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6691 <context context-type="linenumber">93</context>
6691 </trans-unit> 6692 </context-group>
6692 <trans-unit id="5886492514458202177" datatype="html"> 6693 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6693 <source>Domains entered contain duplicates.</source> 6694 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6694 <target state="translated">Hai duplicados nos dominios escritos.</target> 6695 <context-group purpose="location">
6695 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6696 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6697 <context context-type="linenumber">94</context>
6698 </context-group>
6699 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6700 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6701 <context-group purpose="location">
6702 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6703 <context context-type="linenumber">102</context>
6704 </context-group>
6705 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6706 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6707 <context-group purpose="location">
6708 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6709 <context context-type="linenumber">103</context>
6710 </context-group>
6696 </trans-unit> 6711 </trans-unit>
6712
6713
6697 <trans-unit id="240806681889331244" datatype="html"> 6714 <trans-unit id="240806681889331244" datatype="html">
6698 <source>Unlimited</source> 6715 <source>Unlimited</source>
6699 <target state="translated">Sen límite</target> 6716 <target state="translated">Sen límite</target>
@@ -6857,22 +6874,48 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6857 <source><x id="PH"/> removed from instance followers </source> 6874 <source><x id="PH"/> removed from instance followers </source>
6858 <target state="translated"><x id="PH"/> eliminada das seguidoras da instancia </target> 6875 <target state="translated"><x id="PH"/> eliminada das seguidoras da instancia </target>
6859 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6876 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6877 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6878 <source>Follow</source><target state="new">Follow</target>
6879 <context-group purpose="location">
6880 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6881 <context context-type="linenumber">3</context>
6882 </context-group>
6883 <context-group purpose="location">
6884 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6885 <context context-type="linenumber">37</context>
6886 </context-group>
6887 <context-group purpose="location">
6888 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6889 <context context-type="linenumber">18</context>
6890 </context-group>
6891 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6892 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6893 <context-group purpose="location">
6894 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6895 <context context-type="linenumber">11</context>
6896 </context-group>
6860 </trans-unit> 6897 </trans-unit>
6861 <trans-unit id="2740793005745065895" datatype="html"> 6898 <trans-unit id="2740793005745065895" datatype="html">
6862 <source><x id="PH"/> is not valid </source> 6899 <source><x id="PH"/> is not valid </source>
6863 <target state="translated"><x id="PH"/> non é válido </target> 6900 <target state="translated"><x id="PH"/> non é válido </target>
6864 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6901
6865 </trans-unit> 6902 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6866 <trans-unit id="2355066641781598196" datatype="html"> 6903 <trans-unit id="2355066641781598196" datatype="html">
6867 <source>Follow request(s) sent!</source> 6904 <source>Follow request(s) sent!</source>
6868 <target state="translated">Solicitude(s) de seguimento enviada(s)!</target> 6905 <target state="translated">Solicitude(s) de seguimento enviada(s)!</target>
6869 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6906
6907 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6908 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6909 <context-group purpose="location">
6910 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6911 <context context-type="linenumber">3</context>
6912 </context-group>
6870 </trans-unit> 6913 </trans-unit>
6871 <trans-unit id="4245720728052819482" datatype="html"> 6914 <trans-unit id="4245720728052819482" datatype="html">
6872 <source>Do you really want to unfollow <x id="PH"/>?</source> 6915 <source>Do you really want to unfollow <x id="PH"/>?</source>
6873 <target state="translated">Desexas deixar de seguir a <x id="PH"/>?</target> 6916 <target state="translated">Desexas deixar de seguir a <x id="PH"/>?</target>
6874 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6917
6875 </trans-unit> 6918 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6876 <trans-unit id="9160510009013134726" datatype="html"> 6919 <trans-unit id="9160510009013134726" datatype="html">
6877 <source>Unfollow</source> 6920 <source>Unfollow</source>
6878 <target state="translated">Deixar de seguir</target> 6921 <target state="translated">Deixar de seguir</target>
@@ -6881,8 +6924,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6881 <trans-unit id="3935234189109112926" datatype="html"> 6924 <trans-unit id="3935234189109112926" datatype="html">
6882 <source>You are not following <x id="PH"/> anymore.</source> 6925 <source>You are not following <x id="PH"/> anymore.</source>
6883 <target state="translated">Xa non segues <x id="PH"/>.</target> 6926 <target state="translated">Xa non segues <x id="PH"/>.</target>
6884 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6927
6885 </trans-unit> 6928 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6886 <trans-unit id="2593763089859685916" datatype="html"> 6929 <trans-unit id="2593763089859685916" datatype="html">
6887 <source>enabled</source> 6930 <source>enabled</source>
6888 <target state="translated">activado</target> 6931 <target state="translated">activado</target>
@@ -7346,9 +7389,9 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7346 <trans-unit id="1519954996184640001" datatype="html"> 7389 <trans-unit id="1519954996184640001" datatype="html">
7347 <source>Error</source> 7390 <source>Error</source>
7348 <target state="translated">Erro</target> 7391 <target state="translated">Erro</target>
7349 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7392
7350 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7393
7351 </trans-unit> 7394 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7352 <trans-unit id="5076187961693950167" datatype="html"> 7395 <trans-unit id="5076187961693950167" datatype="html">
7353 <source>Standard logs</source> 7396 <source>Standard logs</source>
7354 <target state="translated">Rexistros estándar</target> 7397 <target state="translated">Rexistros estándar</target>
@@ -7389,16 +7432,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7389 <target state="translated">Actualizar contrasinal da usuaria</target> 7432 <target state="translated">Actualizar contrasinal da usuaria</target>
7390 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7433 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7391 </trans-unit> 7434 </trans-unit>
7392 <trans-unit id="177544274549739411" datatype="html"> 7435
7393 <source>Following list</source> 7436
7394 <target state="translated">Lista de seguimentos</target>
7395 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7396 </trans-unit>
7397 <trans-unit id="8092429110007204784" datatype="html">
7398 <source>Followers list</source>
7399 <target state="translated">Lista de seguidoras</target>
7400 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7401 </trans-unit>
7402 <trans-unit id="780323526182667308" datatype="html"> 7437 <trans-unit id="780323526182667308" datatype="html">
7403 <source>User <x id="PH"/> updated.</source> 7438 <source>User <x id="PH"/> updated.</source>
7404 <target state="translated">Usuaria <x id="PH"/> actualizada.</target> 7439 <target state="translated">Usuaria <x id="PH"/> actualizada.</target>
@@ -7434,16 +7469,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7434 <target state="translated">Federación</target> 7469 <target state="translated">Federación</target>
7435 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7470 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7436 </trans-unit> 7471 </trans-unit>
7437 <trans-unit id="4682675125751819107" datatype="html"> 7472
7438 <source>Instances you follow</source> 7473
7439 <target state="translated">Instancias que segues</target>
7440 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7441 </trans-unit>
7442 <trans-unit id="8899833753704589712" datatype="html">
7443 <source>Instances following you</source>
7444 <target state="translated">Instancias que te seguen</target>
7445 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7446 </trans-unit>
7447 <trans-unit id="3767259920053407667" datatype="html"> 7474 <trans-unit id="3767259920053407667" datatype="html">
7448 <source>Videos will be deleted, comments will be tombstoned.</source> 7475 <source>Videos will be deleted, comments will be tombstoned.</source>
7449 <target state="translated">Os vídeos serán eliminados, os comentarios serán soterrados.</target> 7476 <target state="translated">Os vídeos serán eliminados, os comentarios serán soterrados.</target>
@@ -7474,6 +7501,24 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7474 <target state="translated">Establecer email como Verificado</target> 7501 <target state="translated">Establecer email como Verificado</target>
7475 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7502 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7476 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7503 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7504 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7505 <source>Created</source><target state="new">Created</target>
7506 <context-group purpose="location">
7507 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7508 <context context-type="linenumber">115</context>
7509 </context-group>
7510 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7511 <source>Daily quota</source><target state="new">Daily quota</target>
7512 <context-group purpose="location">
7513 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7514 <context context-type="linenumber">120</context>
7515 </context-group>
7516 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7517 <source>Last login</source><target state="new">Last login</target>
7518 <context-group purpose="location">
7519 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7520 <context context-type="linenumber">122</context>
7521 </context-group>
7477 </trans-unit> 7522 </trans-unit>
7478 <trans-unit id="3403978719736970622" datatype="html"> 7523 <trans-unit id="3403978719736970622" datatype="html">
7479 <source>You cannot ban root.</source> 7524 <source>You cannot ban root.</source>
@@ -7772,13 +7817,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7772 <trans-unit id="1137937154872046253" datatype="html"> 7817 <trans-unit id="1137937154872046253" datatype="html">
7773 <source>Video channel <x id="PH"/> created.</source> 7818 <source>Video channel <x id="PH"/> created.</source>
7774 <target state="translated">Creada a canle de vídeo <x id="PH"/>.</target> 7819 <target state="translated">Creada a canle de vídeo <x id="PH"/>.</target>
7775 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 7820
7776 </trans-unit> 7821 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7777 <trans-unit id="8723777130353305761" datatype="html"> 7822 <trans-unit id="8723777130353305761" datatype="html">
7778 <source>This name already exists on this instance.</source> 7823 <source>This name already exists on this instance.</source>
7779 <target state="translated">Este nome xa existe nesta instancia.</target> 7824 <target state="translated">Este nome xa existe nesta instancia.</target>
7780 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 7825
7781 </trans-unit> 7826 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7782 <trans-unit id="7589345916094713536" datatype="html"> 7827 <trans-unit id="7589345916094713536" datatype="html">
7783 <source>Video channel <x id="PH"/> updated.</source> 7828 <source>Video channel <x id="PH"/> updated.</source>
7784 <target state="translated">Actualizada a canle de vídeo <x id="PH"/>.</target> 7829 <target state="translated">Actualizada a canle de vídeo <x id="PH"/>.</target>
@@ -7799,11 +7844,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7799 <target state="translated">Cabeceira eliminada.</target> 7844 <target state="translated">Cabeceira eliminada.</target>
7800 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 7845 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
7801 </trans-unit> 7846 </trans-unit>
7802 <trans-unit id="2575302837003821736" datatype="html"> 7847
7803 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7804 <target state="translated">Escribe o nome a mostrar para a canle de vídeo (<x id="PH"/>) para confirmar</target>
7805 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
7806 </trans-unit>
7807 <trans-unit id="624066830180032195" datatype="html"> 7848 <trans-unit id="624066830180032195" datatype="html">
7808 <source>Video channel <x id="PH"/> deleted.</source> 7849 <source>Video channel <x id="PH"/> deleted.</source>
7809 <target state="translated">Eliminada a canle de vídeo <x id="PH"/>.</target> 7850 <target state="translated">Eliminada a canle de vídeo <x id="PH"/>.</target>
@@ -7951,6 +7992,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7951 <source>Ownership change request sent.</source> 7992 <source>Ownership change request sent.</source>
7952 <target state="translated">Enviouse a solicitude de cambio de propiedade.</target> 7993 <target state="translated">Enviouse a solicitude de cambio de propiedade.</target>
7953 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 7994 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
7995 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
7996 <source>Sort by</source><target state="new">Sort by</target>
7997 <context-group purpose="location">
7998 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7999 <context context-type="linenumber">26</context>
8000 </context-group>
7954 </trans-unit> 8001 </trans-unit>
7955 <trans-unit id="3245220240937722814" datatype="html"> 8002 <trans-unit id="3245220240937722814" datatype="html">
7956 <source>My channels</source> 8003 <source>My channels</source>
@@ -8049,7 +8096,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8049 <target state="translated">Subscribirse á conta</target> 8096 <target state="translated">Subscribirse á conta</target>
8050 8097
8051 8098
8052 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8099 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8053 <trans-unit id="3131904093925601441" datatype="html"> 8100 <trans-unit id="3131904093925601441" datatype="html">
8054 <source>PLAYLISTS</source> 8101 <source>PLAYLISTS</source>
8055 <target state="translated">LISTAXES</target> 8102 <target state="translated">LISTAXES</target>
@@ -8096,34 +8143,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8096 <trans-unit id="3779524668013120370" datatype="html"> 8143 <trans-unit id="3779524668013120370" datatype="html">
8097 <source>Go to my subscriptions</source> 8144 <source>Go to my subscriptions</source>
8098 <target state="translated">Ir ás miñas subscricións</target> 8145 <target state="translated">Ir ás miñas subscricións</target>
8099 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8146
8100 </trans-unit> 8147 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8101 <trans-unit id="1136469849928650779" datatype="html"> 8148 <trans-unit id="1136469849928650779" datatype="html">
8102 <source>Go to my videos</source> 8149 <source>Go to my videos</source>
8103 <target state="translated">Ir ós meus vídeos</target> 8150 <target state="translated">Ir ós meus vídeos</target>
8104 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8151
8105 </trans-unit> 8152 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8106 <trans-unit id="7836683738999600376" datatype="html"> 8153 <trans-unit id="7836683738999600376" datatype="html">
8107 <source>Go to my imports</source> 8154 <source>Go to my imports</source>
8108 <target state="translated">Ir ás importacións</target> 8155 <target state="translated">Ir ás importacións</target>
8109 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8156
8110 </trans-unit> 8157 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8111 <trans-unit id="7511292153332773503" datatype="html"> 8158 <trans-unit id="7511292153332773503" datatype="html">
8112 <source>Go to my channels</source> 8159 <source>Go to my channels</source>
8113 <target state="translated">Ir ás miñas canles</target> 8160 <target state="translated">Ir ás miñas canles</target>
8114 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8161
8115 </trans-unit> 8162 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8116 <trans-unit id="2013324644839511073" datatype="html"> 8163 <trans-unit id="2013324644839511073" datatype="html">
8117 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8164 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8118Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8165Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8119 <target state="translated">Non se poden obter as credenciais OAuth Client: <x id="PH" equiv-text="error.text"/>. Asegúrate de ter configurado correctamente PeerTube (config/ directory), en particular a sección "webserver".</target> 8166 <target state="translated">Non se poden obter as credenciais OAuth Client: <x id="PH" equiv-text="error.text"/>. Asegúrate de ter configurado correctamente PeerTube (config/ directory), en particular a sección "webserver".</target>
8120 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8167
8121 </trans-unit> 8168 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8122 <trans-unit id="375263728166936544" datatype="html"> 8169 <trans-unit id="375263728166936544" datatype="html">
8123 <source>You need to reconnect.</source> 8170 <source>You need to reconnect.</source>
8124 <target state="translated">Tes que reconectar.</target> 8171 <target state="translated">Tes que reconectar.</target>
8125 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8172
8126 </trans-unit> 8173 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8127 <trans-unit id="2206638022166154361" datatype="html"> 8174 <trans-unit id="2206638022166154361" datatype="html">
8128 <source>Keyboard Shortcuts:</source> 8175 <source>Keyboard Shortcuts:</source>
8129 <target state="translated">Atallos de teclado:</target> 8176 <target state="translated">Atallos de teclado:</target>
@@ -8136,6 +8183,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8136 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8183 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8137 <context context-type="linenumber">98</context> 8184 <context context-type="linenumber">98</context>
8138 </context-group> 8185 </context-group>
8186 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8187 <source>In my library</source><target state="new">In my library</target>
8188 <context-group purpose="location">
8189 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8190 <context context-type="linenumber">104</context>
8191 </context-group>
8139 </trans-unit> 8192 </trans-unit>
8140 <trans-unit id="232050922346936574" datatype="html"> 8193 <trans-unit id="232050922346936574" datatype="html">
8141 <source>Trending</source> 8194 <source>Trending</source>
@@ -8164,38 +8217,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8164 <trans-unit id="1266887509445371246" datatype="html"> 8217 <trans-unit id="1266887509445371246" datatype="html">
8165 <source>Incorrect username or password.</source> 8218 <source>Incorrect username or password.</source>
8166 <target state="translated">Usuaria ou contrasinal incorrectos.</target> 8219 <target state="translated">Usuaria ou contrasinal incorrectos.</target>
8167 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8220
8168 </trans-unit> 8221 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8169 <trans-unit id="6974874606619467663" datatype="html"> 8222 <trans-unit id="6974874606619467663" datatype="html">
8170 <source>Your account is blocked.</source> 8223 <source>Your account is blocked.</source>
8171 <target state="translated">A túa conta está bloqueada.</target> 8224 <target state="translated">A túa conta está bloqueada.</target>
8172 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8225
8173 </trans-unit> 8226 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8174 <trans-unit id="7939914198003891823" datatype="html"> 8227 <trans-unit id="7939914198003891823" datatype="html">
8175 <source>any language</source> 8228 <source>any language</source>
8176 <target state="translated">tódolos idiomas</target> 8229 <target state="translated">tódolos idiomas</target>
8177 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8230
8178 </trans-unit> 8231 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8179 <trans-unit id="5633144232269377096" datatype="html"> 8232 <trans-unit id="5633144232269377096" datatype="html">
8180 <source>hide</source> 8233 <source>hide</source>
8181 <target state="translated">agochar</target> 8234 <target state="translated">agochar</target>
8182 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8235
8183 </trans-unit> 8236 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8184 <trans-unit id="8603861867909474404" datatype="html"> 8237 <trans-unit id="8603861867909474404" datatype="html">
8185 <source>blur</source> 8238 <source>blur</source>
8186 <target state="translated">esborranchar</target> 8239 <target state="translated">esborranchar</target>
8187 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8240
8188 </trans-unit> 8241 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8189 <trans-unit id="4534458451100881847" datatype="html"> 8242 <trans-unit id="4534458451100881847" datatype="html">
8190 <source>display</source> 8243 <source>display</source>
8191 <target state="translated">mostrar</target> 8244 <target state="translated">mostrar</target>
8192 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8245
8193 </trans-unit> 8246 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8194 <trans-unit id="4467323362722952678" datatype="html"> 8247 <trans-unit id="4467323362722952678" datatype="html">
8195 <source>Unknown</source> 8248 <source>Unknown</source>
8196 <target state="translated">Descoñecido</target> 8249 <target state="translated">Descoñecido</target>
8197 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8250
8198 </trans-unit> 8251 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8199 <trans-unit id="8781423666414310853" datatype="html"> 8252 <trans-unit id="8781423666414310853" datatype="html">
8200 <source>Your password has been successfully reset!</source> 8253 <source>Your password has been successfully reset!</source>
8201 <target state="translated">Restableceuse correctamente o contrasinal!</target> 8254 <target state="translated">Restableceuse correctamente o contrasinal!</target>
@@ -9753,18 +9806,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9753 <trans-unit id="968295009933361070" datatype="html"> 9806 <trans-unit id="968295009933361070" datatype="html">
9754 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9807 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9755 <target state="translated">Demasiados intentos, inténtao outra vez tras <x id="PH"/> minutos.</target> 9808 <target state="translated">Demasiados intentos, inténtao outra vez tras <x id="PH"/> minutos.</target>
9756 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9809
9757 </trans-unit> 9810 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9758 <trans-unit id="4965472196059235310" datatype="html"> 9811 <trans-unit id="4965472196059235310" datatype="html">
9759 <source>Too many attempts, please try again later.</source> 9812 <source>Too many attempts, please try again later.</source>
9760 <target state="translated">Demasiados intentos, inténtao máis tarde.</target> 9813 <target state="translated">Demasiados intentos, inténtao máis tarde.</target>
9761 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9814
9762 </trans-unit> 9815 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9763 <trans-unit id="1693549688987384699" datatype="html"> 9816 <trans-unit id="1693549688987384699" datatype="html">
9764 <source>Server error. Please retry later.</source> 9817 <source>Server error. Please retry later.</source>
9765 <target state="translated">Erro do servidor. Inténtao máis tarde.</target> 9818 <target state="translated">Erro do servidor. Inténtao máis tarde.</target>
9766 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9819
9767 </trans-unit> 9820 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9768 <trans-unit id="5927402622550505067" datatype="html"> 9821 <trans-unit id="5927402622550505067" datatype="html">
9769 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9822 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9770 <target state="translated">Subscrita a tódalas canles de <x id="PH"/>. Recibirás notificación de tódolos seus vídeos.</target> 9823 <target state="translated">Subscrita a tódalas canles de <x id="PH"/>. Recibirás notificación de tódolos seus vídeos.</target>
@@ -10339,33 +10392,33 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10339 <trans-unit id="3284171506518522275" datatype="html"> 10392 <trans-unit id="3284171506518522275" datatype="html">
10340 <source>Your video was uploaded to your account and is private.</source> 10393 <source>Your video was uploaded to your account and is private.</source>
10341 <target state="translated">O vídeo subeuse á túa conta e é privado.</target> 10394 <target state="translated">O vídeo subeuse á túa conta e é privado.</target>
10342 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10395
10343 </trans-unit> 10396 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10344 <trans-unit id="5699822024600815733" datatype="html"> 10397 <trans-unit id="5699822024600815733" datatype="html">
10345 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10398 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10346 <target state="translated">Pero os datos asociados (etiquetas, descrición...) perderanse, queres saír igualmente desta páxina?</target> 10399 <target state="translated">Pero os datos asociados (etiquetas, descrición...) perderanse, queres saír igualmente desta páxina?</target>
10347 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10400
10348 </trans-unit> 10401 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10349 <trans-unit id="1219739004043110649" datatype="html"> 10402 <trans-unit id="1219739004043110649" datatype="html">
10350 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10403 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10351 <target state="translated">O vídeo aínda non se subiu, desexas realmente saír desta páxina?</target> 10404 <target state="translated">O vídeo aínda non se subiu, desexas realmente saír desta páxina?</target>
10352 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10405
10353 </trans-unit> 10406 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10354 <trans-unit id="6932865105766151309" datatype="html"> 10407 <trans-unit id="6932865105766151309" datatype="html">
10355 <source>Upload</source> 10408 <source>Upload</source>
10356 <target state="translated">Subir</target> 10409 <target state="translated">Subir</target>
10357 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10410
10358 </trans-unit> 10411 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10359 <trans-unit id="8278735427925094503" datatype="html"> 10412 <trans-unit id="8278735427925094503" datatype="html">
10360 <source>Upload <x id="PH"/> </source> 10413 <source>Upload <x id="PH"/> </source>
10361 <target state="translated">Subir <x id="PH"/> </target> 10414 <target state="translated">Subir <x id="PH"/> </target>
10362 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10415
10363 </trans-unit> 10416 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10364 <trans-unit id="5981816353437801748" datatype="html"> 10417 <trans-unit id="5981816353437801748" datatype="html">
10365 <source>Video published.</source> 10418 <source>Video published.</source>
10366 <target state="translated">Vídeo publicado.</target> 10419 <target state="translated">Vídeo publicado.</target>
10367 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10420
10368 </trans-unit> 10421 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10369 <trans-unit id="764164089183618119" datatype="html"> 10422 <trans-unit id="764164089183618119" datatype="html">
10370 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10423 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10371 <target state="translated">Tes cambios sen gardar! Se saes perderás os cambios.</target> 10424 <target state="translated">Tes cambios sen gardar! Se saes perderás os cambios.</target>
@@ -10412,28 +10465,28 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10412 <trans-unit id="961774488937452220" datatype="html"> 10465 <trans-unit id="961774488937452220" datatype="html">
10413 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10466 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10414 <target state="translated">Este vídeo non está dispoñible na túa instancia. Queres ser redirixida á instancia orixinal: &lt;a href="<x id="PH"/>"><x id="PH_1"/>/a>?</target> 10467 <target state="translated">Este vídeo non está dispoñible na túa instancia. Queres ser redirixida á instancia orixinal: &lt;a href="<x id="PH"/>"><x id="PH_1"/>/a>?</target>
10415 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group> 10468
10416 </trans-unit> 10469 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10417 <trans-unit id="5761611056224181752" datatype="html"> 10470 <trans-unit id="5761611056224181752" datatype="html">
10418 <source>Redirection</source> 10471 <source>Redirection</source>
10419 <target state="translated">Redirección</target> 10472 <target state="translated">Redirección</target>
10420 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group> 10473
10421 </trans-unit> 10474 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10422 <trans-unit id="8858527736400081688" datatype="html"> 10475 <trans-unit id="8858527736400081688" datatype="html">
10423 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10476 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10424 <target state="translated">Este vídeo contén contido explicito ou adulto. Tes certeza de querer velo?</target> 10477 <target state="translated">Este vídeo contén contido explicito ou adulto. Tes certeza de querer velo?</target>
10425 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group> 10478
10426 </trans-unit> 10479 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10427 <trans-unit id="3937119019020041049" datatype="html"> 10480 <trans-unit id="3937119019020041049" datatype="html">
10428 <source>Mature or explicit content</source> 10481 <source>Mature or explicit content</source>
10429 <target state="translated">Contido explícito ou adulto</target> 10482 <target state="translated">Contido explícito ou adulto</target>
10430 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group> 10483
10431 </trans-unit> 10484 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10432 <trans-unit id="1755474755114288376" datatype="html"> 10485 <trans-unit id="1755474755114288376" datatype="html">
10433 <source>Up Next</source> 10486 <source>Up Next</source>
10434 <target state="translated">A seguir</target> 10487 <target state="translated">A seguir</target>
10435 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group> 10488
10436 </trans-unit> 10489 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10437 <trans-unit id="2159130950882492111" datatype="html"> 10490 <trans-unit id="2159130950882492111" datatype="html">
10438 <source>Cancel</source> 10491 <source>Cancel</source>
10439 <target state="translated">Cancelar</target> 10492 <target state="translated">Cancelar</target>
@@ -10442,63 +10495,63 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10442 <trans-unit id="3354816756665089864" datatype="html"> 10495 <trans-unit id="3354816756665089864" datatype="html">
10443 <source>Autoplay is suspended</source> 10496 <source>Autoplay is suspended</source>
10444 <target state="translated">Reprodución automática suspendida</target> 10497 <target state="translated">Reprodución automática suspendida</target>
10445 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group> 10498
10446 </trans-unit> 10499 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10447 <trans-unit id="7895294730547405228" datatype="html"> 10500 <trans-unit id="7895294730547405228" datatype="html">
10448 <source>Enter/exit fullscreen (requires player focus)</source> 10501 <source>Enter/exit fullscreen (requires player focus)</source>
10449 <target state="translated">Entrar/Saír da pantalla completa (require foco no reprodutor)</target> 10502 <target state="translated">Entrar/Saír da pantalla completa (require foco no reprodutor)</target>
10450 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group> 10503
10451 </trans-unit> 10504 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10452 <trans-unit id="7618388257165864759" datatype="html"> 10505 <trans-unit id="7618388257165864759" datatype="html">
10453 <source>Play/Pause the video (requires player focus)</source> 10506 <source>Play/Pause the video (requires player focus)</source>
10454 <target state="translated">Reproducir/Pausar o vídeo (require foco no reprodutor)</target> 10507 <target state="translated">Reproducir/Pausar o vídeo (require foco no reprodutor)</target>
10455 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group> 10508
10456 </trans-unit> 10509 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10457 <trans-unit id="7761890399634216630" datatype="html"> 10510 <trans-unit id="7761890399634216630" datatype="html">
10458 <source>Mute/unmute the video (requires player focus)</source> 10511 <source>Mute/unmute the video (requires player focus)</source>
10459 <target state="translated">Acalar/Restablecer o vídeo (require foco no reprodutor)</target> 10512 <target state="translated">Acalar/Restablecer o vídeo (require foco no reprodutor)</target>
10460 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group> 10513
10461 </trans-unit> 10514 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10462 <trans-unit id="5996585232248234904" datatype="html"> 10515 <trans-unit id="5996585232248234904" datatype="html">
10463 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10516 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10464 <target state="translated">Saltar a unha porcentaxe do vídeo: 0 é 0% e 9 é 90% (require foco no reprodutor)</target> 10517 <target state="translated">Saltar a unha porcentaxe do vídeo: 0 é 0% e 9 é 90% (require foco no reprodutor)</target>
10465 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group> 10518
10466 </trans-unit> 10519 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10467 <trans-unit id="3748765405903319998" datatype="html"> 10520 <trans-unit id="3748765405903319998" datatype="html">
10468 <source>Increase the volume (requires player focus)</source> 10521 <source>Increase the volume (requires player focus)</source>
10469 <target state="translated">Aumentar volume (require foco no reprodutor)</target> 10522 <target state="translated">Aumentar volume (require foco no reprodutor)</target>
10470 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group> 10523
10471 </trans-unit> 10524 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10472 <trans-unit id="5810704036407159982" datatype="html"> 10525 <trans-unit id="5810704036407159982" datatype="html">
10473 <source>Decrease the volume (requires player focus)</source> 10526 <source>Decrease the volume (requires player focus)</source>
10474 <target state="translated">Diminuír volume (require foco no reprodutor)</target> 10527 <target state="translated">Diminuír volume (require foco no reprodutor)</target>
10475 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group> 10528
10476 </trans-unit> 10529 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10477 <trans-unit id="2622048822548065691" datatype="html"> 10530 <trans-unit id="2622048822548065691" datatype="html">
10478 <source>Seek the video forward (requires player focus)</source> 10531 <source>Seek the video forward (requires player focus)</source>
10479 <target state="translated">Saltar adiante no vídeo (require foco no reprodutor)</target> 10532 <target state="translated">Saltar adiante no vídeo (require foco no reprodutor)</target>
10480 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group> 10533
10481 </trans-unit> 10534 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10482 <trans-unit id="6540078205109221153" datatype="html"> 10535 <trans-unit id="6540078205109221153" datatype="html">
10483 <source>Seek the video backward (requires player focus)</source> 10536 <source>Seek the video backward (requires player focus)</source>
10484 <target state="translated">Saltar atrás no vídeo (require foco no reprodutor)</target> 10537 <target state="translated">Saltar atrás no vídeo (require foco no reprodutor)</target>
10485 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group> 10538
10486 </trans-unit> 10539 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10487 <trans-unit id="1956491957766210808" datatype="html"> 10540 <trans-unit id="1956491957766210808" datatype="html">
10488 <source>Increase playback rate (requires player focus)</source> 10541 <source>Increase playback rate (requires player focus)</source>
10489 <target state="translated">Aumentar taxa de reprodución (require foco no reprodutor)</target> 10542 <target state="translated">Aumentar taxa de reprodución (require foco no reprodutor)</target>
10490 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group> 10543
10491 </trans-unit> 10544 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10492 <trans-unit id="5495529997674803186" datatype="html"> 10545 <trans-unit id="5495529997674803186" datatype="html">
10493 <source>Decrease playback rate (requires player focus)</source> 10546 <source>Decrease playback rate (requires player focus)</source>
10494 <target state="translated">Diminuir taxa de reprodución (require foco no reprodutor)</target> 10547 <target state="translated">Diminuir taxa de reprodución (require foco no reprodutor)</target>
10495 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group> 10548
10496 </trans-unit> 10549 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10497 <trans-unit id="3178343147230721210" datatype="html"> 10550 <trans-unit id="3178343147230721210" datatype="html">
10498 <source>Navigate in the video frame by frame (requires player focus)</source> 10551 <source>Navigate in the video frame by frame (requires player focus)</source>
10499 <target state="translated">Navegar fotograma a fotograma (require foco no reprodutor)</target> 10552 <target state="translated">Navegar fotograma a fotograma (require foco no reprodutor)</target>
10500 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group> 10553
10501 </trans-unit> 10554 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10502 <trans-unit id="8025996572234182184" datatype="html"> 10555 <trans-unit id="8025996572234182184" datatype="html">
10503 <source>Like the video</source> 10556 <source>Like the video</source>
10504 <target state="translated">Gústame o vídeo</target> 10557 <target state="translated">Gústame o vídeo</target>
diff --git a/client/src/locale/angular.hu-HU.xlf b/client/src/locale/angular.hu-HU.xlf
index 1a7e3e3e9..884240b28 100644
--- a/client/src/locale/angular.hu-HU.xlf
+++ b/client/src/locale/angular.hu-HU.xlf
@@ -163,7 +163,7 @@
163 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 163 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
164 </target> 164 </target>
165 165
166 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 166 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
167 <trans-unit id="1486537403020619891" datatype="html"> 167 <trans-unit id="1486537403020619891" datatype="html">
168 <source>My watch history</source> 168 <source>My watch history</source>
169 <target state="translated">Saját megtekintési előzmények</target> 169 <target state="translated">Saját megtekintési előzmények</target>
@@ -238,12 +238,12 @@
238 238
239 239
240 240
241 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 241 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
242 <trans-unit id="1006562256968398209" datatype="html"> 242 <trans-unit id="1006562256968398209" datatype="html">
243 <source>video</source> 243 <source>video</source>
244 <target state="translated">videó</target> 244 <target state="translated">videó</target>
245 245
246 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 246 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
247 <trans-unit id="6438815964972582865" datatype="html"> 247 <trans-unit id="6438815964972582865" datatype="html">
248 <source>The following link contains a private token and should not be shared with anyone.</source> 248 <source>The following link contains a private token and should not be shared with anyone.</source>
249 <target state="new"> The following link contains a private token and should not be shared with anyone. </target> 249 <target state="new"> The following link contains a private token and should not be shared with anyone. </target>
@@ -314,10 +314,10 @@
314 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 314 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
315 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 315 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
316 316
317 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 317 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
318 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 318 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
319 319
320 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 320 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
321 <trans-unit id="5235042777215655908" datatype="html"> 321 <trans-unit id="5235042777215655908" datatype="html">
322 <source>subtitles</source> 322 <source>subtitles</source>
323 <target state="translated">feliratok</target> 323 <target state="translated">feliratok</target>
@@ -707,10 +707,10 @@
707 <trans-unit id="2602586221576511475"> 707 <trans-unit id="2602586221576511475">
708 <source>Video quota</source> 708 <source>Video quota</source>
709 <target>Videokvóta</target> 709 <target>Videokvóta</target>
710 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 710
711 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 711
712 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 712
713 </trans-unit> 713 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
714 <trans-unit id="1502595455339510144"> 714 <trans-unit id="1502595455339510144">
715 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 715 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
716 <target>Korlátlan <x id="START_TAG_NG_CONTAINER"/>(napi <x id="INTERPOLATION"/>)<x id="CLOSE_TAG_NG-CONTAINER"/></target> 716 <target>Korlátlan <x id="START_TAG_NG_CONTAINER"/>(napi <x id="INTERPOLATION"/>)<x id="CLOSE_TAG_NG-CONTAINER"/></target>
@@ -789,7 +789,31 @@
789 <source>Federation</source> 789 <source>Federation</source>
790 <target state="translated">Föderáció</target> 790 <target state="translated">Föderáció</target>
791 791
792 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 792 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
793 <source>Following</source><target state="new">Following</target>
794 <context-group purpose="location">
795 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
796 <context context-type="linenumber">29</context>
797 </context-group>
798 <context-group purpose="location">
799 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
800 <context context-type="linenumber">31</context>
801 </context-group>
802 <context-group purpose="location">
803 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
804 <context context-type="linenumber">28</context>
805 </context-group>
806 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
807 <source>Followers</source><target state="new">Followers</target>
808 <context-group purpose="location">
809 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
810 <context context-type="linenumber">34</context>
811 </context-group>
812 <context-group purpose="location">
813 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
814 <context context-type="linenumber">37</context>
815 </context-group>
816 </trans-unit>
793 <trans-unit id="3541687134897970106" datatype="html"> 817 <trans-unit id="3541687134897970106" datatype="html">
794 <source>followers</source> 818 <source>followers</source>
795 <target state="translated">követő</target> 819 <target state="translated">követő</target>
@@ -861,7 +885,7 @@
861 885
862 886
863 887
864 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 888 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
865 <trans-unit id="3616223838716839702"> 889 <trans-unit id="3616223838716839702">
866 <source>Ban this user</source> 890 <source>Ban this user</source>
867 <target>Felhasználó kitiltása</target> 891 <target>Felhasználó kitiltása</target>
@@ -927,19 +951,13 @@
927 <trans-unit id="7252854992688790751" datatype="html"> 951 <trans-unit id="7252854992688790751" datatype="html">
928 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 952 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
929 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 953 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
930 <context-group purpose="location"> 954
931 <context context-type="sourcefile">src/app/+login/login.component.html</context> 955 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
932 <context context-type="linenumber">60,62</context>
933 </context-group>
934 </trans-unit>
935 <trans-unit id="7215649348148521605" datatype="html"> 956 <trans-unit id="7215649348148521605" datatype="html">
936 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 957 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
937 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 958 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
938 <context-group purpose="location"> 959
939 <context context-type="sourcefile">src/app/+login/login.component.html</context> 960 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
940 <context context-type="linenumber">65,67</context>
941 </context-group>
942 </trans-unit>
943 <trans-unit id="2392488717875840729"> 961 <trans-unit id="2392488717875840729">
944 <source>User</source> 962 <source>User</source>
945 <target>Felhasználó</target> 963 <target>Felhasználó</target>
@@ -950,66 +968,66 @@
950 <source>Username or email address</source> 968 <source>Username or email address</source>
951 <target>Felhasználónév vagy e-mail-cím</target> 969 <target>Felhasználónév vagy e-mail-cím</target>
952 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 970 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
971 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
972 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
973 <context-group purpose="location">
974 <context context-type="sourcefile">src/app/+login/login.component.html</context>
975 <context context-type="linenumber">33,34</context>
976 </context-group>
953 </trans-unit> 977 </trans-unit>
954 <trans-unit id="1431416938026210429"> 978 <trans-unit id="1431416938026210429">
955 <source>Password</source> 979 <source>Password</source>
956 <target>Jelszó</target> 980 <target>Jelszó</target>
957 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 981
958 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 982
959 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 983
960 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 984
961 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 985
962 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 986
963 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 987
964 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 988
965 </trans-unit> 989 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
966 <trans-unit id="8715156686857791956" datatype="html"> 990 <trans-unit id="8715156686857791956" datatype="html">
967 <source>Click here to reset your password</source> 991 <source>Click here to reset your password</source>
968 <target state="translated">Kattintson ide a jelszava visszaállításához</target> 992 <target state="translated">Kattintson ide a jelszava visszaállításához</target>
969 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 993
970 </trans-unit> 994 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
971 <trans-unit id="892063502898494584" datatype="html"> 995 <trans-unit id="892063502898494584" datatype="html">
972 <source>I forgot my password</source> 996 <source>I forgot my password</source>
973 <target state="new">I forgot my password</target> 997 <target state="new">I forgot my password</target>
974 <context-group purpose="location"> 998
975 <context context-type="sourcefile">src/app/+login/login.component.html</context> 999 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
976 <context context-type="linenumber">47</context>
977 </context-group>
978 </trans-unit>
979 <trans-unit id="2101170466365500913" datatype="html"> 1000 <trans-unit id="2101170466365500913" datatype="html">
980 <source>Logging into an account lets you publish content</source> 1001 <source>Logging into an account lets you publish content</source>
981 <target state="new"> Logging into an account lets you publish content </target> 1002 <target state="new"> Logging into an account lets you publish content </target>
982 <context-group purpose="location"> 1003
983 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1004 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
984 <context context-type="linenumber">56,57</context>
985 </context-group>
986 </trans-unit>
987 <trans-unit id="2454050363478003966"> 1005 <trans-unit id="2454050363478003966">
988 <source>Login</source> 1006 <source>Login</source>
989 <target>Bejelentkezés</target> 1007 <target>Bejelentkezés</target>
990 1008
991 1009
992 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1010 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
993 <trans-unit id="3183213940445113677" datatype="html"> 1011 <trans-unit id="3183213940445113677" datatype="html">
994 <source>Or sign in with</source> 1012 <source>Or sign in with</source>
995 <target state="translated">Vagy jelentkezzen be ezzel:</target> 1013 <target state="translated">Vagy jelentkezzen be ezzel:</target>
996 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1014
997 </trans-unit> 1015 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
998 <trans-unit id="3238209155172574367"> 1016 <trans-unit id="3238209155172574367">
999 <source>Forgot your password</source> 1017 <source>Forgot your password</source>
1000 <target>Elfelejtett jelszó</target> 1018 <target>Elfelejtett jelszó</target>
1001 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1019
1002 </trans-unit> 1020 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1003 <trans-unit id="87327320394367488" datatype="html"> 1021 <trans-unit id="87327320394367488" datatype="html">
1004 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1022 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1005 <target state="translated">Sajnáljuk, de nem tudja helyreállítani a jelszavát, mert a példány rendszergazdája nem állította be a PeerTube levelezőrendszerét.</target> 1023 <target state="translated">Sajnáljuk, de nem tudja helyreállítani a jelszavát, mert a példány rendszergazdája nem állította be a PeerTube levelezőrendszerét.</target>
1006 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1024
1007 </trans-unit> 1025 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1008 <trans-unit id="3188014010833256853" datatype="html"> 1026 <trans-unit id="3188014010833256853" datatype="html">
1009 <source>Enter your email address and we will send you a link to reset your password.</source> 1027 <source>Enter your email address and we will send you a link to reset your password.</source>
1010 <target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1028 <target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1011 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1029
1012 </trans-unit> 1030 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1013 <trans-unit id="1190256911880544559" datatype="html"> 1031 <trans-unit id="1190256911880544559" datatype="html">
1014 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1032 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1015The link will expire within 1 hour.</source> 1033The link will expire within 1 hour.</source>
@@ -1019,26 +1037,26 @@ The link will expire within 1 hour.</source>
1019 <trans-unit id="4768749765465246664"> 1037 <trans-unit id="4768749765465246664">
1020 <source>Email</source> 1038 <source>Email</source>
1021 <target>E-mail</target> 1039 <target>E-mail</target>
1022 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1040
1023 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1041
1024 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1042
1025 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1043
1026 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1044
1027 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1045
1028 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1046
1029 </trans-unit> 1047 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1030 <trans-unit id="3967269098753656610"> 1048 <trans-unit id="3967269098753656610">
1031 <source>Email address</source> 1049 <source>Email address</source>
1032 <target>E-mail-cím</target> 1050 <target>E-mail-cím</target>
1033 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1051
1034 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1052
1035 </trans-unit> 1053 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1036 <trans-unit id="7808756054397155068" datatype="html"> 1054 <trans-unit id="7808756054397155068" datatype="html">
1037 <source>Reset</source> 1055 <source>Reset</source>
1038 <target state="new">Reset</target> 1056 <target state="new">Reset</target>
1039 <note priority="1" from="description">Password reset button</note> 1057 <note priority="1" from="description">Password reset button</note>
1040 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1058
1041 </trans-unit> 1059 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1042 <trans-unit id="4319634264526091601" datatype="html"> 1060 <trans-unit id="4319634264526091601" datatype="html">
1043 <source>on this instance</source> 1061 <source>on this instance</source>
1044 <target state="translated">ezen a példányon</target> 1062 <target state="translated">ezen a példányon</target>
@@ -1364,7 +1382,7 @@ The link will expire within 1 hour.</source>
1364 <target>Fiók létrehozása</target> 1382 <target>Fiók létrehozása</target>
1365 1383
1366 1384
1367 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1385 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1368 1386
1369 <trans-unit id="3058024914967508975" datatype="html"> 1387 <trans-unit id="3058024914967508975" datatype="html">
1370 <source>My videos</source> 1388 <source>My videos</source>
@@ -1421,7 +1439,7 @@ The link will expire within 1 hour.</source>
1421 <source>VIDEOS</source> 1439 <source>VIDEOS</source>
1422 <target state="translated">VIDEÓK</target> 1440 <target state="translated">VIDEÓK</target>
1423 1441
1424 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 1442 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1425 <trans-unit id="667372110624203230" datatype="html"> 1443 <trans-unit id="667372110624203230" datatype="html">
1426 <source>Import jobs concurrency</source> 1444 <source>Import jobs concurrency</source>
1427 <target state="new">Import jobs concurrency</target> 1445 <target state="new">Import jobs concurrency</target>
@@ -1500,7 +1518,7 @@ The link will expire within 1 hour.</source>
1500 <source>I'm a teapot</source> 1518 <source>I'm a teapot</source>
1501 <target state="new">I'm a teapot</target> 1519 <target state="new">I'm a teapot</target>
1502 1520
1503 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit> 1521 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1504 <trans-unit id="1597262876035959248" datatype="html"> 1522 <trans-unit id="1597262876035959248" datatype="html">
1505 <source>That's an error.</source> 1523 <source>That's an error.</source>
1506 <target state="new">That's an error.</target> 1524 <target state="new">That's an error.</target>
@@ -1593,8 +1611,8 @@ The link will expire within 1 hour.</source>
1593 <trans-unit id="2971365540217107489" datatype="html"> 1611 <trans-unit id="2971365540217107489" datatype="html">
1594 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1612 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1595 <target state="translated">A média túl nagy ehhez a kiszolgálóhoz. Lépjen kapcsolatba a rendszergazdával, ha növelni szeretné a méretkorlátot.</target> 1613 <target state="translated">A média túl nagy ehhez a kiszolgálóhoz. Lépjen kapcsolatba a rendszergazdával, ha növelni szeretné a méretkorlátot.</target>
1596 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1614
1597 </trans-unit> 1615 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1598 1616
1599 <trans-unit id="5131854469652959713" datatype="html"> 1617 <trans-unit id="5131854469652959713" datatype="html">
1600 <source>GLOBAL SEARCH</source> 1618 <source>GLOBAL SEARCH</source>
@@ -2299,7 +2317,7 @@ The link will expire within 1 hour.</source>
2299 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2317 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2300 <source>Upload on hold</source><target state="new">Upload on hold</target> 2318 <source>Upload on hold</source><target state="new">Upload on hold</target>
2301 2319
2302 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2320 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2303 <trans-unit id="285180972645018518" datatype="html"> 2321 <trans-unit id="285180972645018518" datatype="html">
2304 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2322 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2305 <target state="translated">Sajnáljuk, a feltöltési funkció tiltott a fiókjában. Ha videókat akar hozzáadni, akkor egy rendszergazdának fel kell oldania a kvótája zárolását.</target> 2323 <target state="translated">Sajnáljuk, a feltöltési funkció tiltott a fiókjában. Ha videókat akar hozzáadni, akkor egy rendszergazdának fel kell oldania a kvótája zárolását.</target>
@@ -2979,11 +2997,7 @@ The link will expire within 1 hour.</source>
2979 <target>Azonosító</target> 2997 <target>Azonosító</target>
2980 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 2998 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
2981 </trans-unit> 2999 </trans-unit>
2982 <trans-unit id="2265605798180116441" datatype="html"> 3000
2983 <source>Follower handle</source>
2984 <target state="translated">Követőkezelő</target>
2985 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
2986 </trans-unit>
2987 <trans-unit id="5911214550882917183"> 3001 <trans-unit id="5911214550882917183">
2988 <source>State</source> 3002 <source>State</source>
2989 <target>Állapot</target> 3003 <target>Állapot</target>
@@ -3051,11 +3065,7 @@ The link will expire within 1 hour.</source>
3051 </target> 3065 </target>
3052 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3066 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3053 </trans-unit> 3067 </trans-unit>
3054 <trans-unit id="6641024648411549335" datatype="html"> 3068
3055 <source>Host</source>
3056 <target state="translated">Gép</target>
3057 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3058 </trans-unit>
3059 <trans-unit id="6571718060636962350" datatype="html"> 3069 <trans-unit id="6571718060636962350" datatype="html">
3060 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3070 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3061 <target state="translated">Redundancia megengedett <x id="START_TAG_P-SORTICON"/><x id="CLOSE_TAG_P-SORTICON"/></target> 3071 <target state="translated">Redundancia megengedett <x id="START_TAG_P-SORTICON"/><x id="CLOSE_TAG_P-SORTICON"/></target>
@@ -3065,7 +3075,7 @@ The link will expire within 1 hour.</source>
3065 <source>Unfollow</source> 3075 <source>Unfollow</source>
3066 <target state="new">Unfollow</target> 3076 <target state="new">Unfollow</target>
3067 3077
3068 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3078 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3069 <trans-unit id="8246779176913476983" datatype="html"> 3079 <trans-unit id="8246779176913476983" datatype="html">
3070 <source>Open instance in a new tab</source> 3080 <source>Open instance in a new tab</source>
3071 <target state="translated">Példány megnyitása új lapon</target> 3081 <target state="translated">Példány megnyitása új lapon</target>
@@ -3076,28 +3086,20 @@ The link will expire within 1 hour.</source>
3076 <trans-unit id="9132918641931433659" datatype="html"> 3086 <trans-unit id="9132918641931433659" datatype="html">
3077 <source>No host found matching current filters.</source> 3087 <source>No host found matching current filters.</source>
3078 <target state="translated">Nem található a jelenlegi szűrőkre illeszkedő gép.</target> 3088 <target state="translated">Nem található a jelenlegi szűrőkre illeszkedő gép.</target>
3079 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3089
3080 </trans-unit> 3090 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3081 <trans-unit id="7274241885665071790" datatype="html"> 3091 <trans-unit id="7274241885665071790" datatype="html">
3082 <source>Your instance is not following anyone.</source> 3092 <source>Your instance is not following anyone.</source>
3083 <target state="translated">Az Ön példánya nem követ senkit sem.</target> 3093 <target state="translated">Az Ön példánya nem követ senkit sem.</target>
3084 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3094
3085 </trans-unit> 3095 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3086 <trans-unit id="4774348799569692380" datatype="html"> 3096 <trans-unit id="4774348799569692380" datatype="html">
3087 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3097 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3088 <target state="translated"><x id="INTERPOLATION"/> – <x id="INTERPOLATION_1"/> / <x id="INTERPOLATION_2"/> gép megjelenítése</target> 3098 <target state="translated"><x id="INTERPOLATION"/> – <x id="INTERPOLATION_1"/> / <x id="INTERPOLATION_2"/> gép megjelenítése</target>
3089 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3099 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3090 </trans-unit> 3100 </trans-unit>
3091 <trans-unit id="6275803119759621687" datatype="html"> 3101
3092 <source>Follow domains</source> 3102
3093 <target state="translated">Tartományok követése</target>
3094 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3095 </trans-unit>
3096 <trans-unit id="1268699198448750610" datatype="html">
3097 <source>Follow instances</source>
3098 <target state="new">Follow instances</target>
3099 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3100 </trans-unit>
3101 <trans-unit id="9216117865911519658" datatype="html"> 3103 <trans-unit id="9216117865911519658" datatype="html">
3102 <source>Action</source> 3104 <source>Action</source>
3103 <target state="new">Action</target> 3105 <target state="new">Action</target>
@@ -3146,11 +3148,11 @@ The link will expire within 1 hour.</source>
3146 <trans-unit id="5248717555542428023"> 3148 <trans-unit id="5248717555542428023">
3147 <source>Username</source> 3149 <source>Username</source>
3148 <target>Felhasználónév</target> 3150 <target>Felhasználónév</target>
3149 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3151
3150 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3152
3151 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3153
3152 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3154
3153 </trans-unit> 3155 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3154 <trans-unit id="5428411040014095392" datatype="html"> 3156 <trans-unit id="5428411040014095392" datatype="html">
3155 <source>e.g. jane_doe</source> 3157 <source>e.g. jane_doe</source>
3156 <target state="new">e.g. jane_doe</target> 3158 <target state="new">e.g. jane_doe</target>
@@ -3178,9 +3180,9 @@ The link will expire within 1 hour.</source>
3178 <trans-unit id="4145496584631696119"> 3180 <trans-unit id="4145496584631696119">
3179 <source>Role</source> 3181 <source>Role</source>
3180 <target>Szerep</target> 3182 <target>Szerep</target>
3181 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3183
3182 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3184
3183 </trans-unit> 3185 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3184 <trans-unit id="7046347992315328430" datatype="html"> 3186 <trans-unit id="7046347992315328430" datatype="html">
3185 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3187 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3186 <target state="translated">Az átkódolás engedélyezve van. A videokvóta csak az <x id="START_TAG_STRONG"/>eredeti <x id="CLOSE_TAG_STRONG"/> videó méretét veszi figyelembe. <x id="LINE_BREAK"/> Ez a felhasználó legfeljebb ~ <x id="INTERPOLATION"/>-ot tölthet fel. </target> 3188 <target state="translated">Az átkódolás engedélyezve van. A videokvóta csak az <x id="START_TAG_STRONG"/>eredeti <x id="CLOSE_TAG_STRONG"/> videó méretét veszi figyelembe. <x id="LINE_BREAK"/> Ez a felhasználó legfeljebb ~ <x id="INTERPOLATION"/>-ot tölthet fel. </target>
@@ -3197,15 +3199,9 @@ The link will expire within 1 hour.</source>
3197 <trans-unit id="2622255144026150901" datatype="html"> 3199 <trans-unit id="2622255144026150901" datatype="html">
3198 <source>Auth plugin</source> 3200 <source>Auth plugin</source>
3199 <target state="new">Auth plugin</target> 3201 <target state="new">Auth plugin</target>
3200 <context-group purpose="location"> 3202
3201 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3203
3202 <context context-type="linenumber">188</context> 3204 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3203 </context-group>
3204 <context-group purpose="location">
3205 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3206 <context context-type="linenumber">188</context>
3207 </context-group>
3208 </trans-unit>
3209 <trans-unit id="588099657508661970" datatype="html"> 3205 <trans-unit id="588099657508661970" datatype="html">
3210 <source>None (local authentication)</source> 3206 <source>None (local authentication)</source>
3211 <target state="new">None (local authentication)</target> 3207 <target state="new">None (local authentication)</target>
@@ -3445,6 +3441,12 @@ The link will expire within 1 hour.</source>
3445 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3441 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3446 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3442 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3447 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3443 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3444 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3445 <source>Follower</source><target state="new">Follower</target>
3446 <context-group purpose="location">
3447 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3448 <context context-type="linenumber">24</context>
3449 </context-group>
3448 </trans-unit> 3450 </trans-unit>
3449 <trans-unit id="4691552465058437520" datatype="html"> 3451 <trans-unit id="4691552465058437520" datatype="html">
3450 <source>Commented video</source> 3452 <source>Commented video</source>
@@ -3741,8 +3743,8 @@ The link will expire within 1 hour.</source>
3741 <trans-unit id="4917252294930256268" datatype="html"> 3743 <trans-unit id="4917252294930256268" datatype="html">
3742 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3744 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3743 <target state="translated">Úgy tűnik, hogy nem HTTPS kiszolgálón van. A TLS-nek bekapcsolva kell lennie a webkiszolgálóján, hogy kiszolgálókat követhessen.</target> 3745 <target state="translated">Úgy tűnik, hogy nem HTTPS kiszolgálón van. A TLS-nek bekapcsolva kell lennie a webkiszolgálóján, hogy kiszolgálókat követhessen.</target>
3744 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 3746
3745 </trans-unit> 3747 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3746 <trans-unit id="4058814854824495833" datatype="html"> 3748 <trans-unit id="4058814854824495833" datatype="html">
3747 <source>Mute domains</source> 3749 <source>Mute domains</source>
3748 <target state="translated">Tartományok némítása</target> 3750 <target state="translated">Tartományok némítása</target>
@@ -5680,11 +5682,8 @@ color: red;
5680 <trans-unit id="5512878593724620692" datatype="html"> 5682 <trans-unit id="5512878593724620692" datatype="html">
5681 <source>CHANNELS</source> 5683 <source>CHANNELS</source>
5682 <target state="new">CHANNELS</target> 5684 <target state="new">CHANNELS</target>
5683 <context-group purpose="location"> 5685
5684 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5686 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5685 <context context-type="linenumber">82</context>
5686 </context-group>
5687 </trans-unit>
5688 <trans-unit id="3666829335406793239" datatype="html"> 5687 <trans-unit id="3666829335406793239" datatype="html">
5689 <source>This account does not have channels.</source> 5688 <source>This account does not have channels.</source>
5690 <target state="translated">Ennek a fióknak nincsenek csatornái.</target> 5689 <target state="translated">Ennek a fióknak nincsenek csatornái.</target>
@@ -5723,6 +5722,12 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
5723channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 5722channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
5724 <target state="translated">Biztos, hogy törli a(z) <x id="PH" equiv-text="videoChannel.displayName"/> csatornát? Ez törli a csatornára feltöltött <x id="PH_1" equiv-text="videoChannel.videosCount"/> videót, és nem hozhat létre még egy csatornát ugyanezzel a névvel (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target> 5723 <target state="translated">Biztos, hogy törli a(z) <x id="PH" equiv-text="videoChannel.displayName"/> csatornát? Ez törli a csatornára feltöltött <x id="PH_1" equiv-text="videoChannel.videosCount"/> videót, és nem hozhat létre még egy csatornát ugyanezzel a névvel (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target>
5725 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 5724 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
5725 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
5726 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5727 <context-group purpose="location">
5728 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5729 <context context-type="linenumber">48</context>
5730 </context-group>
5726 </trans-unit> 5731 </trans-unit>
5727 <trans-unit id="5387007581996837469" datatype="html"> 5732 <trans-unit id="5387007581996837469" datatype="html">
5728 <source>My Channels</source> 5733 <source>My Channels</source>
@@ -6236,12 +6241,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6236 <source>Your message has been sent.</source> 6241 <source>Your message has been sent.</source>
6237 <target>Az üzenete el lett küldve.</target> 6242 <target>Az üzenete el lett küldve.</target>
6238 6243
6239 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6244 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6240 <trans-unit id="2072135752262464360"> 6245 <trans-unit id="2072135752262464360">
6241 <source>You already sent this form recently</source> 6246 <source>You already sent this form recently</source>
6242 <target>Már nemrég elküldte ezt az űrlapot</target> 6247 <target>Már nemrég elküldte ezt az űrlapot</target>
6243 6248
6244 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6249 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6245 <trans-unit id="819067926858619041" datatype="html"> 6250 <trans-unit id="819067926858619041" datatype="html">
6246 <source>Account videos</source> 6251 <source>Account videos</source>
6247 <target state="translated">Fiók videói</target> 6252 <target state="translated">Fiók videói</target>
@@ -6285,13 +6290,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6285 <target state="translated"> 6290 <target state="translated">
6286 <x id="PH"/> közvetlen fiókkövető 6291 <x id="PH"/> közvetlen fiókkövető
6287 </target> 6292 </target>
6288 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6293
6289 </trans-unit> 6294 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6290 <trans-unit id="6250999352462648289" datatype="html"> 6295 <trans-unit id="6250999352462648289" datatype="html">
6291 <source>Report this account</source> 6296 <source>Report this account</source>
6292 <target state="translated">Fiók jelentése</target> 6297 <target state="translated">Fiók jelentése</target>
6293 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6298
6294 </trans-unit> 6299 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6295 <trans-unit id="1504521795586863905" datatype="html"> 6300 <trans-unit id="1504521795586863905" datatype="html">
6296 <source>VIDEOS</source> 6301 <source>VIDEOS</source>
6297 <target state="translated">VIDEÓK</target> 6302 <target state="translated">VIDEÓK</target>
@@ -6301,29 +6306,21 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6301 <trans-unit id="25349740244798533" datatype="html"> 6306 <trans-unit id="25349740244798533" datatype="html">
6302 <source>Username copied</source> 6307 <source>Username copied</source>
6303 <target state="translated">Felhasználónév lemásolva</target> 6308 <target state="translated">Felhasználónév lemásolva</target>
6304 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6309
6305 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6310
6306 </trans-unit> 6311 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6307 <trans-unit id="9221735175659318025" datatype="html"> 6312 <trans-unit id="9221735175659318025" datatype="html">
6308 <source>1 subscriber</source> 6313 <source>1 subscriber</source>
6309 <target state="translated">1 feliratkozó</target> 6314 <target state="translated">1 feliratkozó</target>
6310 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6315
6311 </trans-unit> 6316 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6312 <trans-unit id="4097331874769079975" datatype="html"> 6317 <trans-unit id="4097331874769079975" datatype="html">
6313 <source><x id="PH"/> subscribers</source> 6318 <source><x id="PH"/> subscribers</source>
6314 <target state="translated"><x id="PH"/> feliratkozó</target> 6319 <target state="translated"><x id="PH"/> feliratkozó</target>
6315 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group>
6316 </trans-unit>
6317 <trans-unit id="4682675125751819107" datatype="html">
6318 <source>Instances you follow</source>
6319 <target state="translated">Példányok, amiket követ</target>
6320
6321 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6322 <trans-unit id="8899833753704589712" datatype="html">
6323 <source>Instances following you</source>
6324 <target state="translated">Példányok, amik követik</target>
6325 6320
6326 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit> 6321 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6322
6323
6327 <trans-unit id="1035838766454786107" datatype="html"> 6324 <trans-unit id="1035838766454786107" datatype="html">
6328 <source>Audio-only</source> 6325 <source>Audio-only</source>
6329 <target state="translated">Csak hang</target> 6326 <target state="translated">Csak hang</target>
@@ -6373,6 +6370,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6373 <source>Auto (via ffmpeg)</source> 6370 <source>Auto (via ffmpeg)</source>
6374 <target>Automatikus (ffmpeg által)</target> 6371 <target>Automatikus (ffmpeg által)</target>
6375 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6372 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6373 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6374 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6375 <context-group purpose="location">
6376 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6377 <context context-type="linenumber">3</context>
6378 </context-group>
6376 </trans-unit> 6379 </trans-unit>
6377 <trans-unit id="931255636742351800" datatype="html"> 6380 <trans-unit id="931255636742351800" datatype="html">
6378 <source>No limit</source> 6381 <source>No limit</source>
@@ -6513,18 +6516,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6513 <trans-unit id="2127446333083057097" datatype="html"> 6516 <trans-unit id="2127446333083057097" datatype="html">
6514 <source>Domain is required.</source> 6517 <source>Domain is required.</source>
6515 <target state="translated">Tartomány szükséges.</target> 6518 <target state="translated">Tartomány szükséges.</target>
6516 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6519
6517 </trans-unit> 6520 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6518 <trans-unit id="6780793142903080663" datatype="html"> 6521 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6519 <source>Domains entered are invalid.</source> 6522 <context-group purpose="location">
6520 <target state="translated">A megadott tartományok érvénytelenek.</target> 6523 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6521 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6524 <context context-type="linenumber">93</context>
6522 </trans-unit> 6525 </context-group>
6523 <trans-unit id="5886492514458202177" datatype="html"> 6526 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6524 <source>Domains entered contain duplicates.</source> 6527 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6525 <target state="translated">A megadott tartományok kettőzéseket tartalmaznak.</target> 6528 <context-group purpose="location">
6526 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6529 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6530 <context context-type="linenumber">94</context>
6531 </context-group>
6532 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6533 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6534 <context-group purpose="location">
6535 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6536 <context context-type="linenumber">102</context>
6537 </context-group>
6538 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6539 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6540 <context-group purpose="location">
6541 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6542 <context context-type="linenumber">103</context>
6543 </context-group>
6527 </trans-unit> 6544 </trans-unit>
6545
6546
6528 <trans-unit id="240806681889331244" datatype="html"> 6547 <trans-unit id="240806681889331244" datatype="html">
6529 <source>Unlimited</source> 6548 <source>Unlimited</source>
6530 <target state="translated">Korlátlan</target> 6549 <target state="translated">Korlátlan</target>
@@ -6684,24 +6703,50 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6684 <x id="PH"/> eltávolítva a példány követőiből 6703 <x id="PH"/> eltávolítva a példány követőiből
6685 </target> 6704 </target>
6686 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6705 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6706 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6707 <source>Follow</source><target state="new">Follow</target>
6708 <context-group purpose="location">
6709 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6710 <context context-type="linenumber">3</context>
6711 </context-group>
6712 <context-group purpose="location">
6713 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6714 <context context-type="linenumber">37</context>
6715 </context-group>
6716 <context-group purpose="location">
6717 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6718 <context context-type="linenumber">18</context>
6719 </context-group>
6720 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6721 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6722 <context-group purpose="location">
6723 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6724 <context context-type="linenumber">11</context>
6725 </context-group>
6687 </trans-unit> 6726 </trans-unit>
6688 <trans-unit id="2740793005745065895" datatype="html"> 6727 <trans-unit id="2740793005745065895" datatype="html">
6689 <source><x id="PH"/> is not valid </source> 6728 <source><x id="PH"/> is not valid </source>
6690 <target state="translated"> 6729 <target state="translated">
6691 <x id="PH"/> nem érvényes 6730 <x id="PH"/> nem érvényes
6692 </target> 6731 </target>
6693 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6732
6694 </trans-unit> 6733 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6695 <trans-unit id="2355066641781598196" datatype="html"> 6734 <trans-unit id="2355066641781598196" datatype="html">
6696 <source>Follow request(s) sent!</source> 6735 <source>Follow request(s) sent!</source>
6697 <target state="translated">Követési kérések elküldve!</target> 6736 <target state="translated">Követési kérések elküldve!</target>
6698 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6737
6738 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6739 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6740 <context-group purpose="location">
6741 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6742 <context context-type="linenumber">3</context>
6743 </context-group>
6699 </trans-unit> 6744 </trans-unit>
6700 <trans-unit id="4245720728052819482" datatype="html"> 6745 <trans-unit id="4245720728052819482" datatype="html">
6701 <source>Do you really want to unfollow <x id="PH"/>?</source> 6746 <source>Do you really want to unfollow <x id="PH"/>?</source>
6702 <target state="translated">Biztos, hogy megszünteti a(z) <x id="PH"/> követését?</target> 6747 <target state="translated">Biztos, hogy megszünteti a(z) <x id="PH"/> követését?</target>
6703 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6748
6704 </trans-unit> 6749 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6705 <trans-unit id="9160510009013134726" datatype="html"> 6750 <trans-unit id="9160510009013134726" datatype="html">
6706 <source>Unfollow</source> 6751 <source>Unfollow</source>
6707 <target state="translated">Követés megszüntetése</target> 6752 <target state="translated">Követés megszüntetése</target>
@@ -6710,8 +6755,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6710 <trans-unit id="3935234189109112926" datatype="html"> 6755 <trans-unit id="3935234189109112926" datatype="html">
6711 <source>You are not following <x id="PH"/> anymore.</source> 6756 <source>You are not following <x id="PH"/> anymore.</source>
6712 <target state="translated">Többé már nem követi a(z) <x id="PH"/> gépet.</target> 6757 <target state="translated">Többé már nem követi a(z) <x id="PH"/> gépet.</target>
6713 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6758
6714 </trans-unit> 6759 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6715 <trans-unit id="2593763089859685916" datatype="html"> 6760 <trans-unit id="2593763089859685916" datatype="html">
6716 <source>enabled</source> 6761 <source>enabled</source>
6717 <target state="translated">engedélyezve</target> 6762 <target state="translated">engedélyezve</target>
@@ -7183,9 +7228,9 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7183 <trans-unit id="1519954996184640001" datatype="html"> 7228 <trans-unit id="1519954996184640001" datatype="html">
7184 <source>Error</source> 7229 <source>Error</source>
7185 <target state="translated">Hiba</target> 7230 <target state="translated">Hiba</target>
7186 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7231
7187 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7232
7188 </trans-unit> 7233 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7189 <trans-unit id="5076187961693950167" datatype="html"> 7234 <trans-unit id="5076187961693950167" datatype="html">
7190 <source>Standard logs</source> 7235 <source>Standard logs</source>
7191 <target state="translated">Szabványos naplók</target> 7236 <target state="translated">Szabványos naplók</target>
@@ -7226,16 +7271,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7226 <target state="translated">Felhasználó jelszavának frissítése</target> 7271 <target state="translated">Felhasználó jelszavának frissítése</target>
7227 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7272 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7228 </trans-unit> 7273 </trans-unit>
7229 <trans-unit id="177544274549739411" datatype="html"> 7274
7230 <source>Following list</source> 7275
7231 <target state="translated">Követési lista</target>
7232 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7233 </trans-unit>
7234 <trans-unit id="8092429110007204784" datatype="html">
7235 <source>Followers list</source>
7236 <target state="translated">Követők listája</target>
7237 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7238 </trans-unit>
7239 <trans-unit id="780323526182667308" datatype="html"> 7276 <trans-unit id="780323526182667308" datatype="html">
7240 <source>User <x id="PH"/> updated.</source> 7277 <source>User <x id="PH"/> updated.</source>
7241 <target state="translated"><x id="PH"/> felhasználó frissítve.</target> 7278 <target state="translated"><x id="PH"/> felhasználó frissítve.</target>
@@ -7271,16 +7308,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7271 <target state="translated">Föderáció</target> 7308 <target state="translated">Föderáció</target>
7272 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7309 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7273 </trans-unit> 7310 </trans-unit>
7274 <trans-unit id="4682675125751819107" datatype="html"> 7311
7275 <source>Instances you follow</source> 7312
7276 <target state="translated">Követett példányok</target>
7277 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7278 </trans-unit>
7279 <trans-unit id="8899833753704589712" datatype="html">
7280 <source>Instances following you</source>
7281 <target state="translated">Követő példányok</target>
7282 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7283 </trans-unit>
7284 <trans-unit id="3767259920053407667" datatype="html"> 7313 <trans-unit id="3767259920053407667" datatype="html">
7285 <source>Videos will be deleted, comments will be tombstoned.</source> 7314 <source>Videos will be deleted, comments will be tombstoned.</source>
7286 <target state="translated">A videók törölve lesznek, a hozzászólások el lesznek temetve.</target> 7315 <target state="translated">A videók törölve lesznek, a hozzászólások el lesznek temetve.</target>
@@ -7311,7 +7340,25 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7311 <target state="translated">E-mail beállítása ellenőrzöttre</target> 7340 <target state="translated">E-mail beállítása ellenőrzöttre</target>
7312 7341
7313 7342
7314 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7343 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7344 <source>Created</source><target state="new">Created</target>
7345 <context-group purpose="location">
7346 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7347 <context context-type="linenumber">115</context>
7348 </context-group>
7349 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7350 <source>Daily quota</source><target state="new">Daily quota</target>
7351 <context-group purpose="location">
7352 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7353 <context context-type="linenumber">120</context>
7354 </context-group>
7355 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7356 <source>Last login</source><target state="new">Last login</target>
7357 <context-group purpose="location">
7358 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7359 <context context-type="linenumber">122</context>
7360 </context-group>
7361 </trans-unit>
7315 <trans-unit id="3403978719736970622" datatype="html"> 7362 <trans-unit id="3403978719736970622" datatype="html">
7316 <source>You cannot ban root.</source> 7363 <source>You cannot ban root.</source>
7317 <target state="translated">Nem tilthatja ki a rendszergazdát.</target> 7364 <target state="translated">Nem tilthatja ki a rendszergazdát.</target>
@@ -7616,13 +7663,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7616 <trans-unit id="1137937154872046253" datatype="html"> 7663 <trans-unit id="1137937154872046253" datatype="html">
7617 <source>Video channel <x id="PH"/> created.</source> 7664 <source>Video channel <x id="PH"/> created.</source>
7618 <target state="translated">A(z) <x id="PH"/> videócsatorna létrehozva.</target> 7665 <target state="translated">A(z) <x id="PH"/> videócsatorna létrehozva.</target>
7619 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 7666
7620 </trans-unit> 7667 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7621 <trans-unit id="8723777130353305761" datatype="html"> 7668 <trans-unit id="8723777130353305761" datatype="html">
7622 <source>This name already exists on this instance.</source> 7669 <source>This name already exists on this instance.</source>
7623 <target state="translated">Ez a név már létezik ebben a példányban.</target> 7670 <target state="translated">Ez a név már létezik ebben a példányban.</target>
7624 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 7671
7625 </trans-unit> 7672 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7626 <trans-unit id="7589345916094713536" datatype="html"> 7673 <trans-unit id="7589345916094713536" datatype="html">
7627 <source>Video channel <x id="PH"/> updated.</source> 7674 <source>Video channel <x id="PH"/> updated.</source>
7628 <target state="translated">A(z) <x id="PH"/> videocsatorna frissítve.</target> 7675 <target state="translated">A(z) <x id="PH"/> videocsatorna frissítve.</target>
@@ -7643,11 +7690,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7643 <target state="new">Banner deleted.</target> 7690 <target state="new">Banner deleted.</target>
7644 7691
7645 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7692 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7646 <trans-unit id="2575302837003821736" datatype="html"> 7693
7647 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7648 <target state="translated">Írja be a videócsatorna megjelenített nevét ( <x id="PH"/>) a megerősítéshez</target>
7649 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
7650 </trans-unit>
7651 <trans-unit id="624066830180032195" datatype="html"> 7694 <trans-unit id="624066830180032195" datatype="html">
7652 <source>Video channel <x id="PH"/> deleted.</source> 7695 <source>Video channel <x id="PH"/> deleted.</source>
7653 <target state="translated">A(z) <x id="PH"/> videócsatorna törölve.</target> 7696 <target state="translated">A(z) <x id="PH"/> videócsatorna törölve.</target>
@@ -7796,6 +7839,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7796 <source>Ownership change request sent.</source> 7839 <source>Ownership change request sent.</source>
7797 <target state="translated">Tulajdonjog-változtatási kérelem elküldve.</target> 7840 <target state="translated">Tulajdonjog-változtatási kérelem elküldve.</target>
7798 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 7841 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
7842 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
7843 <source>Sort by</source><target state="new">Sort by</target>
7844 <context-group purpose="location">
7845 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7846 <context context-type="linenumber">26</context>
7847 </context-group>
7799 </trans-unit> 7848 </trans-unit>
7800 <trans-unit id="3245220240937722814" datatype="html"> 7849 <trans-unit id="3245220240937722814" datatype="html">
7801 <source>My channels</source> 7850 <source>My channels</source>
@@ -7894,7 +7943,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7894 <target state="translated">Feliratkozás a fiókra</target> 7943 <target state="translated">Feliratkozás a fiókra</target>
7895 7944
7896 7945
7897 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 7946 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
7898 <trans-unit id="3131904093925601441" datatype="html"> 7947 <trans-unit id="3131904093925601441" datatype="html">
7899 <source>PLAYLISTS</source> 7948 <source>PLAYLISTS</source>
7900 <target state="new">PLAYLISTS</target> 7949 <target state="new">PLAYLISTS</target>
@@ -7941,34 +7990,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7941 <trans-unit id="3779524668013120370" datatype="html"> 7990 <trans-unit id="3779524668013120370" datatype="html">
7942 <source>Go to my subscriptions</source> 7991 <source>Go to my subscriptions</source>
7943 <target state="translated">Ugrás a feliratkozásaimhoz</target> 7992 <target state="translated">Ugrás a feliratkozásaimhoz</target>
7944 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 7993
7945 </trans-unit> 7994 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
7946 <trans-unit id="1136469849928650779" datatype="html"> 7995 <trans-unit id="1136469849928650779" datatype="html">
7947 <source>Go to my videos</source> 7996 <source>Go to my videos</source>
7948 <target state="translated">Ugrás a videóimhoz</target> 7997 <target state="translated">Ugrás a videóimhoz</target>
7949 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 7998
7950 </trans-unit> 7999 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
7951 <trans-unit id="7836683738999600376" datatype="html"> 8000 <trans-unit id="7836683738999600376" datatype="html">
7952 <source>Go to my imports</source> 8001 <source>Go to my imports</source>
7953 <target state="translated">Ugrás az importálásaimhoz</target> 8002 <target state="translated">Ugrás az importálásaimhoz</target>
7954 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8003
7955 </trans-unit> 8004 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
7956 <trans-unit id="7511292153332773503" datatype="html"> 8005 <trans-unit id="7511292153332773503" datatype="html">
7957 <source>Go to my channels</source> 8006 <source>Go to my channels</source>
7958 <target state="translated">Ugrás a csatornáimhoz</target> 8007 <target state="translated">Ugrás a csatornáimhoz</target>
7959 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8008
7960 </trans-unit> 8009 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
7961 <trans-unit id="2013324644839511073" datatype="html"> 8010 <trans-unit id="2013324644839511073" datatype="html">
7962 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8011 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
7963Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8012Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
7964 <target state="translated">Az OAuth kliens hitelesítő adatai nem kérhetők le: <x id="PH" equiv-text="error.text"/>. Győződjön meg róla, hogy helyesen állította be a PeerTube-ot (konfiguráció / könyvtár), különösképpen a „webserver” szakaszban.</target> 8013 <target state="translated">Az OAuth kliens hitelesítő adatai nem kérhetők le: <x id="PH" equiv-text="error.text"/>. Győződjön meg róla, hogy helyesen állította be a PeerTube-ot (konfiguráció / könyvtár), különösképpen a „webserver” szakaszban.</target>
7965 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8014
7966 </trans-unit> 8015 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
7967 <trans-unit id="375263728166936544" datatype="html"> 8016 <trans-unit id="375263728166936544" datatype="html">
7968 <source>You need to reconnect.</source> 8017 <source>You need to reconnect.</source>
7969 <target state="translated">Újra kell csatlakoznia.</target> 8018 <target state="translated">Újra kell csatlakoznia.</target>
7970 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8019
7971 </trans-unit> 8020 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
7972 <trans-unit id="2206638022166154361" datatype="html"> 8021 <trans-unit id="2206638022166154361" datatype="html">
7973 <source>Keyboard Shortcuts:</source> 8022 <source>Keyboard Shortcuts:</source>
7974 <target state="translated">Gyorsbillentyűk:</target> 8023 <target state="translated">Gyorsbillentyűk:</target>
@@ -7979,6 +8028,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7979 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8028 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7980 <context context-type="linenumber">98</context> 8029 <context context-type="linenumber">98</context>
7981 </context-group> 8030 </context-group>
8031 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8032 <source>In my library</source><target state="new">In my library</target>
8033 <context-group purpose="location">
8034 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8035 <context context-type="linenumber">104</context>
8036 </context-group>
7982 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 8037 </trans-unit><trans-unit id="232050922346936574" datatype="html">
7983 <source>Trending</source><target state="new">Trending</target> 8038 <source>Trending</source><target state="new">Trending</target>
7984 8039
@@ -8001,39 +8056,39 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8001 <trans-unit id="1266887509445371246" datatype="html"> 8056 <trans-unit id="1266887509445371246" datatype="html">
8002 <source>Incorrect username or password.</source> 8057 <source>Incorrect username or password.</source>
8003 <target state="translated">Helytelen felhasználónév vagy jelszó.</target> 8058 <target state="translated">Helytelen felhasználónév vagy jelszó.</target>
8004 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8059
8005 </trans-unit> 8060 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8006 <trans-unit id="6974874606619467663" datatype="html"> 8061 <trans-unit id="6974874606619467663" datatype="html">
8007 <source>Your account is blocked.</source> 8062 <source>Your account is blocked.</source>
8008 <target state="translated">A fiókja le van tiltva.</target> 8063 <target state="translated">A fiókja le van tiltva.</target>
8009 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8064
8010 </trans-unit> 8065 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8011 <trans-unit id="7939914198003891823" datatype="html"> 8066 <trans-unit id="7939914198003891823" datatype="html">
8012 <source>any language</source> 8067 <source>any language</source>
8013 <target state="translated">bármely nyelv</target> 8068 <target state="translated">bármely nyelv</target>
8014 8069
8015 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8070 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8016 8071
8017 <trans-unit id="5633144232269377096" datatype="html"> 8072 <trans-unit id="5633144232269377096" datatype="html">
8018 <source>hide</source> 8073 <source>hide</source>
8019 <target state="translated">elrejtés</target> 8074 <target state="translated">elrejtés</target>
8020 8075
8021 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8076 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8022 <trans-unit id="8603861867909474404" datatype="html"> 8077 <trans-unit id="8603861867909474404" datatype="html">
8023 <source>blur</source> 8078 <source>blur</source>
8024 <target state="translated">homályosítás</target> 8079 <target state="translated">homályosítás</target>
8025 8080
8026 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8081 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8027 <trans-unit id="4534458451100881847" datatype="html"> 8082 <trans-unit id="4534458451100881847" datatype="html">
8028 <source>display</source> 8083 <source>display</source>
8029 <target state="translated">megjelenítés</target> 8084 <target state="translated">megjelenítés</target>
8030 8085
8031 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8086 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8032 <trans-unit id="4467323362722952678" datatype="html"> 8087 <trans-unit id="4467323362722952678" datatype="html">
8033 <source>Unknown</source> 8088 <source>Unknown</source>
8034 <target state="translated">Ismeretlen</target> 8089 <target state="translated">Ismeretlen</target>
8035 8090
8036 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8091 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8037 <trans-unit id="8781423666414310853" datatype="html"> 8092 <trans-unit id="8781423666414310853" datatype="html">
8038 <source>Your password has been successfully reset!</source> 8093 <source>Your password has been successfully reset!</source>
8039 <target state="translated">A jelszava sikeresen vissza lett állítva!</target> 8094 <target state="translated">A jelszava sikeresen vissza lett állítva!</target>
@@ -9606,18 +9661,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9606 <trans-unit id="968295009933361070" datatype="html"> 9661 <trans-unit id="968295009933361070" datatype="html">
9607 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9662 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9608 <target state="translated">Túl sok próbálkozás. Próbálja meg újra <x id="PH"/> perc múlva.</target> 9663 <target state="translated">Túl sok próbálkozás. Próbálja meg újra <x id="PH"/> perc múlva.</target>
9609 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9664
9610 </trans-unit> 9665 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9611 <trans-unit id="4965472196059235310" datatype="html"> 9666 <trans-unit id="4965472196059235310" datatype="html">
9612 <source>Too many attempts, please try again later.</source> 9667 <source>Too many attempts, please try again later.</source>
9613 <target state="translated">Túl sok próbálkozás. Próbálja meg újra később.</target> 9668 <target state="translated">Túl sok próbálkozás. Próbálja meg újra később.</target>
9614 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9669
9615 </trans-unit> 9670 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9616 <trans-unit id="1693549688987384699" datatype="html"> 9671 <trans-unit id="1693549688987384699" datatype="html">
9617 <source>Server error. Please retry later.</source> 9672 <source>Server error. Please retry later.</source>
9618 <target state="translated">Kiszolgálóhiba. Próbálja újra később.</target> 9673 <target state="translated">Kiszolgálóhiba. Próbálja újra később.</target>
9619 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9674
9620 </trans-unit> 9675 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9621 <trans-unit id="5927402622550505067" datatype="html"> 9676 <trans-unit id="5927402622550505067" datatype="html">
9622 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9677 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9623 <target state="translated">Feliratkozva <x id="PH"/> összes jelenlegi csatornájára. Értesítést fog kapni az összes új videójukról.</target> 9678 <target state="translated">Feliratkozva <x id="PH"/> összes jelenlegi csatornájára. Értesítést fog kapni az összes új videójukról.</target>
@@ -10206,35 +10261,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10206 <source>Your video was uploaded to your account and is private.</source> 10261 <source>Your video was uploaded to your account and is private.</source>
10207 <target state="translated">A videó fel lett töltve a fiókjába, és személyes.</target> 10262 <target state="translated">A videó fel lett töltve a fiókjába, és személyes.</target>
10208 10263
10209 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10264 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10210 <trans-unit id="5699822024600815733" datatype="html"> 10265 <trans-unit id="5699822024600815733" datatype="html">
10211 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10266 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10212 <target state="translated">De a kapcsolódó adatok (címkék, leírás, …) el fognak veszni. Biztosan el szeretné hagyni ezt az oldalt?</target> 10267 <target state="translated">De a kapcsolódó adatok (címkék, leírás, …) el fognak veszni. Biztosan el szeretné hagyni ezt az oldalt?</target>
10213 10268
10214 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10269 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10215 <trans-unit id="1219739004043110649" datatype="html"> 10270 <trans-unit id="1219739004043110649" datatype="html">
10216 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10271 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10217 <target state="translated">A videó még nincs feltöltve. Biztosan el szeretné hagyni ezt az oldalt?</target> 10272 <target state="translated">A videó még nincs feltöltve. Biztosan el szeretné hagyni ezt az oldalt?</target>
10218 10273
10219 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10274 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10220 <trans-unit id="6932865105766151309" datatype="html"> 10275 <trans-unit id="6932865105766151309" datatype="html">
10221 <source>Upload</source> 10276 <source>Upload</source>
10222 <target state="translated">Feltöltés</target> 10277 <target state="translated">Feltöltés</target>
10223 10278
10224 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10279 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10225 <trans-unit id="8278735427925094503" datatype="html"> 10280 <trans-unit id="8278735427925094503" datatype="html">
10226 <source>Upload <x id="PH"/> </source> 10281 <source>Upload <x id="PH"/> </source>
10227 <target state="translated"> 10282 <target state="translated">
10228 <x id="PH"/> feltöltése 10283 <x id="PH"/> feltöltése
10229 </target> 10284 </target>
10230 10285
10231 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10286 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10232 10287
10233 <trans-unit id="5981816353437801748" datatype="html"> 10288 <trans-unit id="5981816353437801748" datatype="html">
10234 <source>Video published.</source> 10289 <source>Video published.</source>
10235 <target state="translated">Videó közzétéve.</target> 10290 <target state="translated">Videó közzétéve.</target>
10236 10291
10237 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10292 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10238 10293
10239 10294
10240 <trans-unit id="764164089183618119" datatype="html"> 10295 <trans-unit id="764164089183618119" datatype="html">
@@ -10284,27 +10339,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10284 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10339 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10285 <target state="translated">Ez a videó nem érhető el ezen a példányon. Szeretné, hogy átirányítsuk a forráspéldányhoz: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10340 <target state="translated">Ez a videó nem érhető el ezen a példányon. Szeretné, hogy átirányítsuk a forráspéldányhoz: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10286 10341
10287 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10342 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10288 <trans-unit id="5761611056224181752" datatype="html"> 10343 <trans-unit id="5761611056224181752" datatype="html">
10289 <source>Redirection</source> 10344 <source>Redirection</source>
10290 <target state="translated">Átirányítás</target> 10345 <target state="translated">Átirányítás</target>
10291 10346
10292 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10347 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10293 <trans-unit id="8858527736400081688" datatype="html"> 10348 <trans-unit id="8858527736400081688" datatype="html">
10294 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10349 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10295 <target state="translated">Ez a videó felnőtt vagy korhatáros tartalmat tartalmaz. Biztosan meg szeretné nézni?</target> 10350 <target state="translated">Ez a videó felnőtt vagy korhatáros tartalmat tartalmaz. Biztosan meg szeretné nézni?</target>
10296 10351
10297 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10352 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10298 <trans-unit id="3937119019020041049" datatype="html"> 10353 <trans-unit id="3937119019020041049" datatype="html">
10299 <source>Mature or explicit content</source> 10354 <source>Mature or explicit content</source>
10300 <target state="translated">Felnőtt vagy korhatáros tartalom</target> 10355 <target state="translated">Felnőtt vagy korhatáros tartalom</target>
10301 10356
10302 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10357 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10303 <trans-unit id="1755474755114288376" datatype="html"> 10358 <trans-unit id="1755474755114288376" datatype="html">
10304 <source>Up Next</source> 10359 <source>Up Next</source>
10305 <target state="translated">Legközelebb</target> 10360 <target state="translated">Legközelebb</target>
10306 10361
10307 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10362 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10308 <trans-unit id="2159130950882492111" datatype="html"> 10363 <trans-unit id="2159130950882492111" datatype="html">
10309 <source>Cancel</source> 10364 <source>Cancel</source>
10310 <target state="translated">Mégse</target> 10365 <target state="translated">Mégse</target>
@@ -10314,62 +10369,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10314 <source>Autoplay is suspended</source> 10369 <source>Autoplay is suspended</source>
10315 <target state="translated">Az automatikus lejátszás fel van függesztve</target> 10370 <target state="translated">Az automatikus lejátszás fel van függesztve</target>
10316 10371
10317 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10372 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10318 <trans-unit id="7895294730547405228" datatype="html"> 10373 <trans-unit id="7895294730547405228" datatype="html">
10319 <source>Enter/exit fullscreen (requires player focus)</source> 10374 <source>Enter/exit fullscreen (requires player focus)</source>
10320 <target state="translated">Teljes képernyőre váltás vagy normál méret (lejátszófókuszt igényel)</target> 10375 <target state="translated">Teljes képernyőre váltás vagy normál méret (lejátszófókuszt igényel)</target>
10321 10376
10322 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10377 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10323 <trans-unit id="7618388257165864759" datatype="html"> 10378 <trans-unit id="7618388257165864759" datatype="html">
10324 <source>Play/Pause the video (requires player focus)</source> 10379 <source>Play/Pause the video (requires player focus)</source>
10325 <target state="translated">A videó lejátszása vagy szüneteltetése (lejátszófókuszt igényel)</target> 10380 <target state="translated">A videó lejátszása vagy szüneteltetése (lejátszófókuszt igényel)</target>
10326 10381
10327 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10382 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10328 <trans-unit id="7761890399634216630" datatype="html"> 10383 <trans-unit id="7761890399634216630" datatype="html">
10329 <source>Mute/unmute the video (requires player focus)</source> 10384 <source>Mute/unmute the video (requires player focus)</source>
10330 <target state="translated">A videó némítása vagy a némítás visszavonása (lejátszófókuszt igényel)</target> 10385 <target state="translated">A videó némítása vagy a némítás visszavonása (lejátszófókuszt igényel)</target>
10331 10386
10332 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10387 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10333 <trans-unit id="5996585232248234904" datatype="html"> 10388 <trans-unit id="5996585232248234904" datatype="html">
10334 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10389 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10335 <target state="translated">Kihagyás a videó százalékáig: a 0 jelentése 0%, a 9 jelentése 90% (lejátszófókuszt igényel)</target> 10390 <target state="translated">Kihagyás a videó százalékáig: a 0 jelentése 0%, a 9 jelentése 90% (lejátszófókuszt igényel)</target>
10336 10391
10337 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10392 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10338 <trans-unit id="3748765405903319998" datatype="html"> 10393 <trans-unit id="3748765405903319998" datatype="html">
10339 <source>Increase the volume (requires player focus)</source> 10394 <source>Increase the volume (requires player focus)</source>
10340 <target state="translated">A hangerő növelése (lejátszófókuszt igényel)</target> 10395 <target state="translated">A hangerő növelése (lejátszófókuszt igényel)</target>
10341 10396
10342 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10397 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10343 <trans-unit id="5810704036407159982" datatype="html"> 10398 <trans-unit id="5810704036407159982" datatype="html">
10344 <source>Decrease the volume (requires player focus)</source> 10399 <source>Decrease the volume (requires player focus)</source>
10345 <target state="translated">A hangerő csökkentése (lejátszófókuszt igényel)</target> 10400 <target state="translated">A hangerő csökkentése (lejátszófókuszt igényel)</target>
10346 10401
10347 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10402 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10348 <trans-unit id="2622048822548065691" datatype="html"> 10403 <trans-unit id="2622048822548065691" datatype="html">
10349 <source>Seek the video forward (requires player focus)</source> 10404 <source>Seek the video forward (requires player focus)</source>
10350 <target state="translated">A videó előretekerése (lejátszófókuszt igényel)</target> 10405 <target state="translated">A videó előretekerése (lejátszófókuszt igényel)</target>
10351 10406
10352 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10407 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10353 <trans-unit id="6540078205109221153" datatype="html"> 10408 <trans-unit id="6540078205109221153" datatype="html">
10354 <source>Seek the video backward (requires player focus)</source> 10409 <source>Seek the video backward (requires player focus)</source>
10355 <target state="translated">A videó visszatekerése (lejátszófókuszt igényel)</target> 10410 <target state="translated">A videó visszatekerése (lejátszófókuszt igényel)</target>
10356 10411
10357 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10412 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10358 <trans-unit id="1956491957766210808" datatype="html"> 10413 <trans-unit id="1956491957766210808" datatype="html">
10359 <source>Increase playback rate (requires player focus)</source> 10414 <source>Increase playback rate (requires player focus)</source>
10360 <target state="translated">Lejátszási sebesség növelése (lejátszófókuszt igényel)</target> 10415 <target state="translated">Lejátszási sebesség növelése (lejátszófókuszt igényel)</target>
10361 10416
10362 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10417 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10363 <trans-unit id="5495529997674803186" datatype="html"> 10418 <trans-unit id="5495529997674803186" datatype="html">
10364 <source>Decrease playback rate (requires player focus)</source> 10419 <source>Decrease playback rate (requires player focus)</source>
10365 <target state="translated">Lejátszási sebesség csökkentése (lejátszófókuszt igényel)</target> 10420 <target state="translated">Lejátszási sebesség csökkentése (lejátszófókuszt igényel)</target>
10366 10421
10367 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10422 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10368 <trans-unit id="3178343147230721210" datatype="html"> 10423 <trans-unit id="3178343147230721210" datatype="html">
10369 <source>Navigate in the video frame by frame (requires player focus)</source> 10424 <source>Navigate in the video frame by frame (requires player focus)</source>
10370 <target state="translated">Navigálás a videóban képkockánként (lejátszófókuszt igényel)</target> 10425 <target state="translated">Navigálás a videóban képkockánként (lejátszófókuszt igényel)</target>
10371 10426
10372 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10427 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10373 <trans-unit id="8025996572234182184" datatype="html"> 10428 <trans-unit id="8025996572234182184" datatype="html">
10374 <source>Like the video</source> 10429 <source>Like the video</source>
10375 <target state="translated">A videó kedvelése</target> 10430 <target state="translated">A videó kedvelése</target>
diff --git a/client/src/locale/angular.it-IT.xlf b/client/src/locale/angular.it-IT.xlf
index a66051666..0f196bfbf 100644
--- a/client/src/locale/angular.it-IT.xlf
+++ b/client/src/locale/angular.it-IT.xlf
@@ -157,7 +157,7 @@
157 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source> 157 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source>
158 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target> 158 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target>
159 159
160 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 160 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
161 <trans-unit id="1486537403020619891" datatype="html"> 161 <trans-unit id="1486537403020619891" datatype="html">
162 <source>My watch history</source> 162 <source>My watch history</source>
163 <target state="translated">La mia cronologia di visualizzazione</target> 163 <target state="translated">La mia cronologia di visualizzazione</target>
@@ -232,12 +232,12 @@
232 232
233 233
234 234
235 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 235 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
236 <trans-unit id="1006562256968398209" datatype="html"> 236 <trans-unit id="1006562256968398209" datatype="html">
237 <source>video</source> 237 <source>video</source>
238 <target state="translated">video</target> 238 <target state="translated">video</target>
239 239
240 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 240 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
241 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 241 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
242 242
243 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 243 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -299,10 +299,10 @@
299 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 299 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
300 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 300 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
301 301
302 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 302 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
303 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 303 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
304 304
305 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 305 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
306 306
307 <trans-unit id="5235042777215655908" datatype="html"> 307 <trans-unit id="5235042777215655908" datatype="html">
308 <source>subtitles</source> 308 <source>subtitles</source>
@@ -696,10 +696,10 @@
696 <trans-unit id="2602586221576511475"> 696 <trans-unit id="2602586221576511475">
697 <source>Video quota</source> 697 <source>Video quota</source>
698 <target>Quota video</target> 698 <target>Quota video</target>
699 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 699
700 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 700
701 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 701
702 </trans-unit> 702 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
703 <trans-unit id="1502595455339510144"> 703 <trans-unit id="1502595455339510144">
704 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 704 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
705 <target>Illimitato <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> al giorno)<x id="CLOSE_TAG_NG_CONTAINER"/></target> 705 <target>Illimitato <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> al giorno)<x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -778,7 +778,31 @@
778 <source>Federation</source> 778 <source>Federation</source>
779 <target state="translated">Federazione</target> 779 <target state="translated">Federazione</target>
780 780
781 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 781 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
782 <source>Following</source><target state="new">Following</target>
783 <context-group purpose="location">
784 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
785 <context context-type="linenumber">29</context>
786 </context-group>
787 <context-group purpose="location">
788 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
789 <context context-type="linenumber">31</context>
790 </context-group>
791 <context-group purpose="location">
792 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
793 <context context-type="linenumber">28</context>
794 </context-group>
795 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
796 <source>Followers</source><target state="new">Followers</target>
797 <context-group purpose="location">
798 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
799 <context context-type="linenumber">34</context>
800 </context-group>
801 <context-group purpose="location">
802 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
803 <context context-type="linenumber">37</context>
804 </context-group>
805 </trans-unit>
782 <trans-unit id="3541687134897970106" datatype="html"> 806 <trans-unit id="3541687134897970106" datatype="html">
783 <source>followers</source> 807 <source>followers</source>
784 <target state="translated">follower</target> 808 <target state="translated">follower</target>
@@ -853,7 +877,7 @@
853 877
854 878
855 879
856 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 880 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
857 <trans-unit id="3616223838716839702"> 881 <trans-unit id="3616223838716839702">
858 <source>Ban this user</source> 882 <source>Ban this user</source>
859 <target>Espelli questo utente</target> 883 <target>Espelli questo utente</target>
@@ -919,19 +943,13 @@
919 <trans-unit id="7252854992688790751" datatype="html"> 943 <trans-unit id="7252854992688790751" datatype="html">
920 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 944 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
921 <target state="translated">Questa istanza consente la registrazione. Tuttavia, fai attenzione a controllare i <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Termini<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Termini<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>prima di creare un account. Puoi anche cercare un'altra istanza per soddisfare le tue esigenze esatte su: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/it/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 945 <target state="translated">Questa istanza consente la registrazione. Tuttavia, fai attenzione a controllare i <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Termini<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Termini<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>prima di creare un account. Puoi anche cercare un'altra istanza per soddisfare le tue esigenze esatte su: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/it/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
922 <context-group purpose="location"> 946
923 <context context-type="sourcefile">src/app/+login/login.component.html</context> 947 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
924 <context context-type="linenumber">60,62</context>
925 </context-group>
926 </trans-unit>
927 <trans-unit id="7215649348148521605" datatype="html"> 948 <trans-unit id="7215649348148521605" datatype="html">
928 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 949 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
929 <target state="translated">Attualmente questa istanza non consente la registrazione dell'utente, puoi controllare i <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Termini<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> per maggiori dettagli o per trovare un'istanza che ti dà la possibilità di registrare un'account e caricare i tuoi video lì. Trova il tuo tra più istanze su:<x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/it/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> </target> 950 <target state="translated">Attualmente questa istanza non consente la registrazione dell'utente, puoi controllare i <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Termini<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> per maggiori dettagli o per trovare un'istanza che ti dà la possibilità di registrare un'account e caricare i tuoi video lì. Trova il tuo tra più istanze su:<x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/it/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> </target>
930 <context-group purpose="location"> 951
931 <context context-type="sourcefile">src/app/+login/login.component.html</context> 952 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
932 <context context-type="linenumber">65,67</context>
933 </context-group>
934 </trans-unit>
935 <trans-unit id="2392488717875840729"> 953 <trans-unit id="2392488717875840729">
936 <source>User</source> 954 <source>User</source>
937 <target>Utente</target> 955 <target>Utente</target>
@@ -942,66 +960,66 @@
942 <source>Username or email address</source> 960 <source>Username or email address</source>
943 <target>Nome utente o indirizzo email</target> 961 <target>Nome utente o indirizzo email</target>
944 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 962 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
963 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
964 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
965 <context-group purpose="location">
966 <context context-type="sourcefile">src/app/+login/login.component.html</context>
967 <context context-type="linenumber">33,34</context>
968 </context-group>
945 </trans-unit> 969 </trans-unit>
946 <trans-unit id="1431416938026210429"> 970 <trans-unit id="1431416938026210429">
947 <source>Password</source> 971 <source>Password</source>
948 <target>Password</target> 972 <target>Password</target>
949 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 973
950 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 974
951 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 975
952 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 976
953 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 977
954 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 978
955 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 979
956 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 980
957 </trans-unit> 981 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
958 <trans-unit id="8715156686857791956" datatype="html"> 982 <trans-unit id="8715156686857791956" datatype="html">
959 <source>Click here to reset your password</source> 983 <source>Click here to reset your password</source>
960 <target state="translated">Clicca qui per resettare la tua password</target> 984 <target state="translated">Clicca qui per resettare la tua password</target>
961 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 985
962 </trans-unit> 986 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
963 <trans-unit id="892063502898494584" datatype="html"> 987 <trans-unit id="892063502898494584" datatype="html">
964 <source>I forgot my password</source> 988 <source>I forgot my password</source>
965 <target state="translated">Ho dimenticato la mia password</target> 989 <target state="translated">Ho dimenticato la mia password</target>
966 <context-group purpose="location"> 990
967 <context context-type="sourcefile">src/app/+login/login.component.html</context> 991 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
968 <context context-type="linenumber">47</context>
969 </context-group>
970 </trans-unit>
971 <trans-unit id="2101170466365500913" datatype="html"> 992 <trans-unit id="2101170466365500913" datatype="html">
972 <source>Logging into an account lets you publish content</source> 993 <source>Logging into an account lets you publish content</source>
973 <target state="translated">L'accesso a un account ti consente di pubblicare contenuti</target> 994 <target state="translated">L'accesso a un account ti consente di pubblicare contenuti</target>
974 <context-group purpose="location"> 995
975 <context context-type="sourcefile">src/app/+login/login.component.html</context> 996 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
976 <context context-type="linenumber">56,57</context>
977 </context-group>
978 </trans-unit>
979 <trans-unit id="2454050363478003966"> 997 <trans-unit id="2454050363478003966">
980 <source>Login</source> 998 <source>Login</source>
981 <target>Accedi</target> 999 <target>Accedi</target>
982 1000
983 1001
984 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1002 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
985 <trans-unit id="3183213940445113677" datatype="html"> 1003 <trans-unit id="3183213940445113677" datatype="html">
986 <source>Or sign in with</source> 1004 <source>Or sign in with</source>
987 <target state="translated">O accedi con</target> 1005 <target state="translated">O accedi con</target>
988 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1006
989 </trans-unit> 1007 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
990 <trans-unit id="3238209155172574367"> 1008 <trans-unit id="3238209155172574367">
991 <source>Forgot your password</source> 1009 <source>Forgot your password</source>
992 <target>Password dimenticata</target> 1010 <target>Password dimenticata</target>
993 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1011
994 </trans-unit> 1012 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
995 <trans-unit id="87327320394367488" datatype="html"> 1013 <trans-unit id="87327320394367488" datatype="html">
996 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1014 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
997 <target state="translated">Ci scusiamo, non c'è modo di recuperare la tua password perchè l'amministratore dell'istanza non ha configurato il sistema di email di PeerTube.</target> 1015 <target state="translated">Ci scusiamo, non c'è modo di recuperare la tua password perchè l'amministratore dell'istanza non ha configurato il sistema di email di PeerTube.</target>
998 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1016
999 </trans-unit> 1017 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1000 <trans-unit id="3188014010833256853" datatype="html"> 1018 <trans-unit id="3188014010833256853" datatype="html">
1001 <source>Enter your email address and we will send you a link to reset your password.</source> 1019 <source>Enter your email address and we will send you a link to reset your password.</source>
1002 <target state="translated">Inserisci il tuo indirizzo email e ti invieremo un link per reimpostare la tua password.</target> 1020 <target state="translated">Inserisci il tuo indirizzo email e ti invieremo un link per reimpostare la tua password.</target>
1003 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1021
1004 </trans-unit> 1022 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1005 <trans-unit id="1190256911880544559" datatype="html"> 1023 <trans-unit id="1190256911880544559" datatype="html">
1006 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1024 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1007The link will expire within 1 hour.</source> 1025The link will expire within 1 hour.</source>
@@ -1011,26 +1029,26 @@ The link will expire within 1 hour.</source>
1011 <trans-unit id="4768749765465246664"> 1029 <trans-unit id="4768749765465246664">
1012 <source>Email</source> 1030 <source>Email</source>
1013 <target>Email</target> 1031 <target>Email</target>
1014 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1032
1015 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1033
1016 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1034
1017 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1035
1018 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1036
1019 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1037
1020 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1038
1021 </trans-unit> 1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1022 <trans-unit id="3967269098753656610"> 1040 <trans-unit id="3967269098753656610">
1023 <source>Email address</source> 1041 <source>Email address</source>
1024 <target>Indirizzo email</target> 1042 <target>Indirizzo email</target>
1025 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1043
1026 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1044
1027 </trans-unit> 1045 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1028 <trans-unit id="7808756054397155068" datatype="html"> 1046 <trans-unit id="7808756054397155068" datatype="html">
1029 <source>Reset</source> 1047 <source>Reset</source>
1030 <target state="translated">Reimposta</target> 1048 <target state="translated">Reimposta</target>
1031 <note priority="1" from="description">Password reset button</note> 1049 <note priority="1" from="description">Password reset button</note>
1032 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1050
1033 </trans-unit> 1051 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1034 1052
1035 <trans-unit id="4319634264526091601" datatype="html"> 1053 <trans-unit id="4319634264526091601" datatype="html">
1036 <source>on this instance</source> 1054 <source>on this instance</source>
@@ -1359,7 +1377,7 @@ The link will expire within 1 hour.</source>
1359 <target>Crea un account</target> 1377 <target>Crea un account</target>
1360 1378
1361 1379
1362 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1380 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1363 1381
1364 <trans-unit id="3058024914967508975" datatype="html"> 1382 <trans-unit id="3058024914967508975" datatype="html">
1365 <source>My videos</source> 1383 <source>My videos</source>
@@ -1416,7 +1434,7 @@ The link will expire within 1 hour.</source>
1416 <source>VIDEOS</source> 1434 <source>VIDEOS</source>
1417 <target state="translated">VIDEO</target> 1435 <target state="translated">VIDEO</target>
1418 1436
1419 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 1437 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1420 <trans-unit id="667372110624203230" datatype="html"> 1438 <trans-unit id="667372110624203230" datatype="html">
1421 <source>Import jobs concurrency</source> 1439 <source>Import jobs concurrency</source>
1422 <target state="new">Import jobs concurrency</target> 1440 <target state="new">Import jobs concurrency</target>
@@ -1496,7 +1514,7 @@ The link will expire within 1 hour.</source>
1496 <source>I'm a teapot</source> 1514 <source>I'm a teapot</source>
1497 <target state="translated">Sono una teiera</target> 1515 <target state="translated">Sono una teiera</target>
1498 1516
1499 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit> 1517 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1500 <trans-unit id="1597262876035959248" datatype="html"> 1518 <trans-unit id="1597262876035959248" datatype="html">
1501 <source>That's an error.</source> 1519 <source>That's an error.</source>
1502 <target state="translated">Questo è un errore.</target> 1520 <target state="translated">Questo è un errore.</target>
@@ -1580,8 +1598,8 @@ The link will expire within 1 hour.</source>
1580 <trans-unit id="2971365540217107489" datatype="html"> 1598 <trans-unit id="2971365540217107489" datatype="html">
1581 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1599 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1582 <target state="translated">Il video è troppo grande per il server. Contatta il tuo amministratore se desideri aumentare la dimensione del limite.</target> 1600 <target state="translated">Il video è troppo grande per il server. Contatta il tuo amministratore se desideri aumentare la dimensione del limite.</target>
1583 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1601
1584 </trans-unit> 1602 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1585 1603
1586 <trans-unit id="5131854469652959713" datatype="html"> 1604 <trans-unit id="5131854469652959713" datatype="html">
1587 <source>GLOBAL SEARCH</source> 1605 <source>GLOBAL SEARCH</source>
@@ -2261,7 +2279,7 @@ The link will expire within 1 hour.</source>
2261 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2279 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2262 <source>Upload on hold</source><target state="new">Upload on hold</target> 2280 <source>Upload on hold</source><target state="new">Upload on hold</target>
2263 2281
2264 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2282 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2265 <trans-unit id="285180972645018518" datatype="html"> 2283 <trans-unit id="285180972645018518" datatype="html">
2266 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2284 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2267 <target state="translated">Spiacente, la funzionalità di upload è disabilitata per il tuo account. Se vuoi aggiungere video, un amministratore deve sbloccare la tua quota.</target> 2285 <target state="translated">Spiacente, la funzionalità di upload è disabilitata per il tuo account. Se vuoi aggiungere video, un amministratore deve sbloccare la tua quota.</target>
@@ -2970,11 +2988,7 @@ The link will expire within 1 hour.</source>
2970 <target>ID</target> 2988 <target>ID</target>
2971 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 2989 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
2972 </trans-unit> 2990 </trans-unit>
2973 <trans-unit id="2265605798180116441" datatype="html"> 2991
2974 <source>Follower handle</source>
2975 <target state="translated">Gestione seguaci</target>
2976
2977 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
2978 <trans-unit id="5911214550882917183"> 2992 <trans-unit id="5911214550882917183">
2979 <source>State</source> 2993 <source>State</source>
2980 <target>Stato</target> 2994 <target>Stato</target>
@@ -3040,11 +3054,7 @@ The link will expire within 1 hour.</source>
3040 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target> 3054 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target>
3041 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3055 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3042 </trans-unit> 3056 </trans-unit>
3043 <trans-unit id="6641024648411549335"> 3057
3044 <source>Host</source>
3045 <target>Host</target>
3046
3047 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3048 <trans-unit id="6571718060636962350" datatype="html"> 3058 <trans-unit id="6571718060636962350" datatype="html">
3049 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3059 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3050 <target state="translated">Ridondanza consentita <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 3060 <target state="translated">Ridondanza consentita <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -3054,7 +3064,7 @@ The link will expire within 1 hour.</source>
3054 <source>Unfollow</source> 3064 <source>Unfollow</source>
3055 <target state="translated">Smetti di seguire</target> 3065 <target state="translated">Smetti di seguire</target>
3056 3066
3057 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3067 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3058 <trans-unit id="8246779176913476983" datatype="html"> 3068 <trans-unit id="8246779176913476983" datatype="html">
3059 <source>Open instance in a new tab</source> 3069 <source>Open instance in a new tab</source>
3060 <target state="translated">Apri l'istanza in una nuova scheda</target> 3070 <target state="translated">Apri l'istanza in una nuova scheda</target>
@@ -3066,27 +3076,19 @@ The link will expire within 1 hour.</source>
3066 <source>No host found matching current filters.</source> 3076 <source>No host found matching current filters.</source>
3067 <target state="translated">Nessun host trovato corrispondente ai filtri correnti.</target> 3077 <target state="translated">Nessun host trovato corrispondente ai filtri correnti.</target>
3068 3078
3069 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3079 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3070 <trans-unit id="7274241885665071790" datatype="html"> 3080 <trans-unit id="7274241885665071790" datatype="html">
3071 <source>Your instance is not following anyone.</source> 3081 <source>Your instance is not following anyone.</source>
3072 <target state="translated">La tua istanza non sta seguendo nessuno.</target> 3082 <target state="translated">La tua istanza non sta seguendo nessuno.</target>
3073 3083
3074 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3084 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3075 <trans-unit id="4774348799569692380" datatype="html"> 3085 <trans-unit id="4774348799569692380" datatype="html">
3076 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3086 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3077 <target state="translated">Mostra <x id="INTERPOLATION"/> a <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</target> 3087 <target state="translated">Mostra <x id="INTERPOLATION"/> a <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</target>
3078 3088
3079 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3089 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3080 <trans-unit id="6275803119759621687" datatype="html"> 3090
3081 <source>Follow domains</source> 3091 <trans-unit id="9216117865911519658" datatype="html">
3082 <target state="translated">Segui domini</target>
3083
3084 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit>
3085 <trans-unit id="1268699198448750610" datatype="html">
3086 <source>Follow instances</source>
3087 <target state="translated">Segui istanze</target>
3088
3089 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3090 <source>Action</source><target state="new">Action</target> 3092 <source>Action</source><target state="new">Action</target>
3091 3093
3092 3094
@@ -3133,11 +3135,11 @@ The link will expire within 1 hour.</source>
3133 <trans-unit id="5248717555542428023"> 3135 <trans-unit id="5248717555542428023">
3134 <source>Username</source> 3136 <source>Username</source>
3135 <target>Nome utente</target> 3137 <target>Nome utente</target>
3136 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3138
3137 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3139
3138 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3140
3139 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3141
3140 </trans-unit> 3142 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3141 <trans-unit id="5428411040014095392" datatype="html"> 3143 <trans-unit id="5428411040014095392" datatype="html">
3142 <source>e.g. jane_doe</source> 3144 <source>e.g. jane_doe</source>
3143 <target state="translated">esempio: maria_rossi</target> 3145 <target state="translated">esempio: maria_rossi</target>
@@ -3165,9 +3167,9 @@ The link will expire within 1 hour.</source>
3165 <trans-unit id="4145496584631696119"> 3167 <trans-unit id="4145496584631696119">
3166 <source>Role</source> 3168 <source>Role</source>
3167 <target>Ruolo</target> 3169 <target>Ruolo</target>
3168 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3170
3169 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3171
3170 </trans-unit> 3172 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3171 <trans-unit id="7046347992315328430" datatype="html"> 3173 <trans-unit id="7046347992315328430" datatype="html">
3172 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3174 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3173 <target state="translated">La transcodifica è abilitata. La quota video prende in considerazione solo <x id="START_TAG_STRONG"/>dimensione<x id="CLOSE_TAG_STRONG"/> originale del video. <x id="LINE_BREAK"/> L'utente dovrebbe poter caricare ~ <x id="INTERPOLATION"/>. </target> 3175 <target state="translated">La transcodifica è abilitata. La quota video prende in considerazione solo <x id="START_TAG_STRONG"/>dimensione<x id="CLOSE_TAG_STRONG"/> originale del video. <x id="LINE_BREAK"/> L'utente dovrebbe poter caricare ~ <x id="INTERPOLATION"/>. </target>
@@ -3184,15 +3186,9 @@ The link will expire within 1 hour.</source>
3184 <trans-unit id="2622255144026150901" datatype="html"> 3186 <trans-unit id="2622255144026150901" datatype="html">
3185 <source>Auth plugin</source> 3187 <source>Auth plugin</source>
3186 <target state="new">Auth plugin</target> 3188 <target state="new">Auth plugin</target>
3187 <context-group purpose="location"> 3189
3188 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3190
3189 <context context-type="linenumber">188</context> 3191 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3190 </context-group>
3191 <context-group purpose="location">
3192 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3193 <context context-type="linenumber">188</context>
3194 </context-group>
3195 </trans-unit>
3196 <trans-unit id="588099657508661970" datatype="html"> 3192 <trans-unit id="588099657508661970" datatype="html">
3197 <source>None (local authentication)</source> 3193 <source>None (local authentication)</source>
3198 <target state="new">None (local authentication)</target> 3194 <target state="new">None (local authentication)</target>
@@ -3437,7 +3433,13 @@ The link will expire within 1 hour.</source>
3437 3433
3438 3434
3439 3435
3440 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit> 3436 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3437 <source>Follower</source><target state="new">Follower</target>
3438 <context-group purpose="location">
3439 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3440 <context context-type="linenumber">24</context>
3441 </context-group>
3442 </trans-unit>
3441 <trans-unit id="4691552465058437520" datatype="html"> 3443 <trans-unit id="4691552465058437520" datatype="html">
3442 <source>Commented video</source> 3444 <source>Commented video</source>
3443 <target state="translated">Video commentati</target> 3445 <target state="translated">Video commentati</target>
@@ -3726,7 +3728,7 @@ The link will expire within 1 hour.</source>
3726 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3728 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3727 <target state="translated">Sembra che tu non sia su un server HTTPS. Il tuo server web deve avere TLS attivato per poter seguire i server.</target> 3729 <target state="translated">Sembra che tu non sia su un server HTTPS. Il tuo server web deve avere TLS attivato per poter seguire i server.</target>
3728 3730
3729 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3731 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3730 <trans-unit id="4058814854824495833" datatype="html"> 3732 <trans-unit id="4058814854824495833" datatype="html">
3731 <source>Mute domains</source> 3733 <source>Mute domains</source>
3732 <target state="translated">Silenzia domini</target> 3734 <target state="translated">Silenzia domini</target>
@@ -5630,11 +5632,8 @@ color: red;
5630 5632
5631 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5633 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5632 <source>CHANNELS</source><target state="new">CHANNELS</target> 5634 <source>CHANNELS</source><target state="new">CHANNELS</target>
5633 <context-group purpose="location"> 5635
5634 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5636 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5635 <context context-type="linenumber">82</context>
5636 </context-group>
5637 </trans-unit>
5638 5637
5639 <trans-unit id="3666829335406793239" datatype="html"> 5638 <trans-unit id="3666829335406793239" datatype="html">
5640 <source>This account does not have channels.</source> 5639 <source>This account does not have channels.</source>
@@ -5668,7 +5667,13 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
5668channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 5667channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
5669 <target state="translated">Vuoi davvero eliminare <x id="PH" equiv-text="videoChannel.displayName"/>? Verranno eliminati <x id="PH_1" equiv-text="videoChannel.videosCount"/> i video caricati in questo canale e non potrai creare un altro canale con lo stesso nome <x id="PH_2" equiv-text="videoChannel.name"/></target> 5668 <target state="translated">Vuoi davvero eliminare <x id="PH" equiv-text="videoChannel.displayName"/>? Verranno eliminati <x id="PH_1" equiv-text="videoChannel.videosCount"/> i video caricati in questo canale e non potrai creare un altro canale con lo stesso nome <x id="PH_2" equiv-text="videoChannel.name"/></target>
5670 5669
5671 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5670 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5671 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5672 <context-group purpose="location">
5673 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5674 <context context-type="linenumber">48</context>
5675 </context-group>
5676 </trans-unit>
5672 <trans-unit id="5387007581996837469" datatype="html"> 5677 <trans-unit id="5387007581996837469" datatype="html">
5673 <source>My Channels</source> 5678 <source>My Channels</source>
5674 <target state="translated">I miei Canali</target> 5679 <target state="translated">I miei Canali</target>
@@ -6185,12 +6190,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6185 <source>Your message has been sent.</source> 6190 <source>Your message has been sent.</source>
6186 <target>Messaggio inviato.</target> 6191 <target>Messaggio inviato.</target>
6187 6192
6188 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6193 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6189 <trans-unit id="2072135752262464360"> 6194 <trans-unit id="2072135752262464360">
6190 <source>You already sent this form recently</source> 6195 <source>You already sent this form recently</source>
6191 <target>Questo modulo è stato usato di recente</target> 6196 <target>Questo modulo è stato usato di recente</target>
6192 6197
6193 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6198 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6194 6199
6195 <trans-unit id="819067926858619041" datatype="html"> 6200 <trans-unit id="819067926858619041" datatype="html">
6196 <source>Account videos</source> 6201 <source>Account videos</source>
@@ -6236,12 +6241,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6236 <source><x id="PH"/> direct account followers </source> 6241 <source><x id="PH"/> direct account followers </source>
6237 <target state="translated"><x id="PH"/> follower diretti dell'account </target> 6242 <target state="translated"><x id="PH"/> follower diretti dell'account </target>
6238 6243
6239 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit> 6244 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6240 <trans-unit id="6250999352462648289" datatype="html"> 6245 <trans-unit id="6250999352462648289" datatype="html">
6241 <source>Report this account</source> 6246 <source>Report this account</source>
6242 <target state="translated">Segnala questo account</target> 6247 <target state="translated">Segnala questo account</target>
6243 6248
6244 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6249 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6245 6250
6246 6251
6247 <trans-unit id="1504521795586863905" datatype="html"> 6252 <trans-unit id="1504521795586863905" datatype="html">
@@ -6256,27 +6261,19 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6256 <target state="translated">Nome utente copiato</target> 6261 <target state="translated">Nome utente copiato</target>
6257 6262
6258 6263
6259 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit> 6264 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6260 <trans-unit id="9221735175659318025" datatype="html"> 6265 <trans-unit id="9221735175659318025" datatype="html">
6261 <source>1 subscriber</source> 6266 <source>1 subscriber</source>
6262 <target state="translated">1 sottoscrittore</target> 6267 <target state="translated">1 sottoscrittore</target>
6263 6268
6264 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit> 6269 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6265 <trans-unit id="4097331874769079975" datatype="html"> 6270 <trans-unit id="4097331874769079975" datatype="html">
6266 <source><x id="PH"/> subscribers</source> 6271 <source><x id="PH"/> subscribers</source>
6267 <target state="translated"><x id="PH"/> sottoscrittori</target> 6272 <target state="translated"><x id="PH"/> sottoscrittori</target>
6268 6273
6269 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6274 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6270 <trans-unit id="4682675125751819107" datatype="html"> 6275
6271 <source>Instances you follow</source> 6276
6272 <target state="translated">Istanze che segui</target>
6273
6274 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6275 <trans-unit id="8899833753704589712" datatype="html">
6276 <source>Instances following you</source>
6277 <target state="translated">Istanze che ti seguono</target>
6278
6279 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6280 <trans-unit id="1035838766454786107" datatype="html"> 6277 <trans-unit id="1035838766454786107" datatype="html">
6281 <source>Audio-only</source> 6278 <source>Audio-only</source>
6282 <target state="translated">Solo audio</target> 6279 <target state="translated">Solo audio</target>
@@ -6326,6 +6323,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6326 <source>Auto (via ffmpeg)</source> 6323 <source>Auto (via ffmpeg)</source>
6327 <target>Auto (tramite ffmpeg)</target> 6324 <target>Auto (tramite ffmpeg)</target>
6328 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6325 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6326 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6327 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6328 <context-group purpose="location">
6329 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6330 <context context-type="linenumber">3</context>
6331 </context-group>
6329 </trans-unit> 6332 </trans-unit>
6330 <trans-unit id="931255636742351800" datatype="html"> 6333 <trans-unit id="931255636742351800" datatype="html">
6331 <source>No limit</source> 6334 <source>No limit</source>
@@ -6466,18 +6469,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6466 <trans-unit id="2127446333083057097" datatype="html"> 6469 <trans-unit id="2127446333083057097" datatype="html">
6467 <source>Domain is required.</source> 6470 <source>Domain is required.</source>
6468 <target state="translated">Il Dominio è richiesto.</target> 6471 <target state="translated">Il Dominio è richiesto.</target>
6469 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6472
6470 </trans-unit> 6473 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6471 <trans-unit id="6780793142903080663" datatype="html"> 6474 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6472 <source>Domains entered are invalid.</source> 6475 <context-group purpose="location">
6473 <target state="translated">I domini inseriti non sono validi.</target> 6476 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6474 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6477 <context context-type="linenumber">93</context>
6475 </trans-unit> 6478 </context-group>
6476 <trans-unit id="5886492514458202177" datatype="html"> 6479 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6477 <source>Domains entered contain duplicates.</source> 6480 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6478 <target state="translated">I domini inseriti contengono duplicati.</target> 6481 <context-group purpose="location">
6479 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6482 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6483 <context context-type="linenumber">94</context>
6484 </context-group>
6485 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6486 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6487 <context-group purpose="location">
6488 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6489 <context context-type="linenumber">102</context>
6490 </context-group>
6491 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6492 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6493 <context-group purpose="location">
6494 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6495 <context context-type="linenumber">103</context>
6496 </context-group>
6480 </trans-unit> 6497 </trans-unit>
6498
6499
6481 <trans-unit id="240806681889331244"> 6500 <trans-unit id="240806681889331244">
6482 <source>Unlimited</source> 6501 <source>Unlimited</source>
6483 <target>Illimitato/ti</target> 6502 <target>Illimitato/ti</target>
@@ -6631,24 +6650,50 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6631 <source><x id="PH"/> removed from instance followers </source> 6650 <source><x id="PH"/> removed from instance followers </source>
6632 <target state="translated"><x id="PH"/> rimosso dai follower dell'istanza </target> 6651 <target state="translated"><x id="PH"/> rimosso dai follower dell'istanza </target>
6633 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6652 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6653 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6654 <source>Follow</source><target state="new">Follow</target>
6655 <context-group purpose="location">
6656 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6657 <context context-type="linenumber">3</context>
6658 </context-group>
6659 <context-group purpose="location">
6660 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6661 <context context-type="linenumber">37</context>
6662 </context-group>
6663 <context-group purpose="location">
6664 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6665 <context context-type="linenumber">18</context>
6666 </context-group>
6667 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6668 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6669 <context-group purpose="location">
6670 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6671 <context context-type="linenumber">11</context>
6672 </context-group>
6634 </trans-unit> 6673 </trans-unit>
6635 <trans-unit id="2740793005745065895"> 6674 <trans-unit id="2740793005745065895">
6636 <source><x id="PH"/> is not valid </source> 6675 <source><x id="PH"/> is not valid </source>
6637 <target> 6676 <target>
6638 <x id="PH"/> non è valida 6677 <x id="PH"/> non è valida
6639 </target> 6678 </target>
6640 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6679
6641 </trans-unit> 6680 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6642 <trans-unit id="2355066641781598196"> 6681 <trans-unit id="2355066641781598196">
6643 <source>Follow request(s) sent!</source> 6682 <source>Follow request(s) sent!</source>
6644 <target>Richiesta/e per seguire (follow) spedita/e!</target> 6683 <target>Richiesta/e per seguire (follow) spedita/e!</target>
6645 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6684
6685 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6686 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6687 <context-group purpose="location">
6688 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6689 <context context-type="linenumber">3</context>
6690 </context-group>
6646 </trans-unit> 6691 </trans-unit>
6647 <trans-unit id="4245720728052819482"> 6692 <trans-unit id="4245720728052819482">
6648 <source>Do you really want to unfollow <x id="PH"/>?</source> 6693 <source>Do you really want to unfollow <x id="PH"/>?</source>
6649 <target>Vuoi veramente smettere di seguire <x id="PH"/>?</target> 6694 <target>Vuoi veramente smettere di seguire <x id="PH"/>?</target>
6650 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6695
6651 </trans-unit> 6696 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6652 <trans-unit id="9160510009013134726"> 6697 <trans-unit id="9160510009013134726">
6653 <source>Unfollow</source> 6698 <source>Unfollow</source>
6654 <target>Smetti di seguire (unfollow)</target> 6699 <target>Smetti di seguire (unfollow)</target>
@@ -6657,8 +6702,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6657 <trans-unit id="3935234189109112926"> 6702 <trans-unit id="3935234189109112926">
6658 <source>You are not following <x id="PH"/> anymore.</source> 6703 <source>You are not following <x id="PH"/> anymore.</source>
6659 <target>Non stai seguendo piú <x id="PH"/>.</target> 6704 <target>Non stai seguendo piú <x id="PH"/>.</target>
6660 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6705
6661 </trans-unit> 6706 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6662 <trans-unit id="2593763089859685916"> 6707 <trans-unit id="2593763089859685916">
6663 <source>enabled</source> 6708 <source>enabled</source>
6664 <target>attivato</target> 6709 <target>attivato</target>
@@ -7098,9 +7143,9 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7098 <trans-unit id="1519954996184640001"> 7143 <trans-unit id="1519954996184640001">
7099 <source>Error</source> 7144 <source>Error</source>
7100 <target>Errore</target> 7145 <target>Errore</target>
7101 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7146
7102 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7147
7103 </trans-unit> 7148 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7104 <trans-unit id="5076187961693950167" datatype="html"> 7149 <trans-unit id="5076187961693950167" datatype="html">
7105 <source>Standard logs</source> 7150 <source>Standard logs</source>
7106 <target state="translated">Logs standard</target> 7151 <target state="translated">Logs standard</target>
@@ -7141,16 +7186,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7141 <target>Aggiorna password dell' utente</target> 7186 <target>Aggiorna password dell' utente</target>
7142 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7187 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7143 </trans-unit> 7188 </trans-unit>
7144 <trans-unit id="177544274549739411" datatype="html"> 7189
7145 <source>Following list</source> 7190
7146 <target state="translated">Elenco seguente</target>
7147 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7148 </trans-unit>
7149 <trans-unit id="8092429110007204784" datatype="html">
7150 <source>Followers list</source>
7151 <target state="translated">Elenco followers</target>
7152 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7153 </trans-unit>
7154 <trans-unit id="780323526182667308" datatype="html"> 7191 <trans-unit id="780323526182667308" datatype="html">
7155 <source>User <x id="PH"/> updated.</source> 7192 <source>User <x id="PH"/> updated.</source>
7156 <target state="translated">Utente <x id="PH"/> aggiornato.</target> 7193 <target state="translated">Utente <x id="PH"/> aggiornato.</target>
@@ -7186,16 +7223,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7186 <target state="translated">Federazione</target> 7223 <target state="translated">Federazione</target>
7187 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7224 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7188 </trans-unit> 7225 </trans-unit>
7189 <trans-unit id="4682675125751819107" datatype="html"> 7226
7190 <source>Instances you follow</source> 7227
7191 <target state="translated">Istanze che segui</target>
7192 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7193 </trans-unit>
7194 <trans-unit id="8899833753704589712" datatype="html">
7195 <source>Instances following you</source>
7196 <target state="translated">Istanze che ti seguono</target>
7197 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7198 </trans-unit>
7199 <trans-unit id="3767259920053407667" datatype="html"> 7228 <trans-unit id="3767259920053407667" datatype="html">
7200 <source>Videos will be deleted, comments will be tombstoned.</source> 7229 <source>Videos will be deleted, comments will be tombstoned.</source>
7201 <target state="translated">I video verranno eliminati, i commenti verranno rimossi definitivamente.</target> 7230 <target state="translated">I video verranno eliminati, i commenti verranno rimossi definitivamente.</target>
@@ -7226,7 +7255,25 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7226 <target>Imposta email come verificata</target> 7255 <target>Imposta email come verificata</target>
7227 7256
7228 7257
7229 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7258 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7259 <source>Created</source><target state="new">Created</target>
7260 <context-group purpose="location">
7261 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7262 <context context-type="linenumber">115</context>
7263 </context-group>
7264 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7265 <source>Daily quota</source><target state="new">Daily quota</target>
7266 <context-group purpose="location">
7267 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7268 <context context-type="linenumber">120</context>
7269 </context-group>
7270 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7271 <source>Last login</source><target state="new">Last login</target>
7272 <context-group purpose="location">
7273 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7274 <context context-type="linenumber">122</context>
7275 </context-group>
7276 </trans-unit>
7230 <trans-unit id="3403978719736970622"> 7277 <trans-unit id="3403978719736970622">
7231 <source>You cannot ban root.</source> 7278 <source>You cannot ban root.</source>
7232 <target>Non puoi espellere root.</target> 7279 <target>Non puoi espellere root.</target>
@@ -7530,12 +7577,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7530 <source>Video channel <x id="PH"/> created.</source> 7577 <source>Video channel <x id="PH"/> created.</source>
7531 <target>Il canale video <x id="PH"/> è stato creato.</target> 7578 <target>Il canale video <x id="PH"/> è stato creato.</target>
7532 7579
7533 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7580 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7534 <trans-unit id="8723777130353305761"> 7581 <trans-unit id="8723777130353305761">
7535 <source>This name already exists on this instance.</source> 7582 <source>This name already exists on this instance.</source>
7536 <target>Questo nome esiste già nell'istanza.</target> 7583 <target>Questo nome esiste già nell'istanza.</target>
7537 7584
7538 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7585 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7539 <trans-unit id="7589345916094713536"> 7586 <trans-unit id="7589345916094713536">
7540 <source>Video channel <x id="PH"/> updated.</source> 7587 <source>Video channel <x id="PH"/> updated.</source>
7541 <target>Il canale video <x id="PH"/> è stato aggiornato.</target> 7588 <target>Il canale video <x id="PH"/> è stato aggiornato.</target>
@@ -7550,11 +7597,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7550 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 7597 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7551 7598
7552 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7599 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7553 <trans-unit id="2575302837003821736"> 7600
7554 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7555 <target>Digita il nome visualizzato del canale video ( <x id="PH"/>) per confermare</target>
7556
7557 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7558 <trans-unit id="624066830180032195"> 7601 <trans-unit id="624066830180032195">
7559 <source>Video channel <x id="PH"/> deleted.</source> 7602 <source>Video channel <x id="PH"/> deleted.</source>
7560 <target>Il canale video <x id="PH"/> è stato cancellato.</target> 7603 <target>Il canale video <x id="PH"/> è stato cancellato.</target>
@@ -7703,6 +7746,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7703 <source>Ownership change request sent.</source> 7746 <source>Ownership change request sent.</source>
7704 <target>Richiesta di cambio proprietario spedita.</target> 7747 <target>Richiesta di cambio proprietario spedita.</target>
7705 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 7748 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
7749 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
7750 <source>Sort by</source><target state="new">Sort by</target>
7751 <context-group purpose="location">
7752 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7753 <context context-type="linenumber">26</context>
7754 </context-group>
7706 </trans-unit> 7755 </trans-unit>
7707 <trans-unit id="3245220240937722814"> 7756 <trans-unit id="3245220240937722814">
7708 <source>My channels</source> 7757 <source>My channels</source>
@@ -7804,7 +7853,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7804 <target>Iscriversi all'account</target> 7853 <target>Iscriversi all'account</target>
7805 7854
7806 7855
7807 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 7856 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
7808 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 7857 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
7809 <context-group purpose="location"> 7858 <context-group purpose="location">
7810 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 7859 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -7850,34 +7899,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7850 <trans-unit id="3779524668013120370"> 7899 <trans-unit id="3779524668013120370">
7851 <source>Go to my subscriptions</source> 7900 <source>Go to my subscriptions</source>
7852 <target>Vai alle mie iscrizioni</target> 7901 <target>Vai alle mie iscrizioni</target>
7853 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 7902
7854 </trans-unit> 7903 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
7855 <trans-unit id="1136469849928650779"> 7904 <trans-unit id="1136469849928650779">
7856 <source>Go to my videos</source> 7905 <source>Go to my videos</source>
7857 <target>Vai ai miei video</target> 7906 <target>Vai ai miei video</target>
7858 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 7907
7859 </trans-unit> 7908 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
7860 <trans-unit id="7836683738999600376"> 7909 <trans-unit id="7836683738999600376">
7861 <source>Go to my imports</source> 7910 <source>Go to my imports</source>
7862 <target>Vai alle mie importazioni</target> 7911 <target>Vai alle mie importazioni</target>
7863 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 7912
7864 </trans-unit> 7913 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
7865 <trans-unit id="7511292153332773503"> 7914 <trans-unit id="7511292153332773503">
7866 <source>Go to my channels</source> 7915 <source>Go to my channels</source>
7867 <target>Vai ai miei canali</target> 7916 <target>Vai ai miei canali</target>
7868 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 7917
7869 </trans-unit> 7918 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
7870 <trans-unit id="2013324644839511073" datatype="html"> 7919 <trans-unit id="2013324644839511073" datatype="html">
7871 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 7920 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
7872Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 7921Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
7873 <target state="translated">Impossibile recuperare le credenziali del Client OAuth: <x id="PH" equiv-text="error.text"/>. Assicurati di aver configurato correttamente PeerTube (config/ directory), in particolare la sezione "webserver".</target> 7922 <target state="translated">Impossibile recuperare le credenziali del Client OAuth: <x id="PH" equiv-text="error.text"/>. Assicurati di aver configurato correttamente PeerTube (config/ directory), in particolare la sezione "webserver".</target>
7874 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 7923
7875 </trans-unit> 7924 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
7876 <trans-unit id="375263728166936544"> 7925 <trans-unit id="375263728166936544">
7877 <source>You need to reconnect.</source> 7926 <source>You need to reconnect.</source>
7878 <target>Devi riconnetterti.</target> 7927 <target>Devi riconnetterti.</target>
7879 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 7928
7880 </trans-unit> 7929 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
7881 <trans-unit id="2206638022166154361"> 7930 <trans-unit id="2206638022166154361">
7882 <source>Keyboard Shortcuts:</source> 7931 <source>Keyboard Shortcuts:</source>
7883 <target>Scorciatoie per la tastiera:</target> 7932 <target>Scorciatoie per la tastiera:</target>
@@ -7888,6 +7937,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7888 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 7937 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7889 <context context-type="linenumber">98</context> 7938 <context context-type="linenumber">98</context>
7890 </context-group> 7939 </context-group>
7940 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
7941 <source>In my library</source><target state="new">In my library</target>
7942 <context-group purpose="location">
7943 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7944 <context context-type="linenumber">104</context>
7945 </context-group>
7891 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 7946 </trans-unit><trans-unit id="232050922346936574" datatype="html">
7892 <source>Trending</source><target state="new">Trending</target> 7947 <source>Trending</source><target state="new">Trending</target>
7893 7948
@@ -7911,38 +7966,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7911 <source>Incorrect username or password.</source> 7966 <source>Incorrect username or password.</source>
7912 <target>Nome utente o password non corretti.</target> 7967 <target>Nome utente o password non corretti.</target>
7913 7968
7914 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 7969 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
7915 <trans-unit id="6974874606619467663" datatype="html"> 7970 <trans-unit id="6974874606619467663" datatype="html">
7916 <source>Your account is blocked.</source> 7971 <source>Your account is blocked.</source>
7917 <target state="translated">Il tuo account è bloccato.</target> 7972 <target state="translated">Il tuo account è bloccato.</target>
7918 7973
7919 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 7974 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
7920 <trans-unit id="7939914198003891823" datatype="html"> 7975 <trans-unit id="7939914198003891823" datatype="html">
7921 <source>any language</source> 7976 <source>any language</source>
7922 <target state="translated">qualsiasi lingua</target> 7977 <target state="translated">qualsiasi lingua</target>
7923 7978
7924 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 7979 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
7925 7980
7926 <trans-unit id="5633144232269377096" datatype="html"> 7981 <trans-unit id="5633144232269377096" datatype="html">
7927 <source>hide</source> 7982 <source>hide</source>
7928 <target state="translated">nascondi</target> 7983 <target state="translated">nascondi</target>
7929 7984
7930 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 7985 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
7931 <trans-unit id="8603861867909474404" datatype="html"> 7986 <trans-unit id="8603861867909474404" datatype="html">
7932 <source>blur</source> 7987 <source>blur</source>
7933 <target state="translated">sfocatura</target> 7988 <target state="translated">sfocatura</target>
7934 7989
7935 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 7990 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
7936 <trans-unit id="4534458451100881847" datatype="html"> 7991 <trans-unit id="4534458451100881847" datatype="html">
7937 <source>display</source> 7992 <source>display</source>
7938 <target state="translated">schermo</target> 7993 <target state="translated">schermo</target>
7939 7994
7940 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 7995 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
7941 <trans-unit id="4467323362722952678" datatype="html"> 7996 <trans-unit id="4467323362722952678" datatype="html">
7942 <source>Unknown</source> 7997 <source>Unknown</source>
7943 <target state="translated">Sconosciuto</target> 7998 <target state="translated">Sconosciuto</target>
7944 7999
7945 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8000 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
7946 <trans-unit id="8781423666414310853"> 8001 <trans-unit id="8781423666414310853">
7947 <source>Your password has been successfully reset!</source> 8002 <source>Your password has been successfully reset!</source>
7948 <target>La tua password è stata reimpostata con successo!</target> 8003 <target>La tua password è stata reimpostata con successo!</target>
@@ -9499,18 +9554,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9499 <trans-unit id="968295009933361070"> 9554 <trans-unit id="968295009933361070">
9500 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9555 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9501 <target>Troppi tentativi, si potrà provare di nuovo dopo <x id="PH"/> minuti.</target> 9556 <target>Troppi tentativi, si potrà provare di nuovo dopo <x id="PH"/> minuti.</target>
9502 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9557
9503 </trans-unit> 9558 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9504 <trans-unit id="4965472196059235310"> 9559 <trans-unit id="4965472196059235310">
9505 <source>Too many attempts, please try again later.</source> 9560 <source>Too many attempts, please try again later.</source>
9506 <target>Troppi tentativi, riprovare più tardi.</target> 9561 <target>Troppi tentativi, riprovare più tardi.</target>
9507 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9562
9508 </trans-unit> 9563 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9509 <trans-unit id="1693549688987384699"> 9564 <trans-unit id="1693549688987384699">
9510 <source>Server error. Please retry later.</source> 9565 <source>Server error. Please retry later.</source>
9511 <target>Errore del server. Riprovare più tardi.</target> 9566 <target>Errore del server. Riprovare più tardi.</target>
9512 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9567
9513 </trans-unit> 9568 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9514 <trans-unit id="5927402622550505067" datatype="html"> 9569 <trans-unit id="5927402622550505067" datatype="html">
9515 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9570 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9516 <target state="translated">Iscrizione a tutti i canali correnti di <x id="PH"/>. Riceverai una notifica di tutti i loro nuovi video.</target> 9571 <target state="translated">Iscrizione a tutti i canali correnti di <x id="PH"/>. Riceverai una notifica di tutti i loro nuovi video.</target>
@@ -10089,33 +10144,33 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10089 <source>Your video was uploaded to your account and is private.</source> 10144 <source>Your video was uploaded to your account and is private.</source>
10090 <target>Il video è stato caricato sul proprio account ed è privato.</target> 10145 <target>Il video è stato caricato sul proprio account ed è privato.</target>
10091 10146
10092 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10147 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10093 <trans-unit id="5699822024600815733"> 10148 <trans-unit id="5699822024600815733">
10094 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10149 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10095 <target>I dati associati (tag, descrizione, ...) saranno persi. Chiudere questa pagina ?</target> 10150 <target>I dati associati (tag, descrizione, ...) saranno persi. Chiudere questa pagina ?</target>
10096 10151
10097 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10152 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10098 <trans-unit id="1219739004043110649"> 10153 <trans-unit id="1219739004043110649">
10099 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10154 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10100 <target>Il tuo video non è ancora caricato. Sei sicuro di volere chiudere questa pagina ?</target> 10155 <target>Il tuo video non è ancora caricato. Sei sicuro di volere chiudere questa pagina ?</target>
10101 10156
10102 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10157 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10103 <trans-unit id="6932865105766151309" datatype="html"> 10158 <trans-unit id="6932865105766151309" datatype="html">
10104 <source>Upload</source> 10159 <source>Upload</source>
10105 <target state="translated">Carica</target> 10160 <target state="translated">Carica</target>
10106 10161
10107 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10162 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10108 <trans-unit id="8278735427925094503" datatype="html"> 10163 <trans-unit id="8278735427925094503" datatype="html">
10109 <source>Upload <x id="PH"/> </source> 10164 <source>Upload <x id="PH"/> </source>
10110 <target state="translated">Carica<x id="PH"/> </target> 10165 <target state="translated">Carica<x id="PH"/> </target>
10111 10166
10112 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10167 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10113 10168
10114 <trans-unit id="5981816353437801748"> 10169 <trans-unit id="5981816353437801748">
10115 <source>Video published.</source> 10170 <source>Video published.</source>
10116 <target>Video pubblicato.</target> 10171 <target>Video pubblicato.</target>
10117 10172
10118 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10173 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10119 10174
10120 10175
10121 <trans-unit id="764164089183618119"> 10176 <trans-unit id="764164089183618119">
@@ -10165,27 +10220,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10165 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10220 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10166 <target state="translated">Questo video non è disponibile su questa istanza. Vuoi essere reindirizzato sull'istanza di origine: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10221 <target state="translated">Questo video non è disponibile su questa istanza. Vuoi essere reindirizzato sull'istanza di origine: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10167 10222
10168 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10223 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10169 <trans-unit id="5761611056224181752" datatype="html"> 10224 <trans-unit id="5761611056224181752" datatype="html">
10170 <source>Redirection</source> 10225 <source>Redirection</source>
10171 <target state="translated">Redirezione</target> 10226 <target state="translated">Redirezione</target>
10172 10227
10173 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10228 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10174 <trans-unit id="8858527736400081688"> 10229 <trans-unit id="8858527736400081688">
10175 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10230 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10176 <target>Questo video contiene del contenuto sensibile. Sei sicuro di volerlo guardare?</target> 10231 <target>Questo video contiene del contenuto sensibile. Sei sicuro di volerlo guardare?</target>
10177 10232
10178 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10233 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10179 <trans-unit id="3937119019020041049"> 10234 <trans-unit id="3937119019020041049">
10180 <source>Mature or explicit content</source> 10235 <source>Mature or explicit content</source>
10181 <target>Contenuto per adulti o esplicito</target> 10236 <target>Contenuto per adulti o esplicito</target>
10182 10237
10183 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10238 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10184 <trans-unit id="1755474755114288376" datatype="html"> 10239 <trans-unit id="1755474755114288376" datatype="html">
10185 <source>Up Next</source> 10240 <source>Up Next</source>
10186 <target state="translated">Avanti il prossimo</target> 10241 <target state="translated">Avanti il prossimo</target>
10187 10242
10188 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10243 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10189 <trans-unit id="2159130950882492111" datatype="html"> 10244 <trans-unit id="2159130950882492111" datatype="html">
10190 <source>Cancel</source> 10245 <source>Cancel</source>
10191 <target state="translated">Annulla</target> 10246 <target state="translated">Annulla</target>
@@ -10195,62 +10250,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10195 <source>Autoplay is suspended</source> 10250 <source>Autoplay is suspended</source>
10196 <target state="translated">Autoplay sospeso</target> 10251 <target state="translated">Autoplay sospeso</target>
10197 10252
10198 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10253 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10199 <trans-unit id="7895294730547405228" datatype="html"> 10254 <trans-unit id="7895294730547405228" datatype="html">
10200 <source>Enter/exit fullscreen (requires player focus)</source> 10255 <source>Enter/exit fullscreen (requires player focus)</source>
10201 <target state="translated">Entra / esci dallo schermo intero (richiede focus player)</target> 10256 <target state="translated">Entra / esci dallo schermo intero (richiede focus player)</target>
10202 10257
10203 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10258 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10204 <trans-unit id="7618388257165864759" datatype="html"> 10259 <trans-unit id="7618388257165864759" datatype="html">
10205 <source>Play/Pause the video (requires player focus)</source> 10260 <source>Play/Pause the video (requires player focus)</source>
10206 <target state="translated">Riproduci / Metti in pausa il video (richiede focus player)</target> 10261 <target state="translated">Riproduci / Metti in pausa il video (richiede focus player)</target>
10207 10262
10208 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10263 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10209 <trans-unit id="7761890399634216630" datatype="html"> 10264 <trans-unit id="7761890399634216630" datatype="html">
10210 <source>Mute/unmute the video (requires player focus)</source> 10265 <source>Mute/unmute the video (requires player focus)</source>
10211 <target state="translated">Disattiva / riattiva il video (richiede focus player)</target> 10266 <target state="translated">Disattiva / riattiva il video (richiede focus player)</target>
10212 10267
10213 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10268 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10214 <trans-unit id="5996585232248234904" datatype="html"> 10269 <trans-unit id="5996585232248234904" datatype="html">
10215 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10270 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10216 <target state="translated">Passa a una percentuale del video: 0 è 0% e 9 è 90% (richiede il focus del player)</target> 10271 <target state="translated">Passa a una percentuale del video: 0 è 0% e 9 è 90% (richiede il focus del player)</target>
10217 10272
10218 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10273 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10219 <trans-unit id="3748765405903319998" datatype="html"> 10274 <trans-unit id="3748765405903319998" datatype="html">
10220 <source>Increase the volume (requires player focus)</source> 10275 <source>Increase the volume (requires player focus)</source>
10221 <target state="translated">Aumenta il volume (richiede il focus del player)</target> 10276 <target state="translated">Aumenta il volume (richiede il focus del player)</target>
10222 10277
10223 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10278 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10224 <trans-unit id="5810704036407159982" datatype="html"> 10279 <trans-unit id="5810704036407159982" datatype="html">
10225 <source>Decrease the volume (requires player focus)</source> 10280 <source>Decrease the volume (requires player focus)</source>
10226 <target state="translated">Abbassa il volume (richiede il focus del player)</target> 10281 <target state="translated">Abbassa il volume (richiede il focus del player)</target>
10227 10282
10228 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10283 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10229 <trans-unit id="2622048822548065691" datatype="html"> 10284 <trans-unit id="2622048822548065691" datatype="html">
10230 <source>Seek the video forward (requires player focus)</source> 10285 <source>Seek the video forward (requires player focus)</source>
10231 <target state="translated">Cerca il video in avanti (richiede focus player)</target> 10286 <target state="translated">Cerca il video in avanti (richiede focus player)</target>
10232 10287
10233 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10288 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10234 <trans-unit id="6540078205109221153" datatype="html"> 10289 <trans-unit id="6540078205109221153" datatype="html">
10235 <source>Seek the video backward (requires player focus)</source> 10290 <source>Seek the video backward (requires player focus)</source>
10236 <target state="translated">Cerca il video all'indietro (richiede focus player)</target> 10291 <target state="translated">Cerca il video all'indietro (richiede focus player)</target>
10237 10292
10238 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10293 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10239 <trans-unit id="1956491957766210808" datatype="html"> 10294 <trans-unit id="1956491957766210808" datatype="html">
10240 <source>Increase playback rate (requires player focus)</source> 10295 <source>Increase playback rate (requires player focus)</source>
10241 <target state="translated">Aumenta la velocità di riproduzione (richiede focus del player)</target> 10296 <target state="translated">Aumenta la velocità di riproduzione (richiede focus del player)</target>
10242 10297
10243 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10298 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10244 <trans-unit id="5495529997674803186" datatype="html"> 10299 <trans-unit id="5495529997674803186" datatype="html">
10245 <source>Decrease playback rate (requires player focus)</source> 10300 <source>Decrease playback rate (requires player focus)</source>
10246 <target state="translated">Diminuisci la velocità di riproduzione (richiede la messa a fuoco del player)</target> 10301 <target state="translated">Diminuisci la velocità di riproduzione (richiede la messa a fuoco del player)</target>
10247 10302
10248 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10303 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10249 <trans-unit id="3178343147230721210" datatype="html"> 10304 <trans-unit id="3178343147230721210" datatype="html">
10250 <source>Navigate in the video frame by frame (requires player focus)</source> 10305 <source>Navigate in the video frame by frame (requires player focus)</source>
10251 <target state="translated">Navigare nel video fotogramma per fotogramma (richiede focus player)</target> 10306 <target state="translated">Navigare nel video fotogramma per fotogramma (richiede focus player)</target>
10252 10307
10253 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10308 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10254 <trans-unit id="8025996572234182184"> 10309 <trans-unit id="8025996572234182184">
10255 <source>Like the video</source> 10310 <source>Like the video</source>
10256 <target>Mi piace</target> 10311 <target>Mi piace</target>
diff --git a/client/src/locale/angular.ja-JP.xlf b/client/src/locale/angular.ja-JP.xlf
index 2354692c8..942e10017 100644
--- a/client/src/locale/angular.ja-JP.xlf
+++ b/client/src/locale/angular.ja-JP.xlf
@@ -215,19 +215,19 @@
215 <trans-unit id="187187500641108332" datatype="html"> 215 <trans-unit id="187187500641108332" datatype="html">
216 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source> 216 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source>
217 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target> 217 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target>
218 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group> 218
219 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group> 219
220 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group> 220
221 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group> 221
222 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group> 222
223 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 223
224 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group> 224
225 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group> 225
226 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group> 226
227 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group> 227
228 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group> 228
229 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group> 229
230 </trans-unit> 230 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
231 <trans-unit id="1486537403020619891" datatype="html"> 231 <trans-unit id="1486537403020619891" datatype="html">
232 <source>My watch history</source> 232 <source>My watch history</source>
233 <target state="new">My watch history</target> 233 <target state="new">My watch history</target>
@@ -303,22 +303,22 @@
303 <trans-unit id="5674286808255988565"> 303 <trans-unit id="5674286808255988565">
304 <source>Create</source> 304 <source>Create</source>
305 <target>作成</target> 305 <target>作成</target>
306 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 306
307 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 307
308 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 308
309 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 309
310 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 310
311 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 311
312 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 312
313 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 313
314 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 314
315 </trans-unit> 315 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
316 <trans-unit id="1006562256968398209" datatype="html"> 316 <trans-unit id="1006562256968398209" datatype="html">
317 <source>video</source> 317 <source>video</source>
318 <target state="new">video</target> 318 <target state="new">video</target>
319 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group> 319
320 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group> 320
321 </trans-unit> 321 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
322 <trans-unit id="6438815964972582865" datatype="html"> 322 <trans-unit id="6438815964972582865" datatype="html">
323 <source>The following link contains a private token and should not be shared with anyone.</source> 323 <source>The following link contains a private token and should not be shared with anyone.</source>
324 <target state="new"> The following link contains a private token and should not be shared with anyone. </target> 324 <target state="new"> The following link contains a private token and should not be shared with anyone. </target>
@@ -388,13 +388,13 @@
388 <trans-unit id="6995024616159044376" datatype="html"> 388 <trans-unit id="6995024616159044376" datatype="html">
389 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 389 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
390 <target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 390 <target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
391 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 391
392 </trans-unit> 392 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
393 <trans-unit id="7873395933409147217" datatype="html"> 393 <trans-unit id="7873395933409147217" datatype="html">
394 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 394 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
395 <target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 395 <target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
396 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 396
397 </trans-unit> 397 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
398 <trans-unit id="5235042777215655908" datatype="html"> 398 <trans-unit id="5235042777215655908" datatype="html">
399 <source>subtitles</source> 399 <source>subtitles</source>
400 <target state="translated">字幕</target> 400 <target state="translated">字幕</target>
@@ -834,10 +834,10 @@
834 <trans-unit id="2602586221576511475"> 834 <trans-unit id="2602586221576511475">
835 <source>Video quota</source> 835 <source>Video quota</source>
836 <target>動画容量の制限</target> 836 <target>動画容量の制限</target>
837 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 837
838 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 838
839 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 839
840 </trans-unit> 840 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
841 <trans-unit id="1502595455339510144"> 841 <trans-unit id="1502595455339510144">
842 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 842 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
843 <target>無制限 <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/>/ 日) <x id="CLOSE_TAG_NG_CONTAINER"/></target> 843 <target>無制限 <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/>/ 日) <x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -917,6 +917,30 @@
917 <target state="translated">連合</target> 917 <target state="translated">連合</target>
918 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 918 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
919 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 919 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
920 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
921 <source>Following</source><target state="new">Following</target>
922 <context-group purpose="location">
923 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
924 <context context-type="linenumber">29</context>
925 </context-group>
926 <context-group purpose="location">
927 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
928 <context context-type="linenumber">31</context>
929 </context-group>
930 <context-group purpose="location">
931 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
932 <context context-type="linenumber">28</context>
933 </context-group>
934 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
935 <source>Followers</source><target state="new">Followers</target>
936 <context-group purpose="location">
937 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
938 <context context-type="linenumber">34</context>
939 </context-group>
940 <context-group purpose="location">
941 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
942 <context context-type="linenumber">37</context>
943 </context-group>
920 </trans-unit> 944 </trans-unit>
921 <trans-unit id="3541687134897970106" datatype="html"> 945 <trans-unit id="3541687134897970106" datatype="html">
922 <source>followers</source> 946 <source>followers</source>
@@ -980,25 +1004,25 @@
980 <trans-unit id="2159130950882492111"> 1004 <trans-unit id="2159130950882492111">
981 <source>Cancel</source> 1005 <source>Cancel</source>
982 <target>キャンセル</target> 1006 <target>キャンセル</target>
983 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group> 1007
984 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group> 1008
985 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group> 1009
986 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group> 1010
987 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group> 1011
988 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group> 1012
989 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group> 1013
990 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group> 1014
991 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group> 1015
992 <context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group> 1016
993 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group> 1017
994 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group> 1018
995 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 1019
996 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 1020
997 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group> 1021
998 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group> 1022
999 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group> 1023
1000 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group> 1024
1001 </trans-unit> 1025 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
1002 <trans-unit id="3616223838716839702"> 1026 <trans-unit id="3616223838716839702">
1003 <source>Ban this user</source> 1027 <source>Ban this user</source>
1004 <target>このユーザーをBANする</target> 1028 <target>このユーザーをBANする</target>
@@ -1071,19 +1095,13 @@
1071 <trans-unit id="7252854992688790751" datatype="html"> 1095 <trans-unit id="7252854992688790751" datatype="html">
1072 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1096 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1073 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1097 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1074 <context-group purpose="location"> 1098
1075 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1076 <context context-type="linenumber">60,62</context>
1077 </context-group>
1078 </trans-unit>
1079 <trans-unit id="7215649348148521605" datatype="html"> 1100 <trans-unit id="7215649348148521605" datatype="html">
1080 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1101 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1081 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1102 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1082 <context-group purpose="location"> 1103
1083 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1084 <context context-type="linenumber">65,67</context>
1085 </context-group>
1086 </trans-unit>
1087 <trans-unit id="2392488717875840729"> 1105 <trans-unit id="2392488717875840729">
1088 <source>User</source> 1106 <source>User</source>
1089 <target>ユーザー</target> 1107 <target>ユーザー</target>
@@ -1094,67 +1112,67 @@
1094 <source>Username or email address</source> 1112 <source>Username or email address</source>
1095 <target>ユーザー名かメールアドレス</target> 1113 <target>ユーザー名かメールアドレス</target>
1096 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1114 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1115 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1116 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1117 <context-group purpose="location">
1118 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1119 <context context-type="linenumber">33,34</context>
1120 </context-group>
1097 </trans-unit> 1121 </trans-unit>
1098 <trans-unit id="1431416938026210429"> 1122 <trans-unit id="1431416938026210429">
1099 <source>Password</source> 1123 <source>Password</source>
1100 <target>パスワード</target> 1124 <target>パスワード</target>
1101 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1125
1102 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1126
1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1127
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1128
1105 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1129
1106 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1130
1107 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1131
1108 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1132
1109 </trans-unit> 1133 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1110 <trans-unit id="8715156686857791956" datatype="html"> 1134 <trans-unit id="8715156686857791956" datatype="html">
1111 <source>Click here to reset your password</source> 1135 <source>Click here to reset your password</source>
1112 <target state="translated">クリックしてパスワードをリセットします</target> 1136 <target state="translated">クリックしてパスワードをリセットします</target>
1113 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1137
1114 </trans-unit> 1138 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1115 <trans-unit id="892063502898494584" datatype="html"> 1139 <trans-unit id="892063502898494584" datatype="html">
1116 <source>I forgot my password</source> 1140 <source>I forgot my password</source>
1117 <target state="translated">パスワードを忘れました</target> 1141 <target state="translated">パスワードを忘れました</target>
1118 <context-group purpose="location"> 1142
1119 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1143 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1120 <context context-type="linenumber">47</context>
1121 </context-group>
1122 </trans-unit>
1123 <trans-unit id="2101170466365500913" datatype="html"> 1144 <trans-unit id="2101170466365500913" datatype="html">
1124 <source>Logging into an account lets you publish content</source> 1145 <source>Logging into an account lets you publish content</source>
1125 <target state="translated">あなたのアカウントでログインする事で、コンテンツを公開することができます</target> 1146 <target state="translated">あなたのアカウントでログインする事で、コンテンツを公開することができます</target>
1126 <context-group purpose="location"> 1147
1127 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1148 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1128 <context context-type="linenumber">56,57</context>
1129 </context-group>
1130 </trans-unit>
1131 <trans-unit id="2454050363478003966"> 1149 <trans-unit id="2454050363478003966">
1132 <source>Login</source> 1150 <source>Login</source>
1133 <target>ログイン</target> 1151 <target>ログイン</target>
1134 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1152
1135 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1153
1136 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1154
1137 </trans-unit> 1155 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1138 <trans-unit id="3183213940445113677" datatype="html"> 1156 <trans-unit id="3183213940445113677" datatype="html">
1139 <source>Or sign in with</source> 1157 <source>Or sign in with</source>
1140 <target state="new">Or sign in with</target> 1158 <target state="new">Or sign in with</target>
1141 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1159
1142 </trans-unit> 1160 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1143 <trans-unit id="3238209155172574367"> 1161 <trans-unit id="3238209155172574367">
1144 <source>Forgot your password</source> 1162 <source>Forgot your password</source>
1145 <target>パスワードをお忘れですか</target> 1163 <target>パスワードをお忘れですか</target>
1146 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1164
1147 </trans-unit> 1165 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1148 <trans-unit id="87327320394367488" datatype="html"> 1166 <trans-unit id="87327320394367488" datatype="html">
1149 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1167 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1150 <target state="translated">申し訳ありません。インスタンス管理者がPeerTubeのメールシステムの設定をしていないため、パスワードを復元することができません。</target> 1168 <target state="translated">申し訳ありません。インスタンス管理者がPeerTubeのメールシステムの設定をしていないため、パスワードを復元することができません。</target>
1151 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1169
1152 </trans-unit> 1170 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1153 <trans-unit id="3188014010833256853" datatype="html"> 1171 <trans-unit id="3188014010833256853" datatype="html">
1154 <source>Enter your email address and we will send you a link to reset your password.</source> 1172 <source>Enter your email address and we will send you a link to reset your password.</source>
1155 <target state="translated">メールアドレスを入力すれば、パスワードをリセットするためのリンクが送信されます。</target> 1173 <target state="translated">メールアドレスを入力すれば、パスワードをリセットするためのリンクが送信されます。</target>
1156 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1174
1157 </trans-unit> 1175 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1158 <trans-unit id="1190256911880544559" datatype="html"> 1176 <trans-unit id="1190256911880544559" datatype="html">
1159 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1177 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1160The link will expire within 1 hour.</source> 1178The link will expire within 1 hour.</source>
@@ -1164,26 +1182,26 @@ The link will expire within 1 hour.</source>
1164 <trans-unit id="4768749765465246664"> 1182 <trans-unit id="4768749765465246664">
1165 <source>Email</source> 1183 <source>Email</source>
1166 <target>メールアドレス</target> 1184 <target>メールアドレス</target>
1167 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1185
1168 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1186
1169 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1187
1170 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1188
1171 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1189
1172 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1190
1173 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1191
1174 </trans-unit> 1192 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1175 <trans-unit id="3967269098753656610"> 1193 <trans-unit id="3967269098753656610">
1176 <source>Email address</source> 1194 <source>Email address</source>
1177 <target>メールアドレス</target> 1195 <target>メールアドレス</target>
1178 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1196
1179 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1197
1180 </trans-unit> 1198 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1181 <trans-unit id="7808756054397155068" datatype="html"> 1199 <trans-unit id="7808756054397155068" datatype="html">
1182 <source>Reset</source> 1200 <source>Reset</source>
1183 <target state="translated">リセット</target> 1201 <target state="translated">リセット</target>
1184 <note priority="1" from="description">Password reset button</note> 1202 <note priority="1" from="description">Password reset button</note>
1185 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1203
1186 </trans-unit> 1204 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1187 <trans-unit id="4319634264526091601" datatype="html"> 1205 <trans-unit id="4319634264526091601" datatype="html">
1188 <source>on this instance</source> 1206 <source>on this instance</source>
1189 <target state="new">on this instance</target> 1207 <target state="new">on this instance</target>
@@ -1534,9 +1552,9 @@ The link will expire within 1 hour.</source>
1534 <trans-unit id="2308975396733519902"> 1552 <trans-unit id="2308975396733519902">
1535 <source>Create an account</source> 1553 <source>Create an account</source>
1536 <target>アカウントを作成する</target> 1554 <target>アカウントを作成する</target>
1537 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1555
1538 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1556
1539 </trans-unit> 1557 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1540 <trans-unit id="3058024914967508975" datatype="html"> 1558 <trans-unit id="3058024914967508975" datatype="html">
1541 <source>My videos</source> 1559 <source>My videos</source>
1542 <target state="translated">自分の動画</target> 1560 <target state="translated">自分の動画</target>
@@ -1603,10 +1621,10 @@ The link will expire within 1 hour.</source>
1603 <trans-unit id="1504521795586863905" datatype="html"> 1621 <trans-unit id="1504521795586863905" datatype="html">
1604 <source>VIDEOS</source> 1622 <source>VIDEOS</source>
1605 <target state="translated">動画</target> 1623 <target state="translated">動画</target>
1606 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1624
1607 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1625
1608 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1626
1609 </trans-unit> 1627 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1610 <trans-unit id="667372110624203230" datatype="html"> 1628 <trans-unit id="667372110624203230" datatype="html">
1611 <source>Import jobs concurrency</source> 1629 <source>Import jobs concurrency</source>
1612 <target state="new">Import jobs concurrency</target> 1630 <target state="new">Import jobs concurrency</target>
@@ -1685,8 +1703,8 @@ The link will expire within 1 hour.</source>
1685 <trans-unit id="4424964105331349857" datatype="html"> 1703 <trans-unit id="4424964105331349857" datatype="html">
1686 <source>I'm a teapot</source> 1704 <source>I'm a teapot</source>
1687 <target state="translated">I am a teapot</target> 1705 <target state="translated">I am a teapot</target>
1688 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1706
1689 </trans-unit> 1707 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1690 <trans-unit id="1597262876035959248" datatype="html"> 1708 <trans-unit id="1597262876035959248" datatype="html">
1691 <source>That's an error.</source> 1709 <source>That's an error.</source>
1692 <target state="new">That's an error.</target> 1710 <target state="new">That's an error.</target>
@@ -1779,8 +1797,8 @@ The link will expire within 1 hour.</source>
1779 <trans-unit id="2971365540217107489" datatype="html"> 1797 <trans-unit id="2971365540217107489" datatype="html">
1780 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1798 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1781 <target state="needs-translation">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1799 <target state="needs-translation">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1782 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1800
1783 </trans-unit> 1801 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1784 <trans-unit id="5131854469652959713" datatype="html"> 1802 <trans-unit id="5131854469652959713" datatype="html">
1785 <source>GLOBAL SEARCH</source> 1803 <source>GLOBAL SEARCH</source>
1786 <target state="translated">グローバル検索</target> 1804 <target state="translated">グローバル検索</target>
@@ -2549,8 +2567,8 @@ The link will expire within 1 hour.</source>
2549 <trans-unit id="6161604372916832458" datatype="html"> 2567 <trans-unit id="6161604372916832458" datatype="html">
2550 <source>Upload on hold</source> 2568 <source>Upload on hold</source>
2551 <target state="new">Upload on hold</target> 2569 <target state="new">Upload on hold</target>
2552 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2570
2553 </trans-unit> 2571 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2554 <trans-unit id="285180972645018518" datatype="html"> 2572 <trans-unit id="285180972645018518" datatype="html">
2555 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2573 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2556 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2574 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3241,11 +3259,7 @@ The link will expire within 1 hour.</source>
3241 <target>ID</target> 3259 <target>ID</target>
3242 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3260 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3243 </trans-unit> 3261 </trans-unit>
3244 <trans-unit id="2265605798180116441" datatype="html"> 3262
3245 <source>Follower handle</source>
3246 <target state="new">Follower handle</target>
3247 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3248 </trans-unit>
3249 <trans-unit id="5911214550882917183"> 3263 <trans-unit id="5911214550882917183">
3250 <source>State</source> 3264 <source>State</source>
3251 <target>状態</target> 3265 <target>状態</target>
@@ -3315,11 +3329,7 @@ The link will expire within 1 hour.</source>
3315 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target> 3329 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target>
3316 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3330 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3317 </trans-unit> 3331 </trans-unit>
3318 <trans-unit id="6641024648411549335"> 3332
3319 <source>Host</source>
3320 <target>ホスト</target>
3321 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3322 </trans-unit>
3323 <trans-unit id="6571718060636962350" datatype="html"> 3333 <trans-unit id="6571718060636962350" datatype="html">
3324 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3334 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3325 <target state="new">Redundancy allowed 3335 <target state="new">Redundancy allowed
@@ -3331,9 +3341,9 @@ The link will expire within 1 hour.</source>
3331 <trans-unit id="9160510009013134726" datatype="html"> 3341 <trans-unit id="9160510009013134726" datatype="html">
3332 <source>Unfollow</source> 3342 <source>Unfollow</source>
3333 <target state="new">Unfollow</target> 3343 <target state="new">Unfollow</target>
3334 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3344
3335 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3345
3336 </trans-unit> 3346 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3337 <trans-unit id="8246779176913476983" datatype="html"> 3347 <trans-unit id="8246779176913476983" datatype="html">
3338 <source>Open instance in a new tab</source> 3348 <source>Open instance in a new tab</source>
3339 <target state="translated">新しいタブでインスタンスを開く</target> 3349 <target state="translated">新しいタブでインスタンスを開く</target>
@@ -3344,13 +3354,13 @@ The link will expire within 1 hour.</source>
3344 <trans-unit id="9132918641931433659" datatype="html"> 3354 <trans-unit id="9132918641931433659" datatype="html">
3345 <source>No host found matching current filters.</source> 3355 <source>No host found matching current filters.</source>
3346 <target state="new">No host found matching current filters.</target> 3356 <target state="new">No host found matching current filters.</target>
3347 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3357
3348 </trans-unit> 3358 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3349 <trans-unit id="7274241885665071790" datatype="html"> 3359 <trans-unit id="7274241885665071790" datatype="html">
3350 <source>Your instance is not following anyone.</source> 3360 <source>Your instance is not following anyone.</source>
3351 <target state="translated">あなたのインスタンスはまだ誰もフォローしていません。</target> 3361 <target state="translated">あなたのインスタンスはまだ誰もフォローしていません。</target>
3352 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3362
3353 </trans-unit> 3363 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3354 <trans-unit id="4774348799569692380" datatype="html"> 3364 <trans-unit id="4774348799569692380" datatype="html">
3355 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3365 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3356 <target state="new">Showing 3366 <target state="new">Showing
@@ -3360,16 +3370,8 @@ The link will expire within 1 hour.</source>
3360 </target> 3370 </target>
3361 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3371 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3362 </trans-unit> 3372 </trans-unit>
3363 <trans-unit id="6275803119759621687" datatype="html"> 3373
3364 <source>Follow domains</source> 3374
3365 <target state="translated">ドメインをフォロー</target>
3366 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3367 </trans-unit>
3368 <trans-unit id="1268699198448750610" datatype="html">
3369 <source>Follow instances</source>
3370 <target state="translated">インスタンスをフォローする</target>
3371 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3372 </trans-unit>
3373 <trans-unit id="9216117865911519658" datatype="html"> 3375 <trans-unit id="9216117865911519658" datatype="html">
3374 <source>Action</source> 3376 <source>Action</source>
3375 <target state="new">Action</target> 3377 <target state="new">Action</target>
@@ -3419,11 +3421,11 @@ The link will expire within 1 hour.</source>
3419 <trans-unit id="5248717555542428023"> 3421 <trans-unit id="5248717555542428023">
3420 <source>Username</source> 3422 <source>Username</source>
3421 <target>ユーザー名</target> 3423 <target>ユーザー名</target>
3422 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3424
3423 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3425
3424 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3426
3425 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3427
3426 </trans-unit> 3428 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3427 <trans-unit id="5428411040014095392" datatype="html"> 3429 <trans-unit id="5428411040014095392" datatype="html">
3428 <source>e.g. jane_doe</source> 3430 <source>e.g. jane_doe</source>
3429 <target state="translated">e.g.jane_doe</target> 3431 <target state="translated">e.g.jane_doe</target>
@@ -3453,9 +3455,9 @@ The link will expire within 1 hour.</source>
3453 <trans-unit id="4145496584631696119"> 3455 <trans-unit id="4145496584631696119">
3454 <source>Role</source> 3456 <source>Role</source>
3455 <target>権限</target> 3457 <target>権限</target>
3456 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3458
3457 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3459
3458 </trans-unit> 3460 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3459 <trans-unit id="7046347992315328430" datatype="html"> 3461 <trans-unit id="7046347992315328430" datatype="html">
3460 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3462 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3461 <target state="new"> 3463 <target state="new">
@@ -3480,15 +3482,9 @@ The link will expire within 1 hour.</source>
3480 <trans-unit id="2622255144026150901" datatype="html"> 3482 <trans-unit id="2622255144026150901" datatype="html">
3481 <source>Auth plugin</source> 3483 <source>Auth plugin</source>
3482 <target state="new">Auth plugin</target> 3484 <target state="new">Auth plugin</target>
3483 <context-group purpose="location"> 3485
3484 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3486
3485 <context context-type="linenumber">188</context> 3487 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3486 </context-group>
3487 <context-group purpose="location">
3488 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3489 <context context-type="linenumber">188</context>
3490 </context-group>
3491 </trans-unit>
3492 <trans-unit id="588099657508661970" datatype="html"> 3488 <trans-unit id="588099657508661970" datatype="html">
3493 <source>None (local authentication)</source> 3489 <source>None (local authentication)</source>
3494 <target state="new">None (local authentication)</target> 3490 <target state="new">None (local authentication)</target>
@@ -3745,6 +3741,12 @@ The link will expire within 1 hour.</source>
3745 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3741 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3746 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3742 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3747 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3743 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3744 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3745 <source>Follower</source><target state="new">Follower</target>
3746 <context-group purpose="location">
3747 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3748 <context context-type="linenumber">24</context>
3749 </context-group>
3748 </trans-unit> 3750 </trans-unit>
3749 <trans-unit id="4691552465058437520" datatype="html"> 3751 <trans-unit id="4691552465058437520" datatype="html">
3750 <source>Commented video</source> 3752 <source>Commented video</source>
@@ -4064,8 +4066,8 @@ The link will expire within 1 hour.</source>
4064 <target state="new"> 4066 <target state="new">
4065 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 4067 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
4066 </target> 4068 </target>
4067 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 4069
4068 </trans-unit> 4070 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
4069 <trans-unit id="4058814854824495833" datatype="html"> 4071 <trans-unit id="4058814854824495833" datatype="html">
4070 <source>Mute domains</source> 4072 <source>Mute domains</source>
4071 <target state="translated">ミュートしたドメイン</target> 4073 <target state="translated">ミュートしたドメイン</target>
@@ -6032,11 +6034,8 @@ color: red;
6032 <trans-unit id="5512878593724620692" datatype="html"> 6034 <trans-unit id="5512878593724620692" datatype="html">
6033 <source>CHANNELS</source> 6035 <source>CHANNELS</source>
6034 <target state="translated">チャンネル</target> 6036 <target state="translated">チャンネル</target>
6035 <context-group purpose="location"> 6037
6036 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 6038 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
6037 <context context-type="linenumber">82</context>
6038 </context-group>
6039 </trans-unit>
6040 <trans-unit id="3666829335406793239" datatype="html"> 6039 <trans-unit id="3666829335406793239" datatype="html">
6041 <source>This account does not have channels.</source> 6040 <source>This account does not have channels.</source>
6042 <target state="translated">このアカウントはチャンネルを持ってません。</target> 6041 <target state="translated">このアカウントはチャンネルを持ってません。</target>
@@ -6081,6 +6080,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6081It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 6080It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
6082channel with the same name (<x id="PH_2"/>)!</target> 6081channel with the same name (<x id="PH_2"/>)!</target>
6083 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 6082 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
6083 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
6084 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
6085 <context-group purpose="location">
6086 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
6087 <context context-type="linenumber">48</context>
6088 </context-group>
6084 </trans-unit> 6089 </trans-unit>
6085 <trans-unit id="5387007581996837469" datatype="html"> 6090 <trans-unit id="5387007581996837469" datatype="html">
6086 <source>My Channels</source> 6091 <source>My Channels</source>
@@ -6642,13 +6647,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6642 <trans-unit id="6979021199788941693"> 6647 <trans-unit id="6979021199788941693">
6643 <source>Your message has been sent.</source> 6648 <source>Your message has been sent.</source>
6644 <target>メッセージを送信しました。</target> 6649 <target>メッセージを送信しました。</target>
6645 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group> 6650
6646 </trans-unit> 6651 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6647 <trans-unit id="2072135752262464360"> 6652 <trans-unit id="2072135752262464360">
6648 <source>You already sent this form recently</source> 6653 <source>You already sent this form recently</source>
6649 <target>このフォームに最近送信しています</target> 6654 <target>このフォームに最近送信しています</target>
6650 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group> 6655
6651 </trans-unit> 6656 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6652 <trans-unit id="819067926858619041" datatype="html"> 6657 <trans-unit id="819067926858619041" datatype="html">
6653 <source>Account videos</source> 6658 <source>Account videos</source>
6654 <target state="translated">このアカウントの動画</target> 6659 <target state="translated">このアカウントの動画</target>
@@ -6693,13 +6698,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6693 <target state="new"> 6698 <target state="new">
6694 <x id="PH"/> direct account followers 6699 <x id="PH"/> direct account followers
6695 </target> 6700 </target>
6696 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6701
6697 </trans-unit> 6702 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6698 <trans-unit id="6250999352462648289" datatype="html"> 6703 <trans-unit id="6250999352462648289" datatype="html">
6699 <source>Report this account</source> 6704 <source>Report this account</source>
6700 <target state="translated">アカウントを通報する</target> 6705 <target state="translated">アカウントを通報する</target>
6701 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6706
6702 </trans-unit> 6707 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6703 <trans-unit id="1504521795586863905" datatype="html"> 6708 <trans-unit id="1504521795586863905" datatype="html">
6704 <source>VIDEOS</source> 6709 <source>VIDEOS</source>
6705 <target state="new">VIDEOS</target> 6710 <target state="new">VIDEOS</target>
@@ -6709,31 +6714,21 @@ channel with the same name (<x id="PH_2"/>)!</target>
6709 <trans-unit id="25349740244798533" datatype="html"> 6714 <trans-unit id="25349740244798533" datatype="html">
6710 <source>Username copied</source> 6715 <source>Username copied</source>
6711 <target state="translated">ユーザー名をコピーしました</target> 6716 <target state="translated">ユーザー名をコピーしました</target>
6712 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6717
6713 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6718
6714 </trans-unit> 6719 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6715 <trans-unit id="9221735175659318025" datatype="html"> 6720 <trans-unit id="9221735175659318025" datatype="html">
6716 <source>1 subscriber</source> 6721 <source>1 subscriber</source>
6717 <target state="translated">チャンネル登録者1人</target> 6722 <target state="translated">チャンネル登録者1人</target>
6718 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6723
6719 </trans-unit> 6724 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6720 <trans-unit id="4097331874769079975" datatype="html"> 6725 <trans-unit id="4097331874769079975" datatype="html">
6721 <source><x id="PH"/> subscribers</source> 6726 <source><x id="PH"/> subscribers</source>
6722 <target state="translated">チャンネル登録者数 <x id="PH"/> 人</target> 6727 <target state="translated">チャンネル登録者数 <x id="PH"/> 人</target>
6723 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6728
6724 </trans-unit> 6729 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6725 <trans-unit id="4682675125751819107" datatype="html"> 6730
6726 <source>Instances you follow</source> 6731
6727 <target state="new">Instances you follow</target>
6728 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6729 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6730 </trans-unit>
6731 <trans-unit id="8899833753704589712" datatype="html">
6732 <source>Instances following you</source>
6733 <target state="new">Instances following you</target>
6734 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6735 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6736 </trans-unit>
6737 <trans-unit id="1035838766454786107" datatype="html"> 6732 <trans-unit id="1035838766454786107" datatype="html">
6738 <source>Audio-only</source> 6733 <source>Audio-only</source>
6739 <target state="translated">音声のみ</target> 6734 <target state="translated">音声のみ</target>
@@ -6783,6 +6778,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6783 <source>Auto (via ffmpeg)</source> 6778 <source>Auto (via ffmpeg)</source>
6784 <target>自動 (FFmpeg 経由)</target> 6779 <target>自動 (FFmpeg 経由)</target>
6785 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6780 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6781 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6782 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6783 <context-group purpose="location">
6784 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6785 <context context-type="linenumber">3</context>
6786 </context-group>
6786 </trans-unit> 6787 </trans-unit>
6787 <trans-unit id="931255636742351800" datatype="html"> 6788 <trans-unit id="931255636742351800" datatype="html">
6788 <source>No limit</source> 6789 <source>No limit</source>
@@ -6931,18 +6932,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
6931 <trans-unit id="2127446333083057097" datatype="html"> 6932 <trans-unit id="2127446333083057097" datatype="html">
6932 <source>Domain is required.</source> 6933 <source>Domain is required.</source>
6933 <target state="translated">ドメインが必要です。</target> 6934 <target state="translated">ドメインが必要です。</target>
6934 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6935
6935 </trans-unit> 6936 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6936 <trans-unit id="6780793142903080663" datatype="html"> 6937 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6937 <source>Domains entered are invalid.</source> 6938 <context-group purpose="location">
6938 <target state="translated">入力されたドメインは無効です。</target> 6939 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6939 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6940 <context context-type="linenumber">93</context>
6940 </trans-unit> 6941 </context-group>
6941 <trans-unit id="5886492514458202177" datatype="html"> 6942 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6942 <source>Domains entered contain duplicates.</source> 6943 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6943 <target state="new">Domains entered contain duplicates.</target> 6944 <context-group purpose="location">
6944 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6945 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6946 <context context-type="linenumber">94</context>
6947 </context-group>
6948 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6949 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6950 <context-group purpose="location">
6951 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6952 <context context-type="linenumber">102</context>
6953 </context-group>
6954 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6955 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6956 <context-group purpose="location">
6957 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6958 <context context-type="linenumber">103</context>
6959 </context-group>
6945 </trans-unit> 6960 </trans-unit>
6961
6962
6946 <trans-unit id="240806681889331244"> 6963 <trans-unit id="240806681889331244">
6947 <source>Unlimited</source> 6964 <source>Unlimited</source>
6948 <target>無制限</target> 6965 <target>無制限</target>
@@ -7098,24 +7115,50 @@ channel with the same name (<x id="PH_2"/>)!</target>
7098 <x id="PH"/> removed from instance followers 7115 <x id="PH"/> removed from instance followers
7099 </target> 7116 </target>
7100 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 7117 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
7118 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
7119 <source>Follow</source><target state="new">Follow</target>
7120 <context-group purpose="location">
7121 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7122 <context context-type="linenumber">3</context>
7123 </context-group>
7124 <context-group purpose="location">
7125 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7126 <context context-type="linenumber">37</context>
7127 </context-group>
7128 <context-group purpose="location">
7129 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7130 <context context-type="linenumber">18</context>
7131 </context-group>
7132 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
7133 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
7134 <context-group purpose="location">
7135 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7136 <context context-type="linenumber">11</context>
7137 </context-group>
7101 </trans-unit> 7138 </trans-unit>
7102 <trans-unit id="2740793005745065895"> 7139 <trans-unit id="2740793005745065895">
7103 <source><x id="PH"/> is not valid </source> 7140 <source><x id="PH"/> is not valid </source>
7104 <target> 7141 <target>
7105 <x id="PH"/> は無効です。 7142 <x id="PH"/> は無効です。
7106 </target> 7143 </target>
7107 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 7144
7108 </trans-unit> 7145 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
7109 <trans-unit id="2355066641781598196"> 7146 <trans-unit id="2355066641781598196">
7110 <source>Follow request(s) sent!</source> 7147 <source>Follow request(s) sent!</source>
7111 <target>フォローリクエストを送信しました!</target> 7148 <target>フォローリクエストを送信しました!</target>
7112 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 7149
7150 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
7151 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
7152 <context-group purpose="location">
7153 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7154 <context context-type="linenumber">3</context>
7155 </context-group>
7113 </trans-unit> 7156 </trans-unit>
7114 <trans-unit id="4245720728052819482"> 7157 <trans-unit id="4245720728052819482">
7115 <source>Do you really want to unfollow <x id="PH"/>?</source> 7158 <source>Do you really want to unfollow <x id="PH"/>?</source>
7116 <target>本当に <x id="PH"/> のフォローを解除しますか?</target> 7159 <target>本当に <x id="PH"/> のフォローを解除しますか?</target>
7117 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 7160
7118 </trans-unit> 7161 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
7119 <trans-unit id="9160510009013134726"> 7162 <trans-unit id="9160510009013134726">
7120 <source>Unfollow</source> 7163 <source>Unfollow</source>
7121 <target>フォロー解除</target> 7164 <target>フォロー解除</target>
@@ -7124,8 +7167,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7124 <trans-unit id="3935234189109112926"> 7167 <trans-unit id="3935234189109112926">
7125 <source>You are not following <x id="PH"/> anymore.</source> 7168 <source>You are not following <x id="PH"/> anymore.</source>
7126 <target>あなたはもう <x id="PH"/> をフォローしていません。</target> 7169 <target>あなたはもう <x id="PH"/> をフォローしていません。</target>
7127 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 7170
7128 </trans-unit> 7171 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
7129 <trans-unit id="2593763089859685916"> 7172 <trans-unit id="2593763089859685916">
7130 <source>enabled</source> 7173 <source>enabled</source>
7131 <target>有効</target> 7174 <target>有効</target>
@@ -7597,9 +7640,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7597 <trans-unit id="1519954996184640001"> 7640 <trans-unit id="1519954996184640001">
7598 <source>Error</source> 7641 <source>Error</source>
7599 <target>エラー</target> 7642 <target>エラー</target>
7600 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7643
7601 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7644
7602 </trans-unit> 7645 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7603 <trans-unit id="5076187961693950167" datatype="html"> 7646 <trans-unit id="5076187961693950167" datatype="html">
7604 <source>Standard logs</source> 7647 <source>Standard logs</source>
7605 <target state="translated">標準のログ</target> 7648 <target state="translated">標準のログ</target>
@@ -7640,16 +7683,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7640 <target>ユーザーパスワードを更新する</target> 7683 <target>ユーザーパスワードを更新する</target>
7641 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7684 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7642 </trans-unit> 7685 </trans-unit>
7643 <trans-unit id="177544274549739411" datatype="html"> 7686
7644 <source>Following list</source> 7687
7645 <target state="translated">フォローリスト</target>
7646 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7647 </trans-unit>
7648 <trans-unit id="8092429110007204784" datatype="html">
7649 <source>Followers list</source>
7650 <target state="translated">フォロワーリスト</target>
7651 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7652 </trans-unit>
7653 <trans-unit id="780323526182667308" datatype="html"> 7688 <trans-unit id="780323526182667308" datatype="html">
7654 <source>User <x id="PH"/> updated.</source> 7689 <source>User <x id="PH"/> updated.</source>
7655 <target state="translated">User <x id="PH"/> updated.</target> 7690 <target state="translated">User <x id="PH"/> updated.</target>
@@ -7685,16 +7720,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7685 <target state="translated">他インスタンスとの連合</target> 7720 <target state="translated">他インスタンスとの連合</target>
7686 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7721 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7687 </trans-unit> 7722 </trans-unit>
7688 <trans-unit id="4682675125751819107" datatype="html"> 7723
7689 <source>Instances you follow</source> 7724
7690 <target state="translated">フォローしているインスタンス</target>
7691 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7692 </trans-unit>
7693 <trans-unit id="8899833753704589712" datatype="html">
7694 <source>Instances following you</source>
7695 <target state="translated">フォローされているインスタンス</target>
7696 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7697 </trans-unit>
7698 <trans-unit id="3767259920053407667" datatype="html"> 7725 <trans-unit id="3767259920053407667" datatype="html">
7699 <source>Videos will be deleted, comments will be tombstoned.</source> 7726 <source>Videos will be deleted, comments will be tombstoned.</source>
7700 <target state="translated">動画が削除され、コメントも削除されます。</target> 7727 <target state="translated">動画が削除され、コメントも削除されます。</target>
@@ -7725,6 +7752,24 @@ channel with the same name (<x id="PH_2"/>)!</target>
7725 <target>メールを確認済みとして設定</target> 7752 <target>メールを確認済みとして設定</target>
7726 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7753 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7727 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7754 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7755 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7756 <source>Created</source><target state="new">Created</target>
7757 <context-group purpose="location">
7758 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7759 <context context-type="linenumber">115</context>
7760 </context-group>
7761 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7762 <source>Daily quota</source><target state="new">Daily quota</target>
7763 <context-group purpose="location">
7764 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7765 <context context-type="linenumber">120</context>
7766 </context-group>
7767 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7768 <source>Last login</source><target state="new">Last login</target>
7769 <context-group purpose="location">
7770 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7771 <context context-type="linenumber">122</context>
7772 </context-group>
7728 </trans-unit> 7773 </trans-unit>
7729 <trans-unit id="3403978719736970622"> 7774 <trans-unit id="3403978719736970622">
7730 <source>You cannot ban root.</source> 7775 <source>You cannot ban root.</source>
@@ -8029,13 +8074,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
8029 <trans-unit id="1137937154872046253"> 8074 <trans-unit id="1137937154872046253">
8030 <source>Video channel <x id="PH"/> created.</source> 8075 <source>Video channel <x id="PH"/> created.</source>
8031 <target>動画チャンネル <x id="PH"/> を作成しました。</target> 8076 <target>動画チャンネル <x id="PH"/> を作成しました。</target>
8032 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 8077
8033 </trans-unit> 8078 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
8034 <trans-unit id="8723777130353305761"> 8079 <trans-unit id="8723777130353305761">
8035 <source>This name already exists on this instance.</source> 8080 <source>This name already exists on this instance.</source>
8036 <target>この名前は、このインスタンス上に既に存在します。</target> 8081 <target>この名前は、このインスタンス上に既に存在します。</target>
8037 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 8082
8038 </trans-unit> 8083 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
8039 <trans-unit id="7589345916094713536"> 8084 <trans-unit id="7589345916094713536">
8040 <source>Video channel <x id="PH"/> updated.</source> 8085 <source>Video channel <x id="PH"/> updated.</source>
8041 <target>動画チャンネル: <x id="PH"/> を更新しました。</target> 8086 <target>動画チャンネル: <x id="PH"/> を更新しました。</target>
@@ -8056,11 +8101,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8056 <target state="translated">バナーが削除されました。</target> 8101 <target state="translated">バナーが削除されました。</target>
8057 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 8102 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
8058 </trans-unit> 8103 </trans-unit>
8059 <trans-unit id="2575302837003821736"> 8104
8060 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
8061 <target>動画チャンネル ( <x id="PH"/>) の表示名を入力してください</target>
8062 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
8063 </trans-unit>
8064 <trans-unit id="624066830180032195"> 8105 <trans-unit id="624066830180032195">
8065 <source>Video channel <x id="PH"/> deleted.</source> 8106 <source>Video channel <x id="PH"/> deleted.</source>
8066 <target>動画チャンネル <x id="PH"/> を削除しました。</target> 8107 <target>動画チャンネル <x id="PH"/> を削除しました。</target>
@@ -8212,6 +8253,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8212 <source>Ownership change request sent.</source> 8253 <source>Ownership change request sent.</source>
8213 <target>所有権の変更リクエストが送信されました。</target> 8254 <target>所有権の変更リクエストが送信されました。</target>
8214 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8255 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8256 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8257 <source>Sort by</source><target state="new">Sort by</target>
8258 <context-group purpose="location">
8259 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8260 <context context-type="linenumber">26</context>
8261 </context-group>
8215 </trans-unit> 8262 </trans-unit>
8216 <trans-unit id="3245220240937722814"> 8263 <trans-unit id="3245220240937722814">
8217 <source>My channels</source> 8264 <source>My channels</source>
@@ -8310,7 +8357,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8310 <target>アカウントを購読する</target> 8357 <target>アカウントを購読する</target>
8311 8358
8312 8359
8313 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8360 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8314 <trans-unit id="3131904093925601441" datatype="html"> 8361 <trans-unit id="3131904093925601441" datatype="html">
8315 <source>PLAYLISTS</source> 8362 <source>PLAYLISTS</source>
8316 <target state="translated">プレイリスト</target> 8363 <target state="translated">プレイリスト</target>
@@ -8357,35 +8404,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8357 <trans-unit id="3779524668013120370"> 8404 <trans-unit id="3779524668013120370">
8358 <source>Go to my subscriptions</source> 8405 <source>Go to my subscriptions</source>
8359 <target>自分の登録チャンネルへ移動</target> 8406 <target>自分の登録チャンネルへ移動</target>
8360 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8407
8361 </trans-unit> 8408 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8362 <trans-unit id="1136469849928650779"> 8409 <trans-unit id="1136469849928650779">
8363 <source>Go to my videos</source> 8410 <source>Go to my videos</source>
8364 <target>動画一覧</target> 8411 <target>動画一覧</target>
8365 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8412
8366 </trans-unit> 8413 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8367 <trans-unit id="7836683738999600376"> 8414 <trans-unit id="7836683738999600376">
8368 <source>Go to my imports</source> 8415 <source>Go to my imports</source>
8369 <target>インポートした動画一覧</target> 8416 <target>インポートした動画一覧</target>
8370 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8417
8371 </trans-unit> 8418 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8372 <trans-unit id="7511292153332773503"> 8419 <trans-unit id="7511292153332773503">
8373 <source>Go to my channels</source> 8420 <source>Go to my channels</source>
8374 <target>マイチャンネルに移動する</target> 8421 <target>マイチャンネルに移動する</target>
8375 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8422
8376 </trans-unit> 8423 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8377 <trans-unit id="2013324644839511073" datatype="html"> 8424 <trans-unit id="2013324644839511073" datatype="html">
8378 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8425 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8379Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8426Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8380 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8427 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8381Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8428Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8382 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8429
8383 </trans-unit> 8430 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8384 <trans-unit id="375263728166936544"> 8431 <trans-unit id="375263728166936544">
8385 <source>You need to reconnect.</source> 8432 <source>You need to reconnect.</source>
8386 <target>再接続する必要があります。</target> 8433 <target>再接続する必要があります。</target>
8387 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8434
8388 </trans-unit> 8435 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8389 <trans-unit id="2206638022166154361"> 8436 <trans-unit id="2206638022166154361">
8390 <source>Keyboard Shortcuts:</source> 8437 <source>Keyboard Shortcuts:</source>
8391 <target>キーボードショートカット:</target> 8438 <target>キーボードショートカット:</target>
@@ -8398,6 +8445,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8398 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8445 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8399 <context context-type="linenumber">98</context> 8446 <context context-type="linenumber">98</context>
8400 </context-group> 8447 </context-group>
8448 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8449 <source>In my library</source><target state="new">In my library</target>
8450 <context-group purpose="location">
8451 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8452 <context context-type="linenumber">104</context>
8453 </context-group>
8401 </trans-unit> 8454 </trans-unit>
8402 <trans-unit id="232050922346936574" datatype="html"> 8455 <trans-unit id="232050922346936574" datatype="html">
8403 <source>Trending</source> 8456 <source>Trending</source>
@@ -8426,38 +8479,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8426 <trans-unit id="1266887509445371246"> 8479 <trans-unit id="1266887509445371246">
8427 <source>Incorrect username or password.</source> 8480 <source>Incorrect username or password.</source>
8428 <target>ユーザー名またはパスワードが違います。</target> 8481 <target>ユーザー名またはパスワードが違います。</target>
8429 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8482
8430 </trans-unit> 8483 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8431 <trans-unit id="6974874606619467663" datatype="html"> 8484 <trans-unit id="6974874606619467663" datatype="html">
8432 <source>Your account is blocked.</source> 8485 <source>Your account is blocked.</source>
8433 <target state="translated">あなたのアカウントはブロックされてます。</target> 8486 <target state="translated">あなたのアカウントはブロックされてます。</target>
8434 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8487
8435 </trans-unit> 8488 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8436 <trans-unit id="7939914198003891823" datatype="html"> 8489 <trans-unit id="7939914198003891823" datatype="html">
8437 <source>any language</source> 8490 <source>any language</source>
8438 <target state="new">any language</target> 8491 <target state="new">any language</target>
8439 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8492
8440 </trans-unit> 8493 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8441 <trans-unit id="5633144232269377096" datatype="html"> 8494 <trans-unit id="5633144232269377096" datatype="html">
8442 <source>hide</source> 8495 <source>hide</source>
8443 <target state="translated">表示しない</target> 8496 <target state="translated">表示しない</target>
8444 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8497
8445 </trans-unit> 8498 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8446 <trans-unit id="8603861867909474404" datatype="html"> 8499 <trans-unit id="8603861867909474404" datatype="html">
8447 <source>blur</source> 8500 <source>blur</source>
8448 <target state="translated">ぼかす</target> 8501 <target state="translated">ぼかす</target>
8449 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8502
8450 </trans-unit> 8503 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8451 <trans-unit id="4534458451100881847" datatype="html"> 8504 <trans-unit id="4534458451100881847" datatype="html">
8452 <source>display</source> 8505 <source>display</source>
8453 <target state="translated">表示する</target> 8506 <target state="translated">表示する</target>
8454 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8507
8455 </trans-unit> 8508 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8456 <trans-unit id="4467323362722952678" datatype="html"> 8509 <trans-unit id="4467323362722952678" datatype="html">
8457 <source>Unknown</source> 8510 <source>Unknown</source>
8458 <target state="new">Unknown</target> 8511 <target state="new">Unknown</target>
8459 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8512
8460 </trans-unit> 8513 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8461 <trans-unit id="8781423666414310853"> 8514 <trans-unit id="8781423666414310853">
8462 <source>Your password has been successfully reset!</source> 8515 <source>Your password has been successfully reset!</source>
8463 <target>パスワードは正常にリセットされました!</target> 8516 <target>パスワードは正常にリセットされました!</target>
@@ -10033,18 +10086,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10033 <trans-unit id="968295009933361070"> 10086 <trans-unit id="968295009933361070">
10034 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 10087 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
10035 <target>試行回数が多すぎます。 <x id="PH"/> 分後にもう一度お試しください。</target> 10088 <target>試行回数が多すぎます。 <x id="PH"/> 分後にもう一度お試しください。</target>
10036 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 10089
10037 </trans-unit> 10090 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
10038 <trans-unit id="4965472196059235310"> 10091 <trans-unit id="4965472196059235310">
10039 <source>Too many attempts, please try again later.</source> 10092 <source>Too many attempts, please try again later.</source>
10040 <target>試行回数が多すぎます。しばらくしてからもう一度お試しください。</target> 10093 <target>試行回数が多すぎます。しばらくしてからもう一度お試しください。</target>
10041 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 10094
10042 </trans-unit> 10095 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
10043 <trans-unit id="1693549688987384699"> 10096 <trans-unit id="1693549688987384699">
10044 <source>Server error. Please retry later.</source> 10097 <source>Server error. Please retry later.</source>
10045 <target>サーバーエラーです。 後でもう一度やり直してください。</target> 10098 <target>サーバーエラーです。 後でもう一度やり直してください。</target>
10046 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 10099
10047 </trans-unit> 10100 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
10048 <trans-unit id="5927402622550505067" datatype="html"> 10101 <trans-unit id="5927402622550505067" datatype="html">
10049 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 10102 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
10050 <target state="translated">Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</target> 10103 <target state="translated">Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</target>
@@ -10638,33 +10691,33 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10638 <trans-unit id="3284171506518522275"> 10691 <trans-unit id="3284171506518522275">
10639 <source>Your video was uploaded to your account and is private.</source> 10692 <source>Your video was uploaded to your account and is private.</source>
10640 <target>動画はこのアカウントに非公開でアップロードされています。</target> 10693 <target>動画はこのアカウントに非公開でアップロードされています。</target>
10641 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10694
10642 </trans-unit> 10695 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10643 <trans-unit id="5699822024600815733"> 10696 <trans-unit id="5699822024600815733">
10644 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10697 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10645 <target>関連するデータ (タグ、説明など) は失われます。このページから移動してもよろしいですか?</target> 10698 <target>関連するデータ (タグ、説明など) は失われます。このページから移動してもよろしいですか?</target>
10646 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10699
10647 </trans-unit> 10700 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10648 <trans-unit id="1219739004043110649"> 10701 <trans-unit id="1219739004043110649">
10649 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10702 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10650 <target>動画はまだアップロードされていません。このページから移動してもよろしいですか?</target> 10703 <target>動画はまだアップロードされていません。このページから移動してもよろしいですか?</target>
10651 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10704
10652 </trans-unit> 10705 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10653 <trans-unit id="6932865105766151309" datatype="html"> 10706 <trans-unit id="6932865105766151309" datatype="html">
10654 <source>Upload</source> 10707 <source>Upload</source>
10655 <target state="translated">アップロード</target> 10708 <target state="translated">アップロード</target>
10656 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10709
10657 </trans-unit> 10710 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10658 <trans-unit id="8278735427925094503" datatype="html"> 10711 <trans-unit id="8278735427925094503" datatype="html">
10659 <source>Upload <x id="PH"/> </source> 10712 <source>Upload <x id="PH"/> </source>
10660 <target state="translated">アップロード <x id="PH"/> </target> 10713 <target state="translated">アップロード <x id="PH"/> </target>
10661 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10714
10662 </trans-unit> 10715 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10663 <trans-unit id="5981816353437801748"> 10716 <trans-unit id="5981816353437801748">
10664 <source>Video published.</source> 10717 <source>Video published.</source>
10665 <target>動画が投稿されました。</target> 10718 <target>動画が投稿されました。</target>
10666 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10719
10667 </trans-unit> 10720 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10668 <trans-unit id="764164089183618119"> 10721 <trans-unit id="764164089183618119">
10669 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10722 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10670 <target>保存していない変更があります。 ページを移動すると、変更した内容は失われます。</target> 10723 <target>保存していない変更があります。 ページを移動すると、変更した内容は失われます。</target>
@@ -10731,28 +10784,28 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10731 <trans-unit id="961774488937452220" datatype="html"> 10784 <trans-unit id="961774488937452220" datatype="html">
10732 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10785 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10733 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10786 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10734 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group> 10787
10735 </trans-unit> 10788 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10736 <trans-unit id="5761611056224181752" datatype="html"> 10789 <trans-unit id="5761611056224181752" datatype="html">
10737 <source>Redirection</source> 10790 <source>Redirection</source>
10738 <target state="translated">リダイレクト</target> 10791 <target state="translated">リダイレクト</target>
10739 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group> 10792
10740 </trans-unit> 10793 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10741 <trans-unit id="8858527736400081688"> 10794 <trans-unit id="8858527736400081688">
10742 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10795 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10743 <target>この動画には成人向けまたは過激なコンテンツが含まれています。本当に再生しますか?</target> 10796 <target>この動画には成人向けまたは過激なコンテンツが含まれています。本当に再生しますか?</target>
10744 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group> 10797
10745 </trans-unit> 10798 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10746 <trans-unit id="3937119019020041049"> 10799 <trans-unit id="3937119019020041049">
10747 <source>Mature or explicit content</source> 10800 <source>Mature or explicit content</source>
10748 <target>成人向けまたは過激なコンテンツ</target> 10801 <target>成人向けまたは過激なコンテンツ</target>
10749 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group> 10802
10750 </trans-unit> 10803 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10751 <trans-unit id="1755474755114288376" datatype="html"> 10804 <trans-unit id="1755474755114288376" datatype="html">
10752 <source>Up Next</source> 10805 <source>Up Next</source>
10753 <target state="new">Up Next</target> 10806 <target state="new">Up Next</target>
10754 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group> 10807
10755 </trans-unit> 10808 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10756 <trans-unit id="2159130950882492111" datatype="html"> 10809 <trans-unit id="2159130950882492111" datatype="html">
10757 <source>Cancel</source> 10810 <source>Cancel</source>
10758 <target state="translated">キャンセル</target> 10811 <target state="translated">キャンセル</target>
@@ -10761,63 +10814,63 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10761 <trans-unit id="3354816756665089864" datatype="html"> 10814 <trans-unit id="3354816756665089864" datatype="html">
10762 <source>Autoplay is suspended</source> 10815 <source>Autoplay is suspended</source>
10763 <target state="translated">自動再生は停止中です</target> 10816 <target state="translated">自動再生は停止中です</target>
10764 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group> 10817
10765 </trans-unit> 10818 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10766 <trans-unit id="7895294730547405228" datatype="html"> 10819 <trans-unit id="7895294730547405228" datatype="html">
10767 <source>Enter/exit fullscreen (requires player focus)</source> 10820 <source>Enter/exit fullscreen (requires player focus)</source>
10768 <target state="new">Enter/exit fullscreen (requires player focus)</target> 10821 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10769 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group> 10822
10770 </trans-unit> 10823 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10771 <trans-unit id="7618388257165864759" datatype="html"> 10824 <trans-unit id="7618388257165864759" datatype="html">
10772 <source>Play/Pause the video (requires player focus)</source> 10825 <source>Play/Pause the video (requires player focus)</source>
10773 <target state="new">Play/Pause the video (requires player focus)</target> 10826 <target state="new">Play/Pause the video (requires player focus)</target>
10774 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group> 10827
10775 </trans-unit> 10828 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10776 <trans-unit id="7761890399634216630" datatype="html"> 10829 <trans-unit id="7761890399634216630" datatype="html">
10777 <source>Mute/unmute the video (requires player focus)</source> 10830 <source>Mute/unmute the video (requires player focus)</source>
10778 <target state="new">Mute/unmute the video (requires player focus)</target> 10831 <target state="new">Mute/unmute the video (requires player focus)</target>
10779 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group> 10832
10780 </trans-unit> 10833 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10781 <trans-unit id="5996585232248234904" datatype="html"> 10834 <trans-unit id="5996585232248234904" datatype="html">
10782 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10835 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10783 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10836 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10784 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group> 10837
10785 </trans-unit> 10838 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10786 <trans-unit id="3748765405903319998" datatype="html"> 10839 <trans-unit id="3748765405903319998" datatype="html">
10787 <source>Increase the volume (requires player focus)</source> 10840 <source>Increase the volume (requires player focus)</source>
10788 <target state="new">Increase the volume (requires player focus)</target> 10841 <target state="new">Increase the volume (requires player focus)</target>
10789 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group> 10842
10790 </trans-unit> 10843 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10791 <trans-unit id="5810704036407159982" datatype="html"> 10844 <trans-unit id="5810704036407159982" datatype="html">
10792 <source>Decrease the volume (requires player focus)</source> 10845 <source>Decrease the volume (requires player focus)</source>
10793 <target state="new">Decrease the volume (requires player focus)</target> 10846 <target state="new">Decrease the volume (requires player focus)</target>
10794 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group> 10847
10795 </trans-unit> 10848 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10796 <trans-unit id="2622048822548065691" datatype="html"> 10849 <trans-unit id="2622048822548065691" datatype="html">
10797 <source>Seek the video forward (requires player focus)</source> 10850 <source>Seek the video forward (requires player focus)</source>
10798 <target state="new">Seek the video forward (requires player focus)</target> 10851 <target state="new">Seek the video forward (requires player focus)</target>
10799 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group> 10852
10800 </trans-unit> 10853 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10801 <trans-unit id="6540078205109221153" datatype="html"> 10854 <trans-unit id="6540078205109221153" datatype="html">
10802 <source>Seek the video backward (requires player focus)</source> 10855 <source>Seek the video backward (requires player focus)</source>
10803 <target state="new">Seek the video backward (requires player focus)</target> 10856 <target state="new">Seek the video backward (requires player focus)</target>
10804 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group> 10857
10805 </trans-unit> 10858 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10806 <trans-unit id="1956491957766210808" datatype="html"> 10859 <trans-unit id="1956491957766210808" datatype="html">
10807 <source>Increase playback rate (requires player focus)</source> 10860 <source>Increase playback rate (requires player focus)</source>
10808 <target state="translated">再生速度を早くする (アクティブのプレーヤーが必要)</target> 10861 <target state="translated">再生速度を早くする (アクティブのプレーヤーが必要)</target>
10809 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group> 10862
10810 </trans-unit> 10863 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10811 <trans-unit id="5495529997674803186" datatype="html"> 10864 <trans-unit id="5495529997674803186" datatype="html">
10812 <source>Decrease playback rate (requires player focus)</source> 10865 <source>Decrease playback rate (requires player focus)</source>
10813 <target state="translated">再生速度を遅くする (アクティブのプレーヤーが必要)</target> 10866 <target state="translated">再生速度を遅くする (アクティブのプレーヤーが必要)</target>
10814 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group> 10867
10815 </trans-unit> 10868 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10816 <trans-unit id="3178343147230721210" datatype="html"> 10869 <trans-unit id="3178343147230721210" datatype="html">
10817 <source>Navigate in the video frame by frame (requires player focus)</source> 10870 <source>Navigate in the video frame by frame (requires player focus)</source>
10818 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 10871 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10819 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group> 10872
10820 </trans-unit> 10873 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10821 <trans-unit id="8025996572234182184"> 10874 <trans-unit id="8025996572234182184">
10822 <source>Like the video</source> 10875 <source>Like the video</source>
10823 <target>高く評価</target> 10876 <target>高く評価</target>
diff --git a/client/src/locale/angular.jbo.xlf b/client/src/locale/angular.jbo.xlf
index 7367a10ca..7edbfaace 100644
--- a/client/src/locale/angular.jbo.xlf
+++ b/client/src/locale/angular.jbo.xlf
@@ -307,7 +307,7 @@
307 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 307 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
308 </target> 308 </target>
309 309
310 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html"> 310 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html">
311 <source>My watch history</source><target state="new">My watch history</target> 311 <source>My watch history</source><target state="new">My watch history</target>
312 312
313 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 313 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
@@ -388,12 +388,12 @@
388 388
389 389
390 390
391 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 391 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
392 <trans-unit id="1006562256968398209" datatype="html"> 392 <trans-unit id="1006562256968398209" datatype="html">
393 <source>video</source> 393 <source>video</source>
394 <target state="new">video</target> 394 <target state="new">video</target>
395 395
396 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 396 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
397 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 397 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
398 398
399 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 399 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -455,10 +455,10 @@
455 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 455 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
456 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 456 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
457 457
458 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 458 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
459 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 459 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
460 460
461 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html"> 461 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html">
462 <source>subtitles</source><target state="new">subtitles</target> 462 <source>subtitles</source><target state="new">subtitles</target>
463 463
464 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 464 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
@@ -847,7 +847,7 @@
847 847
848 848
849 849
850 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group></trans-unit> 850 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
851 <trans-unit id="1502595455339510144" datatype="html"> 851 <trans-unit id="1502595455339510144" datatype="html">
852 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 852 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
853 <target state="new"> 853 <target state="new">
@@ -930,7 +930,31 @@
930 <source>Federation</source> 930 <source>Federation</source>
931 <target state="new">Federation</target> 931 <target state="new">Federation</target>
932 932
933 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 933 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
934 <source>Following</source><target state="new">Following</target>
935 <context-group purpose="location">
936 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
937 <context context-type="linenumber">29</context>
938 </context-group>
939 <context-group purpose="location">
940 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
941 <context context-type="linenumber">31</context>
942 </context-group>
943 <context-group purpose="location">
944 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
945 <context context-type="linenumber">28</context>
946 </context-group>
947 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
948 <source>Followers</source><target state="new">Followers</target>
949 <context-group purpose="location">
950 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
951 <context context-type="linenumber">34</context>
952 </context-group>
953 <context-group purpose="location">
954 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
955 <context context-type="linenumber">37</context>
956 </context-group>
957 </trans-unit>
934 <trans-unit id="3541687134897970106" datatype="html"> 958 <trans-unit id="3541687134897970106" datatype="html">
935 <source>followers</source> 959 <source>followers</source>
936 <target state="new">followers</target> 960 <target state="new">followers</target>
@@ -994,7 +1018,7 @@
994 1018
995 1019
996 1020
997 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 1021 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
998 <trans-unit id="3616223838716839702" datatype="html"> 1022 <trans-unit id="3616223838716839702" datatype="html">
999 <source>Ban this user</source> 1023 <source>Ban this user</source>
1000 <target state="new">Ban this user</target> 1024 <target state="new">Ban this user</target>
@@ -1062,17 +1086,11 @@
1062 1086
1063 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html"> 1087 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html">
1064 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1088 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1065 <context-group purpose="location"> 1089
1066 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1090 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7215649348148521605" datatype="html">
1067 <context context-type="linenumber">60,62</context>
1068 </context-group>
1069 </trans-unit><trans-unit id="7215649348148521605" datatype="html">
1070 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1091 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1071 <context-group purpose="location"> 1092
1072 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1093 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1073 <context context-type="linenumber">65,67</context>
1074 </context-group>
1075 </trans-unit>
1076 <trans-unit id="2392488717875840729"> 1094 <trans-unit id="2392488717875840729">
1077 <source>User</source> 1095 <source>User</source>
1078 <target>lo pilno</target> 1096 <target>lo pilno</target>
@@ -1083,7 +1101,13 @@
1083 <source>Username or email address</source> 1101 <source>Username or email address</source>
1084 <target state="new">Username or email address</target> 1102 <target state="new">Username or email address</target>
1085 1103
1086 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit> 1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="1758058452376026925" datatype="html">
1105 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1106 <context-group purpose="location">
1107 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1108 <context context-type="linenumber">33,34</context>
1109 </context-group>
1110 </trans-unit>
1087 1111
1088 <trans-unit id="1431416938026210429"> 1112 <trans-unit id="1431416938026210429">
1089 <source>Password</source> 1113 <source>Password</source>
@@ -1096,40 +1120,34 @@
1096 1120
1097 1121
1098 1122
1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group></trans-unit> 1123 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1100 <trans-unit id="8715156686857791956" datatype="html"> 1124 <trans-unit id="8715156686857791956" datatype="html">
1101 <source>Click here to reset your password</source> 1125 <source>Click here to reset your password</source>
1102 <target state="new">Click here to reset your password</target> 1126 <target state="new">Click here to reset your password</target>
1103 1127
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html"> 1128 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html">
1105 <source>I forgot my password</source><target state="new">I forgot my password</target> 1129 <source>I forgot my password</source><target state="new">I forgot my password</target>
1106 <context-group purpose="location"> 1130
1107 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1131 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="2101170466365500913" datatype="html">
1108 <context context-type="linenumber">47</context>
1109 </context-group>
1110 </trans-unit><trans-unit id="2101170466365500913" datatype="html">
1111 <source> Logging into an account lets you publish content </source><target state="new"> Logging into an account lets you publish content </target> 1132 <source> Logging into an account lets you publish content </source><target state="new"> Logging into an account lets you publish content </target>
1112 <context-group purpose="location"> 1133
1113 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1134 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1114 <context context-type="linenumber">56,57</context>
1115 </context-group>
1116 </trans-unit>
1117 <trans-unit id="2454050363478003966"> 1135 <trans-unit id="2454050363478003966">
1118 <source>Login</source> 1136 <source>Login</source>
1119 <target>co'a cmisau</target> 1137 <target>co'a cmisau</target>
1120 1138
1121 1139
1122 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1140 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1123 <trans-unit id="3183213940445113677" datatype="html"> 1141 <trans-unit id="3183213940445113677" datatype="html">
1124 <source>Or sign in with</source> 1142 <source>Or sign in with</source>
1125 <target state="new">Or sign in with</target> 1143 <target state="new">Or sign in with</target>
1126 1144
1127 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit> 1145 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1128 <trans-unit id="3238209155172574367"> 1146 <trans-unit id="3238209155172574367">
1129 <source>Forgot your password</source> 1147 <source>Forgot your password</source>
1130 <target>.i mi nalmo'i le mi lerpoijaspu</target> 1148 <target>.i mi nalmo'i le mi lerpoijaspu</target>
1131 1149
1132 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group></trans-unit> 1150 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1133 <trans-unit id="87327320394367488" datatype="html"> 1151 <trans-unit id="87327320394367488" datatype="html">
1134 <source> 1152 <source>
1135 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1153 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
@@ -1138,10 +1156,10 @@
1138 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1156 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
1139 </target> 1157 </target>
1140 1158
1141 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html"> 1159 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html">
1142 <source> Enter your email address and we will send you a link to reset your password. </source><target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1160 <source> Enter your email address and we will send you a link to reset your password. </source><target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1143 1161
1144 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html"> 1162 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html">
1145 <source>An email with the reset password instructions will be sent to <x id="PH"/>. 1163 <source>An email with the reset password instructions will be sent to <x id="PH"/>.
1146The link will expire within 1 hour.</source><target state="new">An email with the reset password instructions will be sent to <x id="PH"/>. 1164The link will expire within 1 hour.</source><target state="new">An email with the reset password instructions will be sent to <x id="PH"/>.
1147The link will expire within 1 hour.</target> 1165The link will expire within 1 hour.</target>
@@ -1157,17 +1175,17 @@ The link will expire within 1 hour.</target>
1157 1175
1158 1176
1159 1177
1160 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group></trans-unit> 1178 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1161 <trans-unit id="3967269098753656610"> 1179 <trans-unit id="3967269098753656610">
1162 <source>Email address</source> 1180 <source>Email address</source>
1163 <target>lo ve samymri</target> 1181 <target>lo ve samymri</target>
1164 1182
1165 1183
1166 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html"> 1184 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html">
1167 <source>Reset</source><target state="new">Reset</target> 1185 <source>Reset</source><target state="new">Reset</target>
1168 1186
1169 <note priority="1" from="description">Password reset button</note> 1187 <note priority="1" from="description">Password reset button</note>
1170 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group></trans-unit> 1188 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1171 1189
1172 1190
1173 <trans-unit id="4319634264526091601" datatype="html"> 1191 <trans-unit id="4319634264526091601" datatype="html">
@@ -1534,7 +1552,7 @@ galfi le mi japyvla</target>
1534 <source>Create an account</source> 1552 <source>Create an account</source>
1535 <target>zbasu lo pilno</target> 1553 <target>zbasu lo pilno</target>
1536 1554
1537 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1555 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1538 1556
1539 <trans-unit id="3058024914967508975" datatype="html"> 1557 <trans-unit id="3058024914967508975" datatype="html">
1540 <source>My videos</source><target state="new">My videos</target> 1558 <source>My videos</source><target state="new">My videos</target>
@@ -1580,7 +1598,7 @@ galfi le mi japyvla</target>
1580 <target state="new">VIDEOS</target> 1598 <target state="new">VIDEOS</target>
1581 1599
1582 1600
1583 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html"> 1601 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html">
1584 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target> 1602 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target>
1585 1603
1586 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html"> 1604 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html">
@@ -1648,7 +1666,7 @@ galfi le mi japyvla</target>
1648 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html"> 1666 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html">
1649 <source>I'm a teapot</source><target state="new">I'm a teapot</target> 1667 <source>I'm a teapot</source><target state="new">I'm a teapot</target>
1650 1668
1651 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html"> 1669 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html">
1652 <source>That's an error.</source><target state="new">That's an error.</target> 1670 <source>That's an error.</source><target state="new">That's an error.</target>
1653 <context-group purpose="location"> 1671 <context-group purpose="location">
1654 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context> 1672 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context>
@@ -1714,7 +1732,7 @@ galfi le mi japyvla</target>
1714 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html"> 1732 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html">
1715 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1733 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1716 1734
1717 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group></trans-unit> 1735 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1718 1736
1719 <trans-unit id="5131854469652959713" datatype="html"> 1737 <trans-unit id="5131854469652959713" datatype="html">
1720 <source>GLOBAL SEARCH</source> 1738 <source>GLOBAL SEARCH</source>
@@ -2422,7 +2440,7 @@ galfi le mi japyvla</target>
2422 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2440 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2423 <source>Upload on hold</source><target state="new">Upload on hold</target> 2441 <source>Upload on hold</source><target state="new">Upload on hold</target>
2424 2442
2425 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2443 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2426 <trans-unit id="285180972645018518" datatype="html"> 2444 <trans-unit id="285180972645018518" datatype="html">
2427 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2445 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2428 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2446 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3094,11 +3112,7 @@ galfi le mi japyvla</target>
3094 <target state="new">ID</target> 3112 <target state="new">ID</target>
3095 3113
3096 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit> 3114 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit>
3097 <trans-unit id="2265605798180116441" datatype="html"> 3115
3098 <source>Follower handle</source>
3099 <target state="new">Follower handle</target>
3100
3101 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
3102 <trans-unit id="5911214550882917183" datatype="html"> 3116 <trans-unit id="5911214550882917183" datatype="html">
3103 <source>State</source> 3117 <source>State</source>
3104 <target state="new">State</target> 3118 <target state="new">State</target>
@@ -3180,11 +3194,7 @@ galfi le mi japyvla</target>
3180 </target> 3194 </target>
3181 3195
3182 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit> 3196 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
3183 <trans-unit id="6641024648411549335" datatype="html"> 3197
3184 <source>Host</source>
3185 <target state="new">Host</target>
3186
3187 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3188 <trans-unit id="6571718060636962350" datatype="html"> 3198 <trans-unit id="6571718060636962350" datatype="html">
3189 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3199 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3190 <target state="new">Redundancy allowed 3200 <target state="new">Redundancy allowed
@@ -3195,7 +3205,7 @@ galfi le mi japyvla</target>
3195 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html"> 3205 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html">
3196 <source>Unfollow</source><target state="new">Unfollow</target> 3206 <source>Unfollow</source><target state="new">Unfollow</target>
3197 3207
3198 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3208 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3199 <trans-unit id="8246779176913476983" datatype="html"> 3209 <trans-unit id="8246779176913476983" datatype="html">
3200 <source>Open instance in a new tab</source> 3210 <source>Open instance in a new tab</source>
3201 <target state="new">Open instance in a new tab</target> 3211 <target state="new">Open instance in a new tab</target>
@@ -3207,12 +3217,12 @@ galfi le mi japyvla</target>
3207 <source>No host found matching current filters.</source> 3217 <source>No host found matching current filters.</source>
3208 <target state="new">No host found matching current filters.</target> 3218 <target state="new">No host found matching current filters.</target>
3209 3219
3210 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3220 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3211 <trans-unit id="7274241885665071790" datatype="html"> 3221 <trans-unit id="7274241885665071790" datatype="html">
3212 <source>Your instance is not following anyone.</source> 3222 <source>Your instance is not following anyone.</source>
3213 <target state="new">Your instance is not following anyone.</target> 3223 <target state="new">Your instance is not following anyone.</target>
3214 3224
3215 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3225 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3216 <trans-unit id="4774348799569692380" datatype="html"> 3226 <trans-unit id="4774348799569692380" datatype="html">
3217 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3227 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3218 <target state="new">Showing 3228 <target state="new">Showing
@@ -3222,14 +3232,7 @@ galfi le mi japyvla</target>
3222 </target> 3232 </target>
3223 3233
3224 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3234 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3225 <trans-unit id="6275803119759621687" datatype="html"> 3235 <trans-unit id="9216117865911519658" datatype="html">
3226 <source>Follow domains</source>
3227 <target state="new">Follow domains</target>
3228
3229 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit><trans-unit id="1268699198448750610" datatype="html">
3230 <source>Follow instances</source><target state="new">Follow instances</target>
3231
3232 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3233 <source>Action</source><target state="new">Action</target> 3236 <source>Action</source><target state="new">Action</target>
3234 3237
3235 3238
@@ -3279,7 +3282,7 @@ galfi le mi japyvla</target>
3279 3282
3280 3283
3281 3284
3282 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html"> 3285 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html">
3283 <source>e.g. jane_doe</source><target state="new">e.g. jane_doe</target> 3286 <source>e.g. jane_doe</source><target state="new">e.g. jane_doe</target>
3284 3287
3285 <note priority="1" from="description">Username choice placeholder in the registration form</note> 3288 <note priority="1" from="description">Username choice placeholder in the registration form</note>
@@ -3311,7 +3314,7 @@ galfi le mi japyvla</target>
3311 <target state="new">Role</target> 3314 <target state="new">Role</target>
3312 3315
3313 3316
3314 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group></trans-unit> 3317 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3315 <trans-unit id="7046347992315328430" datatype="html"> 3318 <trans-unit id="7046347992315328430" datatype="html">
3316 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3319 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3317 <target state="new"> 3320 <target state="new">
@@ -3334,15 +3337,9 @@ galfi le mi japyvla</target>
3334 3337
3335 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html"> 3338 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html">
3336 <source>Auth plugin</source><target state="new">Auth plugin</target> 3339 <source>Auth plugin</source><target state="new">Auth plugin</target>
3337 <context-group purpose="location"> 3340
3338 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3341
3339 <context context-type="linenumber">188</context> 3342 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit><trans-unit id="588099657508661970" datatype="html">
3340 </context-group>
3341 <context-group purpose="location">
3342 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3343 <context context-type="linenumber">188</context>
3344 </context-group>
3345 </trans-unit><trans-unit id="588099657508661970" datatype="html">
3346 <source>None (local authentication)</source><target state="new">None (local authentication)</target> 3343 <source>None (local authentication)</source><target state="new">None (local authentication)</target>
3347 <context-group purpose="location"> 3344 <context-group purpose="location">
3348 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3345 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
@@ -3569,7 +3566,13 @@ galfi le mi japyvla</target>
3569 3566
3570 3567
3571 3568
3572 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="4691552465058437520" datatype="html"> 3569 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3570 <source>Follower</source><target state="new">Follower</target>
3571 <context-group purpose="location">
3572 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3573 <context context-type="linenumber">24</context>
3574 </context-group>
3575 </trans-unit><trans-unit id="4691552465058437520" datatype="html">
3573 <source>Commented video</source><target state="new">Commented video</target> 3576 <source>Commented video</source><target state="new">Commented video</target>
3574 3577
3575 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html"> 3578 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html">
@@ -3893,7 +3896,7 @@ galfi le mi japyvla</target>
3893 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 3896 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
3894 </target> 3897 </target>
3895 3898
3896 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3899 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3897 <trans-unit id="4058814854824495833" datatype="html"> 3900 <trans-unit id="4058814854824495833" datatype="html">
3898 <source>Mute domains</source> 3901 <source>Mute domains</source>
3899 <target state="new">Mute domains</target> 3902 <target state="new">Mute domains</target>
@@ -5708,11 +5711,8 @@ color: red;
5708 5711
5709 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5712 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5710 <source>CHANNELS</source><target state="new">CHANNELS</target> 5713 <source>CHANNELS</source><target state="new">CHANNELS</target>
5711 <context-group purpose="location"> 5714
5712 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5715 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5713 <context context-type="linenumber">82</context>
5714 </context-group>
5715 </trans-unit>
5716 5716
5717 <trans-unit id="3666829335406793239" datatype="html"> 5717 <trans-unit id="3666829335406793239" datatype="html">
5718 <source>This account does not have channels.</source> 5718 <source>This account does not have channels.</source>
@@ -5750,7 +5750,13 @@ channel with the same name (<x id="PH_2"/>)!</source><target state="new">Do you
5750It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 5750It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
5751channel with the same name (<x id="PH_2"/>)!</target> 5751channel with the same name (<x id="PH_2"/>)!</target>
5752 5752
5753 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5753 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5754 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5755 <context-group purpose="location">
5756 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5757 <context context-type="linenumber">48</context>
5758 </context-group>
5759 </trans-unit>
5754 <trans-unit id="5387007581996837469" datatype="html"> 5760 <trans-unit id="5387007581996837469" datatype="html">
5755 <source>My Channels</source> 5761 <source>My Channels</source>
5756 <target state="new">My Channels</target> 5762 <target state="new">My Channels</target>
@@ -6361,12 +6367,12 @@ zbasu lo pilno</target>
6361 <source>Your message has been sent.</source> 6367 <source>Your message has been sent.</source>
6362 <target state="new">Your message has been sent.</target> 6368 <target state="new">Your message has been sent.</target>
6363 6369
6364 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6370 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6365 <trans-unit id="2072135752262464360" datatype="html"> 6371 <trans-unit id="2072135752262464360" datatype="html">
6366 <source>You already sent this form recently</source> 6372 <source>You already sent this form recently</source>
6367 <target state="new">You already sent this form recently</target> 6373 <target state="new">You already sent this form recently</target>
6368 6374
6369 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6375 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6370 <trans-unit id="819067926858619041" datatype="html"> 6376 <trans-unit id="819067926858619041" datatype="html">
6371 <source>Account videos</source><target state="new">Account videos</target> 6377 <source>Account videos</source><target state="new">Account videos</target>
6372 6378
@@ -6401,10 +6407,10 @@ zbasu lo pilno</target>
6401 <x id="PH"/> direct account followers 6407 <x id="PH"/> direct account followers
6402 </target> 6408 </target>
6403 6409
6404 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html"> 6410 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html">
6405 <source>Report this account</source><target state="new">Report this account</target> 6411 <source>Report this account</source><target state="new">Report this account</target>
6406 6412
6407 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6413 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6408 6414
6409 <trans-unit id="1504521795586863905" datatype="html"> 6415 <trans-unit id="1504521795586863905" datatype="html">
6410 <source>VIDEOS</source><target state="new">VIDEOS</target> 6416 <source>VIDEOS</source><target state="new">VIDEOS</target>
@@ -6416,24 +6422,16 @@ zbasu lo pilno</target>
6416 <target state="new">Username copied</target> 6422 <target state="new">Username copied</target>
6417 6423
6418 6424
6419 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html"> 6425 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html">
6420 <source>1 subscriber</source><target state="new">1 subscriber</target> 6426 <source>1 subscriber</source><target state="new">1 subscriber</target>
6421 6427
6422 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html"> 6428 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html">
6423 <source><x id="PH"/> subscribers</source><target state="new"><x id="PH"/> subscribers</target> 6429 <source><x id="PH"/> subscribers</source><target state="new"><x id="PH"/> subscribers</target>
6424 6430
6425 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6431 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6432
6433
6426 6434
6427 <trans-unit id="4682675125751819107" datatype="html">
6428 <source>Instances you follow</source>
6429 <target state="new">Instances you follow</target>
6430
6431 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6432 <trans-unit id="8899833753704589712" datatype="html">
6433 <source>Instances following you</source>
6434 <target state="new">Instances following you</target>
6435
6436 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6437 <trans-unit id="1035838766454786107" datatype="html"> 6435 <trans-unit id="1035838766454786107" datatype="html">
6438 <source>Audio-only</source> 6436 <source>Audio-only</source>
6439 <target state="new">Audio-only</target> 6437 <target state="new">Audio-only</target>
@@ -6481,7 +6479,13 @@ zbasu lo pilno</target>
6481 <source>Auto (via ffmpeg)</source> 6479 <source>Auto (via ffmpeg)</source>
6482 <target state="new">Auto (via ffmpeg)</target> 6480 <target state="new">Auto (via ffmpeg)</target>
6483 6481
6484 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="931255636742351800" datatype="html"> 6482 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="3642770981085338761" datatype="html">
6483 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6484 <context-group purpose="location">
6485 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6486 <context context-type="linenumber">3</context>
6487 </context-group>
6488 </trans-unit><trans-unit id="931255636742351800" datatype="html">
6485 <source>No limit</source><target state="new">No limit</target> 6489 <source>No limit</source><target state="new">No limit</target>
6486 6490
6487 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html"> 6491 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html">
@@ -6600,17 +6604,33 @@ zbasu lo pilno</target>
6600 <source>Domain is required.</source> 6604 <source>Domain is required.</source>
6601 <target state="new">Domain is required.</target> 6605 <target state="new">Domain is required.</target>
6602 6606
6603 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group></trans-unit> 6607 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6604 <trans-unit id="6780793142903080663" datatype="html"> 6608 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6605 <source>Domains entered are invalid.</source> 6609 <context-group purpose="location">
6606 <target state="new">Domains entered are invalid.</target> 6610 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6607 6611 <context context-type="linenumber">93</context>
6608 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6612 </context-group>
6609 <trans-unit id="5886492514458202177" datatype="html"> 6613 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6610 <source>Domains entered contain duplicates.</source> 6614 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6611 <target state="new">Domains entered contain duplicates.</target> 6615 <context-group purpose="location">
6612 6616 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6613 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 6617 <context context-type="linenumber">94</context>
6618 </context-group>
6619 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6620 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6621 <context-group purpose="location">
6622 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6623 <context context-type="linenumber">102</context>
6624 </context-group>
6625 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6626 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6627 <context-group purpose="location">
6628 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6629 <context context-type="linenumber">103</context>
6630 </context-group>
6631 </trans-unit>
6632
6633
6614 <trans-unit id="240806681889331244" datatype="html"> 6634 <trans-unit id="240806681889331244" datatype="html">
6615 <source>Unlimited</source> 6635 <source>Unlimited</source>
6616 <target state="new">Unlimited</target> 6636 <target state="new">Unlimited</target>
@@ -6741,7 +6761,27 @@ zbasu lo pilno</target>
6741 <x id="PH"/> removed from instance followers 6761 <x id="PH"/> removed from instance followers
6742 </target> 6762 </target>
6743 6763
6744 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit> 6764 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit><trans-unit id="6018246591673612412" datatype="html">
6765 <source>Follow</source><target state="new">Follow</target>
6766 <context-group purpose="location">
6767 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6768 <context context-type="linenumber">3</context>
6769 </context-group>
6770 <context-group purpose="location">
6771 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6772 <context context-type="linenumber">37</context>
6773 </context-group>
6774 <context-group purpose="location">
6775 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6776 <context context-type="linenumber">18</context>
6777 </context-group>
6778 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6779 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6780 <context-group purpose="location">
6781 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6782 <context context-type="linenumber">11</context>
6783 </context-group>
6784 </trans-unit>
6745 <trans-unit id="2740793005745065895" datatype="html"> 6785 <trans-unit id="2740793005745065895" datatype="html">
6746 <source> 6786 <source>
6747 <x id="PH"/> is not valid 6787 <x id="PH"/> is not valid
@@ -6750,19 +6790,25 @@ zbasu lo pilno</target>
6750 <x id="PH"/> is not valid 6790 <x id="PH"/> is not valid
6751 </target> 6791 </target>
6752 6792
6753 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group></trans-unit> 6793 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6754 <trans-unit id="2355066641781598196"> 6794 <trans-unit id="2355066641781598196">
6755 <source>Follow request(s) sent!</source> 6795 <source>Follow request(s) sent!</source>
6756 <target>.i mo'u mrilu lo jersi pe'a ve cpedu</target> 6796 <target>.i mo'u mrilu lo jersi pe'a ve cpedu</target>
6757 6797
6758 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit> 6798 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6799 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6800 <context-group purpose="location">
6801 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6802 <context context-type="linenumber">3</context>
6803 </context-group>
6804 </trans-unit>
6759 <trans-unit id="4245720728052819482" datatype="html"> 6805 <trans-unit id="4245720728052819482" datatype="html">
6760 <source>Do you really want to unfollow <x id="PH"/>?</source> 6806 <source>Do you really want to unfollow <x id="PH"/>?</source>
6761 <target state="new">Do you really want to unfollow 6807 <target state="new">Do you really want to unfollow
6762 <x id="PH"/>? 6808 <x id="PH"/>?
6763 </target> 6809 </target>
6764 6810
6765 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6811 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6766 <trans-unit id="9160510009013134726"> 6812 <trans-unit id="9160510009013134726">
6767 <source>Unfollow</source> 6813 <source>Unfollow</source>
6768 <target>co'u jersi pe'a</target> 6814 <target>co'u jersi pe'a</target>
@@ -6774,7 +6820,7 @@ zbasu lo pilno</target>
6774 <x id="PH"/> anymore. 6820 <x id="PH"/> anymore.
6775 </target> 6821 </target>
6776 6822
6777 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 6823 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6778 <trans-unit id="2593763089859685916" datatype="html"> 6824 <trans-unit id="2593763089859685916" datatype="html">
6779 <source>enabled</source> 6825 <source>enabled</source>
6780 <target state="new">enabled</target> 6826 <target state="new">enabled</target>
@@ -7232,7 +7278,7 @@ zbasu lo pilno</target>
7232 7278
7233 7279
7234 7280
7235 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit> 7281 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7236 <trans-unit id="5076187961693950167" datatype="html"> 7282 <trans-unit id="5076187961693950167" datatype="html">
7237 <source>Standard logs</source> 7283 <source>Standard logs</source>
7238 <target state="new">Standard logs</target> 7284 <target state="new">Standard logs</target>
@@ -7270,13 +7316,7 @@ zbasu lo pilno</target>
7270 <source>Update user password</source> 7316 <source>Update user password</source>
7271 <target state="new">Update user password</target> 7317 <target state="new">Update user password</target>
7272 7318
7273 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit><trans-unit id="177544274549739411" datatype="html"> 7319 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit>
7274 <source>Following list</source><target state="new">Following list</target>
7275
7276 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group></trans-unit><trans-unit id="8092429110007204784" datatype="html">
7277 <source>Followers list</source><target state="new">Followers list</target>
7278
7279 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group></trans-unit>
7280 <trans-unit id="780323526182667308" datatype="html"> 7320 <trans-unit id="780323526182667308" datatype="html">
7281 <source>User <x id="PH"/> updated.</source> 7321 <source>User <x id="PH"/> updated.</source>
7282 <target state="new">User 7322 <target state="new">User
@@ -7307,13 +7347,7 @@ zbasu lo pilno</target>
7307 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html"> 7347 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html">
7308 <source>Federation</source><target state="new">Federation</target> 7348 <source>Federation</source><target state="new">Federation</target>
7309 7349
7310 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="4682675125751819107" datatype="html"> 7350 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit>
7311 <source>Instances you follow</source><target state="new">Instances you follow</target>
7312
7313 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group></trans-unit><trans-unit id="8899833753704589712" datatype="html">
7314 <source>Instances following you</source><target state="new">Instances following you</target>
7315
7316 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit>
7317 <trans-unit id="3767259920053407667" datatype="html"> 7351 <trans-unit id="3767259920053407667" datatype="html">
7318 <source>Videos will be deleted, comments will be tombstoned.</source> 7352 <source>Videos will be deleted, comments will be tombstoned.</source>
7319 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7353 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7341,7 +7375,25 @@ zbasu lo pilno</target>
7341 <target state="new">Set Email as Verified</target> 7375 <target state="new">Set Email as Verified</target>
7342 7376
7343 7377
7344 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7378 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7379 <source>Created</source><target state="new">Created</target>
7380 <context-group purpose="location">
7381 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7382 <context context-type="linenumber">115</context>
7383 </context-group>
7384 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7385 <source>Daily quota</source><target state="new">Daily quota</target>
7386 <context-group purpose="location">
7387 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7388 <context context-type="linenumber">120</context>
7389 </context-group>
7390 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7391 <source>Last login</source><target state="new">Last login</target>
7392 <context-group purpose="location">
7393 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7394 <context context-type="linenumber">122</context>
7395 </context-group>
7396 </trans-unit>
7345 <trans-unit id="3403978719736970622" datatype="html"> 7397 <trans-unit id="3403978719736970622" datatype="html">
7346 <source>You cannot ban root.</source> 7398 <source>You cannot ban root.</source>
7347 <target state="new">You cannot ban root.</target> 7399 <target state="new">You cannot ban root.</target>
@@ -7646,12 +7698,12 @@ zbasu lo pilno</target>
7646 <x id="PH"/> .ly. noi vidvi te tivni 7698 <x id="PH"/> .ly. noi vidvi te tivni
7647 </target> 7699 </target>
7648 7700
7649 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7701 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7650 <trans-unit id="8723777130353305761"> 7702 <trans-unit id="8723777130353305761">
7651 <source>This name already exists on this instance.</source> 7703 <source>This name already exists on this instance.</source>
7652 <target>.i le cmene xa'o zasti ci'e le mupli</target> 7704 <target>.i le cmene xa'o zasti ci'e le mupli</target>
7653 7705
7654 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7706 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7655 <trans-unit id="7589345916094713536"> 7707 <trans-unit id="7589345916094713536">
7656 <source>Video channel <x id="PH"/> updated.</source> 7708 <source>Video channel <x id="PH"/> updated.</source>
7657 <target>.i mo'u galfi la'o ly. 7709 <target>.i mo'u galfi la'o ly.
@@ -7668,13 +7720,7 @@ zbasu lo pilno</target>
7668 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 7720 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7669 7721
7670 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7722 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7671 <trans-unit id="2575302837003821736" datatype="html"> 7723
7672 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7673 <target state="new">Please type the display name of the video channel (
7674 <x id="PH"/>) to confirm
7675 </target>
7676
7677 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7678 <trans-unit id="624066830180032195"> 7724 <trans-unit id="624066830180032195">
7679 <source>Video channel <x id="PH"/> deleted.</source> 7725 <source>Video channel <x id="PH"/> deleted.</source>
7680 <target>.i mo'u vimcu la'o ly. 7726 <target>.i mo'u vimcu la'o ly.
@@ -7817,7 +7863,13 @@ zbasu lo pilno</target>
7817 <source>Ownership change request sent.</source> 7863 <source>Ownership change request sent.</source>
7818 <target state="new">Ownership change request sent.</target> 7864 <target state="new">Ownership change request sent.</target>
7819 7865
7820 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 7866 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7699622144571229146" datatype="html">
7867 <source>Sort by</source><target state="new">Sort by</target>
7868 <context-group purpose="location">
7869 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7870 <context context-type="linenumber">26</context>
7871 </context-group>
7872 </trans-unit>
7821 <trans-unit id="3245220240937722814"> 7873 <trans-unit id="3245220240937722814">
7822 <source>My channels</source> 7874 <source>My channels</source>
7823 <target>lo mi te tivni</target> 7875 <target>lo mi te tivni</target>
@@ -7912,7 +7964,7 @@ zbasu lo pilno</target>
7912 <target>jersi pe'a le pilno</target> 7964 <target>jersi pe'a le pilno</target>
7913 7965
7914 7966
7915 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 7967 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
7916 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 7968 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
7917 <context-group purpose="location"> 7969 <context-group purpose="location">
7918 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 7970 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -7959,34 +8011,34 @@ zbasu lo pilno</target>
7959 <source>Go to my subscriptions</source> 8011 <source>Go to my subscriptions</source>
7960 <target>klama lo se jersi pe'a be mi</target> 8012 <target>klama lo se jersi pe'a be mi</target>
7961 8013
7962 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 8014 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
7963 <trans-unit id="1136469849928650779"> 8015 <trans-unit id="1136469849928650779">
7964 <source>Go to my videos</source> 8016 <source>Go to my videos</source>
7965 <target>klama lo mi vidvi</target> 8017 <target>klama lo mi vidvi</target>
7966 8018
7967 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit> 8019 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
7968 <trans-unit id="7836683738999600376"> 8020 <trans-unit id="7836683738999600376">
7969 <source>Go to my imports</source> 8021 <source>Go to my imports</source>
7970 <target>klama lo se nerbei be mi</target> 8022 <target>klama lo se nerbei be mi</target>
7971 8023
7972 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 8024 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
7973 <trans-unit id="7511292153332773503"> 8025 <trans-unit id="7511292153332773503">
7974 <source>Go to my channels</source> 8026 <source>Go to my channels</source>
7975 <target>klama lo mi te tivni</target> 8027 <target>klama lo mi te tivni</target>
7976 8028
7977 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html"> 8029 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html">
7978 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8030 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7979Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8031Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7980Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8032Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
7981 8033
7982 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group></trans-unit> 8034 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
7983 8035
7984 8036
7985 <trans-unit id="375263728166936544" datatype="html"> 8037 <trans-unit id="375263728166936544" datatype="html">
7986 <source>You need to reconnect.</source> 8038 <source>You need to reconnect.</source>
7987 <target state="new">You need to reconnect.</target> 8039 <target state="new">You need to reconnect.</target>
7988 8040
7989 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group></trans-unit> 8041 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
7990 <trans-unit id="2206638022166154361" datatype="html"> 8042 <trans-unit id="2206638022166154361" datatype="html">
7991 <source>Keyboard Shortcuts:</source> 8043 <source>Keyboard Shortcuts:</source>
7992 <target state="new">Keyboard Shortcuts:</target> 8044 <target state="new">Keyboard Shortcuts:</target>
@@ -7997,6 +8049,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7997 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8049 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7998 <context context-type="linenumber">98</context> 8050 <context context-type="linenumber">98</context>
7999 </context-group> 8051 </context-group>
8052 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8053 <source>In my library</source><target state="new">In my library</target>
8054 <context-group purpose="location">
8055 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8056 <context context-type="linenumber">104</context>
8057 </context-group>
8000 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 8058 </trans-unit><trans-unit id="232050922346936574" datatype="html">
8001 <source>Trending</source><target state="new">Trending</target> 8059 <source>Trending</source><target state="new">Trending</target>
8002 8060
@@ -8020,38 +8078,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8020 <source>Incorrect username or password.</source> 8078 <source>Incorrect username or password.</source>
8021 <target state="new">Incorrect username or password.</target> 8079 <target state="new">Incorrect username or password.</target>
8022 8080
8023 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 8081 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8024 <trans-unit id="6974874606619467663" datatype="html"> 8082 <trans-unit id="6974874606619467663" datatype="html">
8025 <source>Your account is blocked.</source> 8083 <source>Your account is blocked.</source>
8026 <target state="new">Your account is blocked.</target> 8084 <target state="new">Your account is blocked.</target>
8027 8085
8028 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 8086 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8029 8087
8030 <trans-unit id="7939914198003891823" datatype="html"> 8088 <trans-unit id="7939914198003891823" datatype="html">
8031 <source>any language</source> 8089 <source>any language</source>
8032 <target state="new">any language</target> 8090 <target state="new">any language</target>
8033 8091
8034 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8092 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8035 <trans-unit id="5633144232269377096" datatype="html"> 8093 <trans-unit id="5633144232269377096" datatype="html">
8036 <source>hide</source> 8094 <source>hide</source>
8037 <target state="new">hide</target> 8095 <target state="new">hide</target>
8038 8096
8039 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8097 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8040 <trans-unit id="8603861867909474404" datatype="html"> 8098 <trans-unit id="8603861867909474404" datatype="html">
8041 <source>blur</source> 8099 <source>blur</source>
8042 <target state="new">blur</target> 8100 <target state="new">blur</target>
8043 8101
8044 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8102 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8045 <trans-unit id="4534458451100881847" datatype="html"> 8103 <trans-unit id="4534458451100881847" datatype="html">
8046 <source>display</source> 8104 <source>display</source>
8047 <target state="new">display</target> 8105 <target state="new">display</target>
8048 8106
8049 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8107 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8050 <trans-unit id="4467323362722952678" datatype="html"> 8108 <trans-unit id="4467323362722952678" datatype="html">
8051 <source>Unknown</source> 8109 <source>Unknown</source>
8052 <target state="new">Unknown</target> 8110 <target state="new">Unknown</target>
8053 8111
8054 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8112 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8055 <trans-unit id="8781423666414310853"> 8113 <trans-unit id="8781423666414310853">
8056 <source>Your password has been successfully reset!</source> 8114 <source>Your password has been successfully reset!</source>
8057 <target>.i snada lo nu mo'u galfi le do japyvla</target> 8115 <target>.i snada lo nu mo'u galfi le do japyvla</target>
@@ -9594,17 +9652,17 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9594 <x id="PH"/> minutes. 9652 <x id="PH"/> minutes.
9595 </target> 9653 </target>
9596 9654
9597 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 9655 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9598 <trans-unit id="4965472196059235310" datatype="html"> 9656 <trans-unit id="4965472196059235310" datatype="html">
9599 <source>Too many attempts, please try again later.</source> 9657 <source>Too many attempts, please try again later.</source>
9600 <target state="new">Too many attempts, please try again later.</target> 9658 <target state="new">Too many attempts, please try again later.</target>
9601 9659
9602 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group></trans-unit> 9660 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9603 <trans-unit id="1693549688987384699" datatype="html"> 9661 <trans-unit id="1693549688987384699" datatype="html">
9604 <source>Server error. Please retry later.</source> 9662 <source>Server error. Please retry later.</source>
9605 <target state="new">Server error. Please retry later.</target> 9663 <target state="new">Server error. Please retry later.</target>
9606 9664
9607 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 9665 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9608 <trans-unit id="5927402622550505067" datatype="html"> 9666 <trans-unit id="5927402622550505067" datatype="html">
9609 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9667 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9610 <target state="new">Subscribed to all current channels of 9668 <target state="new">Subscribed to all current channels of
@@ -10107,20 +10165,20 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10107 <source>Your video was uploaded to your account and is private.</source> 10165 <source>Your video was uploaded to your account and is private.</source>
10108 <target state="new">Your video was uploaded to your account and is private.</target> 10166 <target state="new">Your video was uploaded to your account and is private.</target>
10109 10167
10110 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10168 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10111 <trans-unit id="5699822024600815733" datatype="html"> 10169 <trans-unit id="5699822024600815733" datatype="html">
10112 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10170 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10113 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target> 10171 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target>
10114 10172
10115 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10173 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10116 <trans-unit id="1219739004043110649" datatype="html"> 10174 <trans-unit id="1219739004043110649" datatype="html">
10117 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10175 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10118 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target> 10176 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target>
10119 10177
10120 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html"> 10178 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html">
10121 <source>Upload</source><target state="new">Upload</target> 10179 <source>Upload</source><target state="new">Upload</target>
10122 10180
10123 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10181 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10124 <trans-unit id="8278735427925094503" datatype="html"> 10182 <trans-unit id="8278735427925094503" datatype="html">
10125 <source>Upload 10183 <source>Upload
10126 <x id="PH"/> 10184 <x id="PH"/>
@@ -10129,13 +10187,13 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10129 <x id="PH"/> 10187 <x id="PH"/>
10130 </target> 10188 </target>
10131 10189
10132 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10190 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10133 10191
10134 <trans-unit id="5981816353437801748"> 10192 <trans-unit id="5981816353437801748">
10135 <source>Video published.</source> 10193 <source>Video published.</source>
10136 <target>.i lo se vidvi mo'u co'a gubni</target> 10194 <target>.i lo se vidvi mo'u co'a gubni</target>
10137 10195
10138 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10196 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10139 10197
10140 10198
10141 <trans-unit id="764164089183618119" datatype="html"> 10199 <trans-unit id="764164089183618119" datatype="html">
@@ -10199,26 +10257,26 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10199 <trans-unit id="961774488937452220" datatype="html"> 10257 <trans-unit id="961774488937452220" datatype="html">
10200 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10258 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10201 10259
10202 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html"> 10260 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html">
10203 <source>Redirection</source><target state="new">Redirection</target> 10261 <source>Redirection</source><target state="new">Redirection</target>
10204 10262
10205 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10263 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10206 10264
10207 <trans-unit id="8858527736400081688" datatype="html"> 10265 <trans-unit id="8858527736400081688" datatype="html">
10208 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10266 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10209 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target> 10267 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target>
10210 10268
10211 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10269 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10212 <trans-unit id="3937119019020041049" datatype="html"> 10270 <trans-unit id="3937119019020041049" datatype="html">
10213 <source>Mature or explicit content</source> 10271 <source>Mature or explicit content</source>
10214 <target state="new">Mature or explicit content</target> 10272 <target state="new">Mature or explicit content</target>
10215 10273
10216 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10274 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10217 <trans-unit id="1755474755114288376" datatype="html"> 10275 <trans-unit id="1755474755114288376" datatype="html">
10218 <source>Up Next</source> 10276 <source>Up Next</source>
10219 <target state="new">Up Next</target> 10277 <target state="new">Up Next</target>
10220 10278
10221 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html"> 10279 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html">
10222 <source>Cancel</source><target state="new">Cancel</target> 10280 <source>Cancel</source><target state="new">Cancel</target>
10223 10281
10224 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit> 10282 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit>
@@ -10226,62 +10284,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10226 <source>Autoplay is suspended</source> 10284 <source>Autoplay is suspended</source>
10227 <target state="new">Autoplay is suspended</target> 10285 <target state="new">Autoplay is suspended</target>
10228 10286
10229 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10287 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10230 <trans-unit id="7895294730547405228" datatype="html"> 10288 <trans-unit id="7895294730547405228" datatype="html">
10231 <source>Enter/exit fullscreen (requires player focus)</source> 10289 <source>Enter/exit fullscreen (requires player focus)</source>
10232 <target state="new">Enter/exit fullscreen (requires player focus)</target> 10290 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10233 10291
10234 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10292 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10235 <trans-unit id="7618388257165864759" datatype="html"> 10293 <trans-unit id="7618388257165864759" datatype="html">
10236 <source>Play/Pause the video (requires player focus)</source> 10294 <source>Play/Pause the video (requires player focus)</source>
10237 <target state="new">Play/Pause the video (requires player focus)</target> 10295 <target state="new">Play/Pause the video (requires player focus)</target>
10238 10296
10239 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10297 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10240 <trans-unit id="7761890399634216630" datatype="html"> 10298 <trans-unit id="7761890399634216630" datatype="html">
10241 <source>Mute/unmute the video (requires player focus)</source> 10299 <source>Mute/unmute the video (requires player focus)</source>
10242 <target state="new">Mute/unmute the video (requires player focus)</target> 10300 <target state="new">Mute/unmute the video (requires player focus)</target>
10243 10301
10244 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10302 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10245 <trans-unit id="5996585232248234904" datatype="html"> 10303 <trans-unit id="5996585232248234904" datatype="html">
10246 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10304 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10247 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10305 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10248 10306
10249 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10307 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10250 <trans-unit id="3748765405903319998" datatype="html"> 10308 <trans-unit id="3748765405903319998" datatype="html">
10251 <source>Increase the volume (requires player focus)</source> 10309 <source>Increase the volume (requires player focus)</source>
10252 <target state="new">Increase the volume (requires player focus)</target> 10310 <target state="new">Increase the volume (requires player focus)</target>
10253 10311
10254 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10312 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10255 <trans-unit id="5810704036407159982" datatype="html"> 10313 <trans-unit id="5810704036407159982" datatype="html">
10256 <source>Decrease the volume (requires player focus)</source> 10314 <source>Decrease the volume (requires player focus)</source>
10257 <target state="new">Decrease the volume (requires player focus)</target> 10315 <target state="new">Decrease the volume (requires player focus)</target>
10258 10316
10259 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10317 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10260 <trans-unit id="2622048822548065691" datatype="html"> 10318 <trans-unit id="2622048822548065691" datatype="html">
10261 <source>Seek the video forward (requires player focus)</source> 10319 <source>Seek the video forward (requires player focus)</source>
10262 <target state="new">Seek the video forward (requires player focus)</target> 10320 <target state="new">Seek the video forward (requires player focus)</target>
10263 10321
10264 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10322 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10265 <trans-unit id="6540078205109221153" datatype="html"> 10323 <trans-unit id="6540078205109221153" datatype="html">
10266 <source>Seek the video backward (requires player focus)</source> 10324 <source>Seek the video backward (requires player focus)</source>
10267 <target state="new">Seek the video backward (requires player focus)</target> 10325 <target state="new">Seek the video backward (requires player focus)</target>
10268 10326
10269 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10327 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10270 <trans-unit id="1956491957766210808" datatype="html"> 10328 <trans-unit id="1956491957766210808" datatype="html">
10271 <source>Increase playback rate (requires player focus)</source> 10329 <source>Increase playback rate (requires player focus)</source>
10272 <target state="new">Increase playback rate (requires player focus)</target> 10330 <target state="new">Increase playback rate (requires player focus)</target>
10273 10331
10274 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10332 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10275 <trans-unit id="5495529997674803186" datatype="html"> 10333 <trans-unit id="5495529997674803186" datatype="html">
10276 <source>Decrease playback rate (requires player focus)</source> 10334 <source>Decrease playback rate (requires player focus)</source>
10277 <target state="new">Decrease playback rate (requires player focus)</target> 10335 <target state="new">Decrease playback rate (requires player focus)</target>
10278 10336
10279 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10337 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10280 <trans-unit id="3178343147230721210" datatype="html"> 10338 <trans-unit id="3178343147230721210" datatype="html">
10281 <source>Navigate in the video frame by frame (requires player focus)</source> 10339 <source>Navigate in the video frame by frame (requires player focus)</source>
10282 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 10340 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10283 10341
10284 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10342 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10285 <trans-unit id="8025996572234182184"> 10343 <trans-unit id="8025996572234182184">
10286 <source>Like the video</source> 10344 <source>Like the video</source>
10287 <target>nu zanru lo se vidvi</target> 10345 <target>nu zanru lo se vidvi</target>
diff --git a/client/src/locale/angular.kab.xlf b/client/src/locale/angular.kab.xlf
index bec074ca7..a982fd2a9 100644
--- a/client/src/locale/angular.kab.xlf
+++ b/client/src/locale/angular.kab.xlf
@@ -163,7 +163,7 @@
163 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 163 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
164 </target> 164 </target>
165 165
166 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 166 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
167 <trans-unit id="1486537403020619891" datatype="html"> 167 <trans-unit id="1486537403020619891" datatype="html">
168 <source>My watch history</source> 168 <source>My watch history</source>
169 <target state="translated">Azray-inu n tmeẓriwt</target> 169 <target state="translated">Azray-inu n tmeẓriwt</target>
@@ -238,7 +238,7 @@
238 238
239 239
240 240
241 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 241 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
242 <trans-unit id="3099741642167775297" datatype="html"> 242 <trans-unit id="3099741642167775297" datatype="html">
243 <source>Download</source> 243 <source>Download</source>
244 <target>Sider</target> 244 <target>Sider</target>
@@ -254,7 +254,7 @@
254 <source>video</source> 254 <source>video</source>
255 <target>tavidyut</target> 255 <target>tavidyut</target>
256 256
257 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 257 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
258 <trans-unit id="6438815964972582865" datatype="html"> 258 <trans-unit id="6438815964972582865" datatype="html">
259 <source>The following link contains a private token and should not be shared with anyone.</source> 259 <source>The following link contains a private token and should not be shared with anyone.</source>
260 <target state="translated">Aseɣwen-a deg-s ajuṭun sli, ur ilaq ara ad yettwabḍu ula d yiwen.</target> 260 <target state="translated">Aseɣwen-a deg-s ajuṭun sli, ur ilaq ara ad yettwabḍu ula d yiwen.</target>
@@ -320,10 +320,10 @@
320 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 320 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
321 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 321 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
322 322
323 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 323 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
324 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 324 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
325 325
326 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 326 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
327 <trans-unit id="5235042777215655908" datatype="html"> 327 <trans-unit id="5235042777215655908" datatype="html">
328 <source>subtitles</source> 328 <source>subtitles</source>
329 <target state="translated">iduzwilen</target> 329 <target state="translated">iduzwilen</target>
@@ -349,7 +349,7 @@
349 349
350 350
351 351
352 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 352 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
353 <trans-unit id="6325096236207614377" datatype="html"> 353 <trans-unit id="6325096236207614377" datatype="html">
354 <source>Reason...</source> 354 <source>Reason...</source>
355 <target state="translated">Taɣẓint...</target> 355 <target state="translated">Taɣẓint...</target>
@@ -746,10 +746,10 @@
746 <trans-unit id="2602586221576511475" datatype="html"> 746 <trans-unit id="2602586221576511475" datatype="html">
747 <source>Video quota</source> 747 <source>Video quota</source>
748 <target state="translated">Amur n tvidyut</target> 748 <target state="translated">Amur n tvidyut</target>
749 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 749
750 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 750
751 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 751
752 </trans-unit> 752 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
753 <trans-unit id="1502595455339510144" datatype="html"> 753 <trans-unit id="1502595455339510144" datatype="html">
754 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 754 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
755 <target state="translated">War tilas <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/>i wass)<x id="CLOSE_TAG_NG_CONTAINER"/></target> 755 <target state="translated">War tilas <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/>i wass)<x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -840,7 +840,31 @@
840 <source>Federation</source> 840 <source>Federation</source>
841 <target state="translated">Federation</target> 841 <target state="translated">Federation</target>
842 842
843 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 843 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
844 <source>Following</source><target state="new">Following</target>
845 <context-group purpose="location">
846 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
847 <context context-type="linenumber">29</context>
848 </context-group>
849 <context-group purpose="location">
850 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
851 <context context-type="linenumber">31</context>
852 </context-group>
853 <context-group purpose="location">
854 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
855 <context context-type="linenumber">28</context>
856 </context-group>
857 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
858 <source>Followers</source><target state="new">Followers</target>
859 <context-group purpose="location">
860 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
861 <context context-type="linenumber">34</context>
862 </context-group>
863 <context-group purpose="location">
864 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
865 <context context-type="linenumber">37</context>
866 </context-group>
867 </trans-unit>
844 <trans-unit id="3541687134897970106" datatype="html"> 868 <trans-unit id="3541687134897970106" datatype="html">
845 <source>followers</source> 869 <source>followers</source>
846 <target state="translated">Ineḍfaren</target> 870 <target state="translated">Ineḍfaren</target>
@@ -1055,19 +1079,13 @@
1055 <trans-unit id="7252854992688790751" datatype="html"> 1079 <trans-unit id="7252854992688790751" datatype="html">
1056 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1080 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1057 <target state="translated">Tummant-a tsareg ajerred. Γas akken, ɣur-k·m senqed <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Tiwtilin<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Tiwtilin<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> send timerna n umiḍan. Tzemreḍ daɣen ad tnadiḍ tummant-nniḍen i wakken ad yemṣada swaswa wayen i tuḥwaǧeḍ deg: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1081 <target state="translated">Tummant-a tsareg ajerred. Γas akken, ɣur-k·m senqed <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Tiwtilin<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Tiwtilin<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> send timerna n umiḍan. Tzemreḍ daɣen ad tnadiḍ tummant-nniḍen i wakken ad yemṣada swaswa wayen i tuḥwaǧeḍ deg: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1058 <context-group purpose="location"> 1082
1059 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1083 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1060 <context context-type="linenumber">60,62</context>
1061 </context-group>
1062 </trans-unit>
1063 <trans-unit id="7215649348148521605" datatype="html"> 1084 <trans-unit id="7215649348148521605" datatype="html">
1064 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1085 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1065 <target state="translated">Akka tura tummant-a ur tsirig ara ajerred n yiseqdacen, ilaq ad tesneqdeḍ <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Tiwtilin<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> i wugar n telqayt neɣ i tifin n tummant ara ak·am-imudden tazmert n ujerred i umiḍan, syen ad d-tsalayeḍ tividyutin din. Af-d ayla-k·m gar waṭas n tummanin yemgaraden deg: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1086 <target state="translated">Akka tura tummant-a ur tsirig ara ajerred n yiseqdacen, ilaq ad tesneqdeḍ <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Tiwtilin<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> i wugar n telqayt neɣ i tifin n tummant ara ak·am-imudden tazmert n ujerred i umiḍan, syen ad d-tsalayeḍ tividyutin din. Af-d ayla-k·m gar waṭas n tummanin yemgaraden deg: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1066 <context-group purpose="location"> 1087
1067 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1088 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1068 <context context-type="linenumber">65,67</context>
1069 </context-group>
1070 </trans-unit>
1071 <trans-unit id="2392488717875840729" datatype="html"> 1089 <trans-unit id="2392488717875840729" datatype="html">
1072 <source>User</source> 1090 <source>User</source>
1073 <target>Aseqdac</target> 1091 <target>Aseqdac</target>
@@ -1078,66 +1096,66 @@
1078 <source>Username or email address</source> 1096 <source>Username or email address</source>
1079 <target>Isem n useqdac neɣ tansa imayl</target> 1097 <target>Isem n useqdac neɣ tansa imayl</target>
1080 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1098 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1099 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1100 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1101 <context-group purpose="location">
1102 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1103 <context context-type="linenumber">33,34</context>
1104 </context-group>
1081 </trans-unit> 1105 </trans-unit>
1082 <trans-unit id="1431416938026210429" datatype="html"> 1106 <trans-unit id="1431416938026210429" datatype="html">
1083 <source>Password</source> 1107 <source>Password</source>
1084 <target>Awal uffir</target> 1108 <target>Awal uffir</target>
1085 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1109
1086 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1110
1087 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1111
1088 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1112
1089 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1113
1090 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1114
1091 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1115
1092 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1116
1093 </trans-unit> 1117 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1094 <trans-unit id="8715156686857791956" datatype="html"> 1118 <trans-unit id="8715156686857791956" datatype="html">
1095 <source>Click here to reset your password</source> 1119 <source>Click here to reset your password</source>
1096 <target state="translated">Si da akken ad twenzeḍ awal uffir</target> 1120 <target state="translated">Si da akken ad twenzeḍ awal uffir</target>
1097 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1121
1098 </trans-unit> 1122 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1099 <trans-unit id="892063502898494584" datatype="html"> 1123 <trans-unit id="892063502898494584" datatype="html">
1100 <source>I forgot my password</source> 1124 <source>I forgot my password</source>
1101 <target state="translated">Ttuɣ awal-iw uffir</target> 1125 <target state="translated">Ttuɣ awal-iw uffir</target>
1102 <context-group purpose="location"> 1126
1103 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1127 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1104 <context context-type="linenumber">47</context>
1105 </context-group>
1106 </trans-unit>
1107 <trans-unit id="2101170466365500913" datatype="html"> 1128 <trans-unit id="2101170466365500913" datatype="html">
1108 <source>Logging into an account lets you publish content</source> 1129 <source>Logging into an account lets you publish content</source>
1109 <target state="translated">Anekcum ɣer umiḍan ad ak·akem-yeǧǧ ad tsuffɣeḍ agbur</target> 1130 <target state="translated">Anekcum ɣer umiḍan ad ak·akem-yeǧǧ ad tsuffɣeḍ agbur</target>
1110 <context-group purpose="location"> 1131
1111 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1132 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1112 <context context-type="linenumber">56,57</context>
1113 </context-group>
1114 </trans-unit>
1115 <trans-unit id="2454050363478003966" datatype="html"> 1133 <trans-unit id="2454050363478003966" datatype="html">
1116 <source>Login</source> 1134 <source>Login</source>
1117 <target>Tuqqna</target> 1135 <target>Tuqqna</target>
1118 1136
1119 1137
1120 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1138 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1121 <trans-unit id="3183213940445113677" datatype="html"> 1139 <trans-unit id="3183213940445113677" datatype="html">
1122 <source>Or sign in with</source> 1140 <source>Or sign in with</source>
1123 <target state="translated">Or sign in with</target> 1141 <target state="translated">Or sign in with</target>
1124 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1142
1125 </trans-unit> 1143 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1126 <trans-unit id="3238209155172574367" datatype="html"> 1144 <trans-unit id="3238209155172574367" datatype="html">
1127 <source>Forgot your password</source> 1145 <source>Forgot your password</source>
1128 <target state="translated">Tettuḍ awal-ik uffir</target> 1146 <target state="translated">Tettuḍ awal-ik uffir</target>
1129 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1147
1130 </trans-unit> 1148 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1131 <trans-unit id="87327320394367488" datatype="html"> 1149 <trans-unit id="87327320394367488" datatype="html">
1132 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1150 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1133 <target state="translated">Suref-aɣ, ur tezmireḍ ara ad d-terreḍ awal-inek·inem uffir acku anedbal-ik·im n tummant ur isefrek ara anagraw n yimayl n PeerTube.</target> 1151 <target state="translated">Suref-aɣ, ur tezmireḍ ara ad d-terreḍ awal-inek·inem uffir acku anedbal-ik·im n tummant ur isefrek ara anagraw n yimayl n PeerTube.</target>
1134 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1152
1135 </trans-unit> 1153 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1136 <trans-unit id="3188014010833256853" datatype="html"> 1154 <trans-unit id="3188014010833256853" datatype="html">
1137 <source>Enter your email address and we will send you a link to reset your password.</source> 1155 <source>Enter your email address and we will send you a link to reset your password.</source>
1138 <target state="translated">Sekem tansa-inek·inem n yimayl syen nekkni ad ak·am-n-aznen aseɣwen i wakken ad twennzeḍ awal-ik·im uffir.</target> 1156 <target state="translated">Sekem tansa-inek·inem n yimayl syen nekkni ad ak·am-n-aznen aseɣwen i wakken ad twennzeḍ awal-ik·im uffir.</target>
1139 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1157
1140 </trans-unit> 1158 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1141 <trans-unit id="1190256911880544559" datatype="html"> 1159 <trans-unit id="1190256911880544559" datatype="html">
1142 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1160 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1143The link will expire within 1 hour.</source> 1161The link will expire within 1 hour.</source>
@@ -1147,26 +1165,26 @@ The link will expire within 1 hour.</source>
1147 <trans-unit id="4768749765465246664" datatype="html"> 1165 <trans-unit id="4768749765465246664" datatype="html">
1148 <source>Email</source> 1166 <source>Email</source>
1149 <target>Imayl</target> 1167 <target>Imayl</target>
1150 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1168
1151 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1169
1152 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1170
1153 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1171
1154 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1172
1155 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1173
1156 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1174
1157 </trans-unit> 1175 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1158 <trans-unit id="3967269098753656610" datatype="html"> 1176 <trans-unit id="3967269098753656610" datatype="html">
1159 <source>Email address</source> 1177 <source>Email address</source>
1160 <target>Tansa email</target> 1178 <target>Tansa email</target>
1161 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1179
1162 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1180
1163 </trans-unit> 1181 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1164 <trans-unit id="7808756054397155068" datatype="html"> 1182 <trans-unit id="7808756054397155068" datatype="html">
1165 <source>Reset</source> 1183 <source>Reset</source>
1166 <target state="translated">Wennez</target> 1184 <target state="translated">Wennez</target>
1167 <note priority="1" from="description">Password reset button</note> 1185 <note priority="1" from="description">Password reset button</note>
1168 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1186
1169 </trans-unit> 1187 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1170 <trans-unit id="4319634264526091601" datatype="html"> 1188 <trans-unit id="4319634264526091601" datatype="html">
1171 <source>on this instance</source> 1189 <source>on this instance</source>
1172 <target state="translated">deg tummant-a</target> 1190 <target state="translated">deg tummant-a</target>
@@ -1507,7 +1525,7 @@ The link will expire within 1 hour.</source>
1507 <target>Rnu amiḍan</target> 1525 <target>Rnu amiḍan</target>
1508 1526
1509 1527
1510 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1528 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1511 1529
1512 <trans-unit id="8936704404804793618" datatype="html"> 1530 <trans-unit id="8936704404804793618" datatype="html">
1513 <source>Videos</source> 1531 <source>Videos</source>
@@ -1538,7 +1556,7 @@ The link will expire within 1 hour.</source>
1538 <source>VIDEOS</source> 1556 <source>VIDEOS</source>
1539 <target state="translated">VIDEOS</target> 1557 <target state="translated">VIDEOS</target>
1540 1558
1541 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 1559 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1542 <trans-unit id="667372110624203230" datatype="html"> 1560 <trans-unit id="667372110624203230" datatype="html">
1543 <source>Import jobs concurrency</source> 1561 <source>Import jobs concurrency</source>
1544 <target state="translated">Kter imahilen yemqaraben</target> 1562 <target state="translated">Kter imahilen yemqaraben</target>
@@ -1657,7 +1675,7 @@ The link will expire within 1 hour.</source>
1657 <source>I'm a teapot</source> 1675 <source>I'm a teapot</source>
1658 <target state="translated">Nekk d t·amsatay·t</target> 1676 <target state="translated">Nekk d t·amsatay·t</target>
1659 1677
1660 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit> 1678 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1661 <trans-unit id="1597262876035959248" datatype="html"> 1679 <trans-unit id="1597262876035959248" datatype="html">
1662 <source>That's an error.</source> 1680 <source>That's an error.</source>
1663 <target state="translated">Tagi d tuccḍa.</target> 1681 <target state="translated">Tagi d tuccḍa.</target>
@@ -1750,8 +1768,8 @@ The link will expire within 1 hour.</source>
1750 <trans-unit id="2971365540217107489" datatype="html"> 1768 <trans-unit id="2971365540217107489" datatype="html">
1751 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1769 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1752 <target state="translated">midyat ɣezzif aṭas i uqeddac-a. Ma ulac aɣilif nermes anedbal-ik·im ma yella tebɣiḍ ad ternuḍ deg teɣzi n talast.</target> 1770 <target state="translated">midyat ɣezzif aṭas i uqeddac-a. Ma ulac aɣilif nermes anedbal-ik·im ma yella tebɣiḍ ad ternuḍ deg teɣzi n talast.</target>
1753 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1771
1754 </trans-unit> 1772 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1755 <trans-unit id="2468689683507870964" datatype="html"> 1773 <trans-unit id="2468689683507870964" datatype="html">
1756 <source>In this instance's network</source> 1774 <source>In this instance's network</source>
1757 <target state="translated">Deg uzeṭṭa n tummant-a</target> 1775 <target state="translated">Deg uzeṭṭa n tummant-a</target>
@@ -2431,7 +2449,7 @@ The link will expire within 1 hour.</source>
2431 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2449 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2432 <source>Upload on hold</source><target state="new">Upload on hold</target> 2450 <source>Upload on hold</source><target state="new">Upload on hold</target>
2433 2451
2434 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2452 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2435 <trans-unit id="285180972645018518" datatype="html"> 2453 <trans-unit id="285180972645018518" datatype="html">
2436 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2454 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2437 <target state="translated">Nesḥassef, tamahilt n usali tensa i umiḍan-ik·im. Ma yella tebɣiḍ ad ternuḍ tividyutin, anedbal yezmer ad yekkes asekker afmiḍi-inek·inem.</target> 2455 <target state="translated">Nesḥassef, tamahilt n usali tensa i umiḍan-ik·im. Ma yella tebɣiḍ ad ternuḍ tividyutin, anedbal yezmer ad yekkes asekker afmiḍi-inek·inem.</target>
@@ -2999,11 +3017,7 @@ The link will expire within 1 hour.</source>
2999 <target state="translated">Tavidyut/Awennit/Amiḍan</target> 3017 <target state="translated">Tavidyut/Awennit/Amiḍan</target>
3000 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">22</context></context-group> 3018 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">22</context></context-group>
3001 </trans-unit> 3019 </trans-unit>
3002 <trans-unit id="2265605798180116441" datatype="html"> 3020
3003 <source>Follower handle</source>
3004 <target state="translated">Abagan n uneḍfar</target>
3005 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3006 </trans-unit>
3007 <trans-unit id="3301856295120048857" datatype="html"> 3021 <trans-unit id="3301856295120048857" datatype="html">
3008 <source>State <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3022 <source>State <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3009 <target state="translated">Addad n <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 3023 <target state="translated">Addad n <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -3167,11 +3181,7 @@ The link will expire within 1 hour.</source>
3167 <target state="translated">Ɛwed ajuṭun amynut</target> 3181 <target state="translated">Ɛwed ajuṭun amynut</target>
3168 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-applications/my-account-applications.component.html</context><context context-type="linenumber">35</context></context-group> 3182 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-applications/my-account-applications.component.html</context><context context-type="linenumber">35</context></context-group>
3169 </trans-unit> 3183 </trans-unit>
3170 <trans-unit id="6641024648411549335" datatype="html"> 3184
3171 <source>Host</source>
3172 <target>Asneftaɣ</target>
3173 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3174 </trans-unit>
3175 <trans-unit id="6571718060636962350" datatype="html"> 3185 <trans-unit id="6571718060636962350" datatype="html">
3176 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3186 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3177 <target state="translated">Tuɣalin tettusireg <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 3187 <target state="translated">Tuɣalin tettusireg <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -3181,7 +3191,7 @@ The link will expire within 1 hour.</source>
3181 <source>Unfollow</source> 3191 <source>Unfollow</source>
3182 <target state="translated">Ḥbes aḍfar</target> 3192 <target state="translated">Ḥbes aḍfar</target>
3183 3193
3184 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3194 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3185 <trans-unit id="8246779176913476983" datatype="html"> 3195 <trans-unit id="8246779176913476983" datatype="html">
3186 <source>Open instance in a new tab</source> 3196 <source>Open instance in a new tab</source>
3187 <target state="translated">Ldi tummant deg yiccer amaynut</target> 3197 <target state="translated">Ldi tummant deg yiccer amaynut</target>
@@ -3192,13 +3202,13 @@ The link will expire within 1 hour.</source>
3192 <trans-unit id="9132918641931433659" datatype="html"> 3202 <trans-unit id="9132918641931433659" datatype="html">
3193 <source>No host found matching current filters.</source> 3203 <source>No host found matching current filters.</source>
3194 <target state="translated">Ulac inebgi yettwafen yemṣada d yimsizedgen imiranen.</target> 3204 <target state="translated">Ulac inebgi yettwafen yemṣada d yimsizedgen imiranen.</target>
3195 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3205
3196 </trans-unit> 3206 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3197 <trans-unit id="7274241885665071790" datatype="html"> 3207 <trans-unit id="7274241885665071790" datatype="html">
3198 <source>Your instance is not following anyone.</source> 3208 <source>Your instance is not following anyone.</source>
3199 <target state="translated">Tummant-ik·im ulac win i teṭṭafar.</target> 3209 <target state="translated">Tummant-ik·im ulac win i teṭṭafar.</target>
3200 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3210
3201 </trans-unit> 3211 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3202 <trans-unit id="4774348799569692380" datatype="html"> 3212 <trans-unit id="4774348799569692380" datatype="html">
3203 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3213 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3204 <target state="translated">Sken <x id="INTERPOLATION"/> i <x id="INTERPOLATION_1"/> n <x id="INTERPOLATION_2"/> yinebgawen</target> 3214 <target state="translated">Sken <x id="INTERPOLATION"/> i <x id="INTERPOLATION_1"/> n <x id="INTERPOLATION_2"/> yinebgawen</target>
@@ -3207,18 +3217,10 @@ The link will expire within 1 hour.</source>
3207 <trans-unit id="4917252294930256268" datatype="html"> 3217 <trans-unit id="4917252294930256268" datatype="html">
3208 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3218 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3209 <target state="translated">Akka i d-yettban mačči ɣef uqeddac HTTPS i telliḍ. Aqeddac-ik·im web yesra ad yettwarmed TLS i uḍfar n yiqeddacen.</target> 3219 <target state="translated">Akka i d-yettban mačči ɣef uqeddac HTTPS i telliḍ. Aqeddac-ik·im web yesra ad yettwarmed TLS i uḍfar n yiqeddacen.</target>
3210 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 3220
3211 </trans-unit> 3221 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3212 <trans-unit id="6275803119759621687" datatype="html"> 3222
3213 <source>Follow domains</source> 3223
3214 <target state="translated">Ḍfer taɣulin</target>
3215 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3216 </trans-unit>
3217 <trans-unit id="1268699198448750610" datatype="html">
3218 <source>Follow instances</source>
3219 <target state="translated">Ḍfer tummanin</target>
3220 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3221 </trans-unit>
3222 <trans-unit id="9216117865911519658" datatype="html"> 3224 <trans-unit id="9216117865911519658" datatype="html">
3223 <source>Action</source> 3225 <source>Action</source>
3224 <target state="translated">Tigawt</target> 3226 <target state="translated">Tigawt</target>
@@ -3341,11 +3343,11 @@ The link will expire within 1 hour.</source>
3341 <trans-unit id="5248717555542428023" datatype="html"> 3343 <trans-unit id="5248717555542428023" datatype="html">
3342 <source>Username</source> 3344 <source>Username</source>
3343 <target>Nom d'utilisateur</target> 3345 <target>Nom d'utilisateur</target>
3344 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3346
3345 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3347
3346 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3348
3347 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3349
3348 </trans-unit> 3350 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3349 <trans-unit id="5428411040014095392" datatype="html"> 3351 <trans-unit id="5428411040014095392" datatype="html">
3350 <source>e.g. jane_doe</source> 3352 <source>e.g. jane_doe</source>
3351 <target state="translated">am. jane_doe</target> 3353 <target state="translated">am. jane_doe</target>
@@ -3373,9 +3375,9 @@ The link will expire within 1 hour.</source>
3373 <trans-unit id="4145496584631696119" datatype="html"> 3375 <trans-unit id="4145496584631696119" datatype="html">
3374 <source>Role</source> 3376 <source>Role</source>
3375 <target>Tamlilt</target> 3377 <target>Tamlilt</target>
3376 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3378
3377 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3379
3378 </trans-unit> 3380 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3379 <trans-unit id="7046347992315328430" datatype="html"> 3381 <trans-unit id="7046347992315328430" datatype="html">
3380 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3382 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3381 <target state="translated">Anigtengel yermed. Ableɣ n tvidyut yettaṭṭaf akan teɣzi <x id="START_TAG_STRONG"/>taneẓlit<x id="CLOSE_TAG_STRONG"/> n tvidyut. <x id="LINE_BREAK"/> Deg tuget, aseqdac-a yezmer ad d-isali ~ <x id="INTERPOLATION"/>. </target> 3383 <target state="translated">Anigtengel yermed. Ableɣ n tvidyut yettaṭṭaf akan teɣzi <x id="START_TAG_STRONG"/>taneẓlit<x id="CLOSE_TAG_STRONG"/> n tvidyut. <x id="LINE_BREAK"/> Deg tuget, aseqdac-a yezmer ad d-isali ~ <x id="INTERPOLATION"/>. </target>
@@ -3392,15 +3394,9 @@ The link will expire within 1 hour.</source>
3392 <trans-unit id="2622255144026150901" datatype="html"> 3394 <trans-unit id="2622255144026150901" datatype="html">
3393 <source>Auth plugin</source> 3395 <source>Auth plugin</source>
3394 <target state="translated">Izegrar n usesteb</target> 3396 <target state="translated">Izegrar n usesteb</target>
3395 <context-group purpose="location"> 3397
3396 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3398
3397 <context context-type="linenumber">188</context> 3399 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3398 </context-group>
3399 <context-group purpose="location">
3400 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3401 <context context-type="linenumber">188</context>
3402 </context-group>
3403 </trans-unit>
3404 <trans-unit id="588099657508661970" datatype="html"> 3400 <trans-unit id="588099657508661970" datatype="html">
3405 <source>None (local authentication)</source> 3401 <source>None (local authentication)</source>
3406 <target state="translated">Ulac (alɣu adigan)</target> 3402 <target state="translated">Ulac (alɣu adigan)</target>
@@ -3631,6 +3627,12 @@ The link will expire within 1 hour.</source>
3631 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3627 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3632 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3628 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3633 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3629 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3630 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3631 <source>Follower</source><target state="new">Follower</target>
3632 <context-group purpose="location">
3633 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3634 <context context-type="linenumber">24</context>
3635 </context-group>
3634 </trans-unit> 3636 </trans-unit>
3635 <trans-unit id="4691552465058437520" datatype="html"> 3637 <trans-unit id="4691552465058437520" datatype="html">
3636 <source>Commented video</source> 3638 <source>Commented video</source>
@@ -5465,6 +5467,12 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
5465channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 5467channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
5466 <target state="translated">Tebɣiḍ s tidet ad tekkseḍ <x id="PH" equiv-text="videoChannel.displayName"/>? Ad yekkes <x id="PH_1" equiv-text="videoChannel.videosCount"/> tividyutin i d-yettwasulin deg ubadu-a, syen ur tettizmired ara ad ternuḍ abadu-nniḍen s yisem-nni kan (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target> 5468 <target state="translated">Tebɣiḍ s tidet ad tekkseḍ <x id="PH" equiv-text="videoChannel.displayName"/>? Ad yekkes <x id="PH_1" equiv-text="videoChannel.videosCount"/> tividyutin i d-yettwasulin deg ubadu-a, syen ur tettizmired ara ad ternuḍ abadu-nniḍen s yisem-nni kan (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target>
5467 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 5469 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
5470 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
5471 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5472 <context-group purpose="location">
5473 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5474 <context context-type="linenumber">48</context>
5475 </context-group>
5468 </trans-unit> 5476 </trans-unit>
5469 <trans-unit id="5387007581996837469" datatype="html"> 5477 <trans-unit id="5387007581996837469" datatype="html">
5470 <source>My Channels</source> 5478 <source>My Channels</source>
@@ -5676,11 +5684,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
5676 <trans-unit id="5512878593724620692" datatype="html"> 5684 <trans-unit id="5512878593724620692" datatype="html">
5677 <source>CHANNELS</source> 5685 <source>CHANNELS</source>
5678 <target state="translated">IBUDA</target> 5686 <target state="translated">IBUDA</target>
5679 <context-group purpose="location"> 5687
5680 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5688 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5681 <context context-type="linenumber">82</context>
5682 </context-group>
5683 </trans-unit>
5684 <trans-unit id="3666829335406793239" datatype="html"> 5689 <trans-unit id="3666829335406793239" datatype="html">
5685 <source>This account does not have channels.</source> 5690 <source>This account does not have channels.</source>
5686 <target state="translated">Amiḍan-a ulac ɣur-s ibuda n tvidyut.</target> 5691 <target state="translated">Amiḍan-a ulac ɣur-s ibuda n tvidyut.</target>
@@ -6179,12 +6184,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6179 <source>Your message has been sent.</source> 6184 <source>Your message has been sent.</source>
6180 <target state="translated">Izen-ik·im yettwazen.</target> 6185 <target state="translated">Izen-ik·im yettwazen.</target>
6181 6186
6182 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6187 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6183 <trans-unit id="2072135752262464360" datatype="html"> 6188 <trans-unit id="2072135752262464360" datatype="html">
6184 <source>You already sent this form recently</source> 6189 <source>You already sent this form recently</source>
6185 <target state="translated">Tuzneḍ yakan tiferkit-a</target> 6190 <target state="translated">Tuzneḍ yakan tiferkit-a</target>
6186 6191
6187 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6192 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6188 <trans-unit id="819067926858619041" datatype="html"> 6193 <trans-unit id="819067926858619041" datatype="html">
6189 <source>Account videos</source> 6194 <source>Account videos</source>
6190 <target state="translated">Tividyutin n umiḍan</target> 6195 <target state="translated">Tividyutin n umiḍan</target>
@@ -6226,13 +6231,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6226 <trans-unit id="4856575356061361269" datatype="html"> 6231 <trans-unit id="4856575356061361269" datatype="html">
6227 <source><x id="PH"/> direct account followers </source> 6232 <source><x id="PH"/> direct account followers </source>
6228 <target state="translated"><x id="PH"/> ineḍfaren n umiḍan usriden </target> 6233 <target state="translated"><x id="PH"/> ineḍfaren n umiḍan usriden </target>
6229 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6234
6230 </trans-unit> 6235 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6231 <trans-unit id="6250999352462648289" datatype="html"> 6236 <trans-unit id="6250999352462648289" datatype="html">
6232 <source>Report this account</source> 6237 <source>Report this account</source>
6233 <target state="translated">Azen aneqqis ɣef umiḍan-a</target> 6238 <target state="translated">Azen aneqqis ɣef umiḍan-a</target>
6234 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6239
6235 </trans-unit> 6240 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6236 <trans-unit id="1504521795586863905" datatype="html"> 6241 <trans-unit id="1504521795586863905" datatype="html">
6237 <source>VIDEOS</source> 6242 <source>VIDEOS</source>
6238 <target state="translated">VIDEOS</target> 6243 <target state="translated">VIDEOS</target>
@@ -6242,29 +6247,21 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6242 <trans-unit id="25349740244798533" datatype="html"> 6247 <trans-unit id="25349740244798533" datatype="html">
6243 <source>Username copied</source> 6248 <source>Username copied</source>
6244 <target state="translated">Yettwanɣel yisem n useqdac</target> 6249 <target state="translated">Yettwanɣel yisem n useqdac</target>
6245 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6250
6246 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6251
6247 </trans-unit> 6252 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6248 <trans-unit id="9221735175659318025" datatype="html"> 6253 <trans-unit id="9221735175659318025" datatype="html">
6249 <source>1 subscriber</source> 6254 <source>1 subscriber</source>
6250 <target state="translated">1 umulteɣ</target> 6255 <target state="translated">1 umulteɣ</target>
6251 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6256
6252 </trans-unit> 6257 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6253 <trans-unit id="4097331874769079975" datatype="html"> 6258 <trans-unit id="4097331874769079975" datatype="html">
6254 <source><x id="PH"/> subscribers</source> 6259 <source><x id="PH"/> subscribers</source>
6255 <target state="translated"><x id="PH"/> yimultaɣ</target> 6260 <target state="translated"><x id="PH"/> yimultaɣ</target>
6256 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group>
6257 </trans-unit>
6258 <trans-unit id="4682675125751819107" datatype="html">
6259 <source>Instances you follow</source>
6260 <target state="translated">Tummanin i teṭṭafareḍ</target>
6261
6262 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6263 <trans-unit id="8899833753704589712" datatype="html">
6264 <source>Instances following you</source>
6265 <target state="translated">Tummanin i ak·akem-yeṭṭafaren</target>
6266 6261
6267 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit> 6262 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6263
6264
6268 <trans-unit id="3008420115644088420" datatype="html"> 6265 <trans-unit id="3008420115644088420" datatype="html">
6269 <source>Configuration</source> 6266 <source>Configuration</source>
6270 <target>Tawila</target> 6267 <target>Tawila</target>
@@ -6319,6 +6316,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6319 <source>Auto (via ffmpeg)</source> 6316 <source>Auto (via ffmpeg)</source>
6320 <target state="translated">Awurman (s ffmpeg)</target> 6317 <target state="translated">Awurman (s ffmpeg)</target>
6321 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6318 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6319 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6320 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6321 <context-group purpose="location">
6322 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6323 <context context-type="linenumber">3</context>
6324 </context-group>
6322 </trans-unit> 6325 </trans-unit>
6323 <trans-unit id="931255636742351800" datatype="html"> 6326 <trans-unit id="931255636742351800" datatype="html">
6324 <source>No limit</source> 6327 <source>No limit</source>
@@ -6459,23 +6462,39 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6459 <trans-unit id="2127446333083057097" datatype="html"> 6462 <trans-unit id="2127446333083057097" datatype="html">
6460 <source>Domain is required.</source> 6463 <source>Domain is required.</source>
6461 <target state="translated">Taɣult tettusra.</target> 6464 <target state="translated">Taɣult tettusra.</target>
6462 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6465
6463 </trans-unit> 6466 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6464 <trans-unit id="6780793142903080663" datatype="html"> 6467 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6465 <source>Domains entered are invalid.</source> 6468 <context-group purpose="location">
6466 <target state="translated">Tiɣula yettwaskecmen d tarimeɣta.</target> 6469 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6467 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6470 <context context-type="linenumber">93</context>
6468 </trans-unit> 6471 </context-group>
6469 <trans-unit id="5886492514458202177" datatype="html"> 6472 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6470 <source>Domains entered contain duplicates.</source> 6473 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6471 <target state="translated">Tiɣula yettwaskecmen yella deg-sent uεiwed.</target> 6474 <context-group purpose="location">
6472 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6475 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6476 <context context-type="linenumber">94</context>
6477 </context-group>
6478 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6479 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6480 <context-group purpose="location">
6481 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6482 <context context-type="linenumber">102</context>
6483 </context-group>
6484 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6485 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6486 <context-group purpose="location">
6487 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6488 <context context-type="linenumber">103</context>
6489 </context-group>
6473 </trans-unit> 6490 </trans-unit>
6491
6492
6474 <trans-unit id="2740793005745065895" datatype="html"> 6493 <trans-unit id="2740793005745065895" datatype="html">
6475 <source><x id="PH"/> is not valid </source> 6494 <source><x id="PH"/> is not valid </source>
6476 <target state="translated"><x id="PH"/> d arameɣtu </target> 6495 <target state="translated"><x id="PH"/> d arameɣtu </target>
6477 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6496
6478 </trans-unit> 6497 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6479 <trans-unit id="240806681889331244" datatype="html"> 6498 <trans-unit id="240806681889331244" datatype="html">
6480 <source>Unlimited</source> 6499 <source>Unlimited</source>
6481 <target>War talast</target> 6500 <target>War talast</target>
@@ -6629,17 +6648,43 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6629 <source><x id="PH"/> removed from instance followers </source> 6648 <source><x id="PH"/> removed from instance followers </source>
6630 <target state="translated"><x id="PH"/> yettwakkes sɣur ineḍfaren n tummant </target> 6649 <target state="translated"><x id="PH"/> yettwakkes sɣur ineḍfaren n tummant </target>
6631 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6650 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6651 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6652 <source>Follow</source><target state="new">Follow</target>
6653 <context-group purpose="location">
6654 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6655 <context context-type="linenumber">3</context>
6656 </context-group>
6657 <context-group purpose="location">
6658 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6659 <context context-type="linenumber">37</context>
6660 </context-group>
6661 <context-group purpose="location">
6662 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6663 <context context-type="linenumber">18</context>
6664 </context-group>
6665 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6666 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6667 <context-group purpose="location">
6668 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6669 <context context-type="linenumber">11</context>
6670 </context-group>
6632 </trans-unit> 6671 </trans-unit>
6633 <trans-unit id="2355066641781598196" datatype="html"> 6672 <trans-unit id="2355066641781598196" datatype="html">
6634 <source>Follow request(s) sent!</source> 6673 <source>Follow request(s) sent!</source>
6635 <target state="translated">Asuter n uḍfar yettwazen!</target> 6674 <target state="translated">Asuter n uḍfar yettwazen!</target>
6636 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6675
6676 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6677 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6678 <context-group purpose="location">
6679 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6680 <context context-type="linenumber">3</context>
6681 </context-group>
6637 </trans-unit> 6682 </trans-unit>
6638 <trans-unit id="4245720728052819482" datatype="html"> 6683 <trans-unit id="4245720728052819482" datatype="html">
6639 <source>Do you really want to unfollow <x id="PH"/>?</source> 6684 <source>Do you really want to unfollow <x id="PH"/>?</source>
6640 <target state="translated">Tebɣiḍ s tidet ad tḥebseḍ aḍfar n <x id="PH"/>?</target> 6685 <target state="translated">Tebɣiḍ s tidet ad tḥebseḍ aḍfar n <x id="PH"/>?</target>
6641 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6686
6642 </trans-unit> 6687 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6643 <trans-unit id="9160510009013134726" datatype="html"> 6688 <trans-unit id="9160510009013134726" datatype="html">
6644 <source>Unfollow</source> 6689 <source>Unfollow</source>
6645 <target state="translated">Ur ṭṭafarara</target> 6690 <target state="translated">Ur ṭṭafarara</target>
@@ -6648,8 +6693,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6648 <trans-unit id="3935234189109112926" datatype="html"> 6693 <trans-unit id="3935234189109112926" datatype="html">
6649 <source>You are not following <x id="PH"/> anymore.</source> 6694 <source>You are not following <x id="PH"/> anymore.</source>
6650 <target state="translated">Ur teṭṭafareḍ <x id="PH"/> yiwen.</target> 6695 <target state="translated">Ur teṭṭafareḍ <x id="PH"/> yiwen.</target>
6651 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6696
6652 </trans-unit> 6697 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6653 <trans-unit id="2593763089859685916" datatype="html"> 6698 <trans-unit id="2593763089859685916" datatype="html">
6654 <source>enabled</source> 6699 <source>enabled</source>
6655 <target>irmed</target> 6700 <target>irmed</target>
@@ -7188,9 +7233,9 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7188 <trans-unit id="1519954996184640001" datatype="html"> 7233 <trans-unit id="1519954996184640001" datatype="html">
7189 <source>Error</source> 7234 <source>Error</source>
7190 <target>Tuccḍa</target> 7235 <target>Tuccḍa</target>
7191 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7236
7192 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7237
7193 </trans-unit> 7238 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7194 <trans-unit id="5076187961693950167" datatype="html"> 7239 <trans-unit id="5076187961693950167" datatype="html">
7195 <source>Standard logs</source> 7240 <source>Standard logs</source>
7196 <target state="translated">Standard logs</target> 7241 <target state="translated">Standard logs</target>
@@ -7231,16 +7276,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7231 <target state="translated">Update user password</target> 7276 <target state="translated">Update user password</target>
7232 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7277 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7233 </trans-unit> 7278 </trans-unit>
7234 <trans-unit id="177544274549739411" datatype="html"> 7279
7235 <source>Following list</source> 7280
7236 <target state="translated">Tabdart n uḍfar</target>
7237 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7238 </trans-unit>
7239 <trans-unit id="8092429110007204784" datatype="html">
7240 <source>Followers list</source>
7241 <target state="translated">Tabdart n yineḍfaren</target>
7242 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7243 </trans-unit>
7244 <trans-unit id="780323526182667308" datatype="html"> 7281 <trans-unit id="780323526182667308" datatype="html">
7245 <source>User <x id="PH"/> updated.</source> 7282 <source>User <x id="PH"/> updated.</source>
7246 <target state="translated">Aseqdac <x id="PH"/> yettwaleqqem.</target> 7283 <target state="translated">Aseqdac <x id="PH"/> yettwaleqqem.</target>
@@ -7276,16 +7313,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7276 <target state="translated">Federation</target> 7313 <target state="translated">Federation</target>
7277 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7314 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7278 </trans-unit> 7315 </trans-unit>
7279 <trans-unit id="4682675125751819107" datatype="html"> 7316
7280 <source>Instances you follow</source> 7317
7281 <target state="translated">Tummanin teṭṭafareḍ</target>
7282 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7283 </trans-unit>
7284 <trans-unit id="8899833753704589712" datatype="html">
7285 <source>Instances following you</source>
7286 <target state="translated">Tummanin i ak·akem-yeṭṭafaren</target>
7287 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7288 </trans-unit>
7289 <trans-unit id="3767259920053407667" datatype="html"> 7318 <trans-unit id="3767259920053407667" datatype="html">
7290 <source>Videos will be deleted, comments will be tombstoned.</source> 7319 <source>Videos will be deleted, comments will be tombstoned.</source>
7291 <target state="translated">Tividyutin ad ttwakksent, iwenniten ad ttwasfesxen.</target> 7320 <target state="translated">Tividyutin ad ttwakksent, iwenniten ad ttwasfesxen.</target>
@@ -7316,7 +7345,25 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7316 <target state="translated">Sbadu imayl am wakken yettusenqed</target> 7345 <target state="translated">Sbadu imayl am wakken yettusenqed</target>
7317 7346
7318 7347
7319 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7348 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7349 <source>Created</source><target state="new">Created</target>
7350 <context-group purpose="location">
7351 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7352 <context context-type="linenumber">115</context>
7353 </context-group>
7354 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7355 <source>Daily quota</source><target state="new">Daily quota</target>
7356 <context-group purpose="location">
7357 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7358 <context context-type="linenumber">120</context>
7359 </context-group>
7360 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7361 <source>Last login</source><target state="new">Last login</target>
7362 <context-group purpose="location">
7363 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7364 <context context-type="linenumber">122</context>
7365 </context-group>
7366 </trans-unit>
7320 <trans-unit id="3403978719736970622" datatype="html"> 7367 <trans-unit id="3403978719736970622" datatype="html">
7321 <source>You cannot ban root.</source> 7368 <source>You cannot ban root.</source>
7322 <target state="translated">Ur tezmired ara ad tgedleḍ aseqdac aẓar.</target> 7369 <target state="translated">Ur tezmired ara ad tgedleḍ aseqdac aẓar.</target>
@@ -7614,13 +7661,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7614 <trans-unit id="1137937154872046253" datatype="html"> 7661 <trans-unit id="1137937154872046253" datatype="html">
7615 <source>Video channel <x id="PH"/> created.</source> 7662 <source>Video channel <x id="PH"/> created.</source>
7616 <target state="translated">Abadu n tvidyut <x id="PH"/> yettwarna.</target> 7663 <target state="translated">Abadu n tvidyut <x id="PH"/> yettwarna.</target>
7617 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 7664
7618 </trans-unit> 7665 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7619 <trans-unit id="8723777130353305761" datatype="html"> 7666 <trans-unit id="8723777130353305761" datatype="html">
7620 <source>This name already exists on this instance.</source> 7667 <source>This name already exists on this instance.</source>
7621 <target state="translated">Isem-a yella yakan ɣef tummant-a.</target> 7668 <target state="translated">Isem-a yella yakan ɣef tummant-a.</target>
7622 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 7669
7623 </trans-unit> 7670 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7624 <trans-unit id="7589345916094713536" datatype="html"> 7671 <trans-unit id="7589345916094713536" datatype="html">
7625 <source>Video channel <x id="PH"/> updated.</source> 7672 <source>Video channel <x id="PH"/> updated.</source>
7626 <target state="translated">Abadu n tvidyut <x id="PH"/> yettwaleqqem.</target> 7673 <target state="translated">Abadu n tvidyut <x id="PH"/> yettwaleqqem.</target>
@@ -7641,11 +7688,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7641 <target state="translated">Aɣrrac yettwakkes.</target> 7688 <target state="translated">Aɣrrac yettwakkes.</target>
7642 7689
7643 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7690 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7644 <trans-unit id="2575302837003821736" datatype="html"> 7691
7645 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7646 <target state="translated">Ttxil-k·m aru isem yettwaskanen n ubadu n tvidyut ( <x id="PH"/>) i usentem</target>
7647 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
7648 </trans-unit>
7649 <trans-unit id="624066830180032195" datatype="html"> 7692 <trans-unit id="624066830180032195" datatype="html">
7650 <source>Video channel <x id="PH"/> deleted.</source> 7693 <source>Video channel <x id="PH"/> deleted.</source>
7651 <target state="translated">Abadu n tvidyut <x id="PH"/> yettwakkes.</target> 7694 <target state="translated">Abadu n tvidyut <x id="PH"/> yettwakkes.</target>
@@ -7790,6 +7833,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7790 <source>Ownership change request sent.</source> 7833 <source>Ownership change request sent.</source>
7791 <target state="translated">Ownership change request sent.</target> 7834 <target state="translated">Ownership change request sent.</target>
7792 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 7835 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
7836 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
7837 <source>Sort by</source><target state="new">Sort by</target>
7838 <context-group purpose="location">
7839 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7840 <context context-type="linenumber">26</context>
7841 </context-group>
7793 </trans-unit> 7842 </trans-unit>
7794 <trans-unit id="3058024914967508975" datatype="html"> 7843 <trans-unit id="3058024914967508975" datatype="html">
7795 <source>My videos</source> 7844 <source>My videos</source>
@@ -7914,7 +7963,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7914 <target state="translated">Multeɣ ɣer umiḍan</target> 7963 <target state="translated">Multeɣ ɣer umiḍan</target>
7915 7964
7916 7965
7917 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 7966 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
7918 <trans-unit id="3131904093925601441" datatype="html"> 7967 <trans-unit id="3131904093925601441" datatype="html">
7919 <source>PLAYLISTS</source> 7968 <source>PLAYLISTS</source>
7920 <target state="translated">TIBDARIN N TΓURI</target> 7969 <target state="translated">TIBDARIN N TΓURI</target>
@@ -7961,34 +8010,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7961 <trans-unit id="3779524668013120370" datatype="html"> 8010 <trans-unit id="3779524668013120370" datatype="html">
7962 <source>Go to my subscriptions</source> 8011 <source>Go to my subscriptions</source>
7963 <target state="translated">Ddu ɣer yimultaɣ-inu</target> 8012 <target state="translated">Ddu ɣer yimultaɣ-inu</target>
7964 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8013
7965 </trans-unit> 8014 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
7966 <trans-unit id="1136469849928650779" datatype="html"> 8015 <trans-unit id="1136469849928650779" datatype="html">
7967 <source>Go to my videos</source> 8016 <source>Go to my videos</source>
7968 <target state="translated">Ddu ɣer tvidyutin-inu</target> 8017 <target state="translated">Ddu ɣer tvidyutin-inu</target>
7969 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8018
7970 </trans-unit> 8019 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
7971 <trans-unit id="7836683738999600376" datatype="html"> 8020 <trans-unit id="7836683738999600376" datatype="html">
7972 <source>Go to my imports</source> 8021 <source>Go to my imports</source>
7973 <target state="translated">Ddu ɣer waktaren-inu</target> 8022 <target state="translated">Ddu ɣer waktaren-inu</target>
7974 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8023
7975 </trans-unit> 8024 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
7976 <trans-unit id="7511292153332773503" datatype="html"> 8025 <trans-unit id="7511292153332773503" datatype="html">
7977 <source>Go to my channels</source> 8026 <source>Go to my channels</source>
7978 <target state="translated">Ddu ɣer yibuda-inu</target> 8027 <target state="translated">Ddu ɣer yibuda-inu</target>
7979 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8028
7980 </trans-unit> 8029 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
7981 <trans-unit id="2013324644839511073" datatype="html"> 8030 <trans-unit id="2013324644839511073" datatype="html">
7982 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8031 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
7983Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8032Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
7984 <target state="translated">D awezɣi ad d-nerr inekcam n umsaɣ OAuth: <x id="PH" equiv-text="error.text"/>. Ḍmen belli tsewleḍ PeerTube akken iwata (akaram n uswel/), aṭas tigezmi n "aqeddac web".</target> 8033 <target state="translated">D awezɣi ad d-nerr inekcam n umsaɣ OAuth: <x id="PH" equiv-text="error.text"/>. Ḍmen belli tsewleḍ PeerTube akken iwata (akaram n uswel/), aṭas tigezmi n "aqeddac web".</target>
7985 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8034
7986 </trans-unit> 8035 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
7987 <trans-unit id="375263728166936544" datatype="html"> 8036 <trans-unit id="375263728166936544" datatype="html">
7988 <source>You need to reconnect.</source> 8037 <source>You need to reconnect.</source>
7989 <target state="translated">Tesriḍ ad teqqneḍ.</target> 8038 <target state="translated">Tesriḍ ad teqqneḍ.</target>
7990 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8039
7991 </trans-unit> 8040 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
7992 <trans-unit id="2206638022166154361" datatype="html"> 8041 <trans-unit id="2206638022166154361" datatype="html">
7993 <source>Keyboard Shortcuts:</source> 8042 <source>Keyboard Shortcuts:</source>
7994 <target state="translated">Inegzumen n unaswi:</target> 8043 <target state="translated">Inegzumen n unaswi:</target>
@@ -7999,6 +8048,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7999 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8048 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8000 <context context-type="linenumber">98</context> 8049 <context context-type="linenumber">98</context>
8001 </context-group> 8050 </context-group>
8051 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8052 <source>In my library</source><target state="new">In my library</target>
8053 <context-group purpose="location">
8054 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8055 <context context-type="linenumber">104</context>
8056 </context-group>
8002 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 8057 </trans-unit><trans-unit id="232050922346936574" datatype="html">
8003 <source>Trending</source><target state="new">Trending</target> 8058 <source>Trending</source><target state="new">Trending</target>
8004 8059
@@ -8021,38 +8076,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8021 <trans-unit id="1266887509445371246" datatype="html"> 8076 <trans-unit id="1266887509445371246" datatype="html">
8022 <source>Incorrect username or password.</source> 8077 <source>Incorrect username or password.</source>
8023 <target>Isem n useqdac neɣ awal n uɛeddi d urameɣtu.</target> 8078 <target>Isem n useqdac neɣ awal n uɛeddi d urameɣtu.</target>
8024 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8079
8025 </trans-unit> 8080 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8026 <trans-unit id="6974874606619467663" datatype="html"> 8081 <trans-unit id="6974874606619467663" datatype="html">
8027 <source>Your account is blocked.</source> 8082 <source>Your account is blocked.</source>
8028 <target state="translated">Amiḍan-ik·im yettusewḥel.</target> 8083 <target state="translated">Amiḍan-ik·im yettusewḥel.</target>
8029 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8084
8030 </trans-unit> 8085 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8031 <trans-unit id="5633144232269377096" datatype="html"> 8086 <trans-unit id="5633144232269377096" datatype="html">
8032 <source>hide</source> 8087 <source>hide</source>
8033 <target>ffer</target> 8088 <target>ffer</target>
8034 8089
8035 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8090 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8036 <trans-unit id="8603861867909474404" datatype="html"> 8091 <trans-unit id="8603861867909474404" datatype="html">
8037 <source>blur</source> 8092 <source>blur</source>
8038 <target state="translated">blur</target> 8093 <target state="translated">blur</target>
8039 8094
8040 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8095 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8041 <trans-unit id="4534458451100881847" datatype="html"> 8096 <trans-unit id="4534458451100881847" datatype="html">
8042 <source>display</source> 8097 <source>display</source>
8043 <target state="translated">sken</target> 8098 <target state="translated">sken</target>
8044 8099
8045 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8100 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8046 <trans-unit id="4467323362722952678" datatype="html"> 8101 <trans-unit id="4467323362722952678" datatype="html">
8047 <source>Unknown</source> 8102 <source>Unknown</source>
8048 <target>D arussin</target> 8103 <target>D arussin</target>
8049 8104
8050 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8105 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8051 <trans-unit id="7939914198003891823" datatype="html"> 8106 <trans-unit id="7939914198003891823" datatype="html">
8052 <source>any language</source> 8107 <source>any language</source>
8053 <target state="translated">yal tutlayt</target> 8108 <target state="translated">yal tutlayt</target>
8054 8109
8055 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8110 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8056 8111
8057 <trans-unit id="8781423666414310853" datatype="html"> 8112 <trans-unit id="8781423666414310853" datatype="html">
8058 <source>Your password has been successfully reset!</source> 8113 <source>Your password has been successfully reset!</source>
@@ -9587,18 +9642,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9587 <trans-unit id="968295009933361070" datatype="html"> 9642 <trans-unit id="968295009933361070" datatype="html">
9588 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9643 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9589 <target state="translated">Aṭas n tikkal i tεerḍeḍ, ttxil-k·m εreḍ tikkelt-nniḍen seld <x id="PH"/> tesdatin.</target> 9644 <target state="translated">Aṭas n tikkal i tεerḍeḍ, ttxil-k·m εreḍ tikkelt-nniḍen seld <x id="PH"/> tesdatin.</target>
9590 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9645
9591 </trans-unit> 9646 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9592 <trans-unit id="4965472196059235310" datatype="html"> 9647 <trans-unit id="4965472196059235310" datatype="html">
9593 <source>Too many attempts, please try again later.</source> 9648 <source>Too many attempts, please try again later.</source>
9594 <target state="translated">Aṭas n yineεruḍen, ttxil-k·m εreḍ tikkelt-nniḍen.</target> 9649 <target state="translated">Aṭas n yineεruḍen, ttxil-k·m εreḍ tikkelt-nniḍen.</target>
9595 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9650
9596 </trans-unit> 9651 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9597 <trans-unit id="1693549688987384699" datatype="html"> 9652 <trans-unit id="1693549688987384699" datatype="html">
9598 <source>Server error. Please retry later.</source> 9653 <source>Server error. Please retry later.</source>
9599 <target state="translated">Tuccḍa deg uqeddac. Ttxil-k·m εreḍ tikkelt-nniḍen.</target> 9654 <target state="translated">Tuccḍa deg uqeddac. Ttxil-k·m εreḍ tikkelt-nniḍen.</target>
9600 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9655
9601 </trans-unit> 9656 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9602 <trans-unit id="5927402622550505067" datatype="html"> 9657 <trans-unit id="5927402622550505067" datatype="html">
9603 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9658 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9604 <target state="translated">Multeɣ ɣer meṛṛa ibuda imiranen n <x id="PH"/>. Ad d-teṭṭfeḍ ilɣa ɣef meṛṛa tividyutin timaynutin.</target> 9659 <target state="translated">Multeɣ ɣer meṛṛa ibuda imiranen n <x id="PH"/>. Ad d-teṭṭfeḍ ilɣa ɣef meṛṛa tividyutin timaynutin.</target>
@@ -10113,33 +10168,33 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10113 <source>Your video was uploaded to your account and is private.</source> 10168 <source>Your video was uploaded to your account and is private.</source>
10114 <target state="translated">Tavidyut-ik·im tettwasuli ɣer umiḍan-ik·im yerna d tusligt.</target> 10169 <target state="translated">Tavidyut-ik·im tettwasuli ɣer umiḍan-ik·im yerna d tusligt.</target>
10115 10170
10116 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10171 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10117 <trans-unit id="5699822024600815733" datatype="html"> 10172 <trans-unit id="5699822024600815733" datatype="html">
10118 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10173 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10119 <target state="translated">Maca ad tesruḥeḍ isefka yemcudden (tibzimin, aglam...), d tidet tebɣiḍ ad teffɣeḍ seg usebter-a?</target> 10174 <target state="translated">Maca ad tesruḥeḍ isefka yemcudden (tibzimin, aglam...), d tidet tebɣiḍ ad teffɣeḍ seg usebter-a?</target>
10120 10175
10121 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10176 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10122 <trans-unit id="1219739004043110649" datatype="html"> 10177 <trans-unit id="1219739004043110649" datatype="html">
10123 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10178 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10124 <target state="translated">Tavidyut-a mazal ur d-tettwasuli ara, d tidet tebɣiḍ ad teffɣeḍ seg usebter-a?</target> 10179 <target state="translated">Tavidyut-a mazal ur d-tettwasuli ara, d tidet tebɣiḍ ad teffɣeḍ seg usebter-a?</target>
10125 10180
10126 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10181 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10127 <trans-unit id="6932865105766151309" datatype="html"> 10182 <trans-unit id="6932865105766151309" datatype="html">
10128 <source>Upload</source> 10183 <source>Upload</source>
10129 <target state="translated">Sali</target> 10184 <target state="translated">Sali</target>
10130 10185
10131 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10186 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10132 <trans-unit id="8278735427925094503" datatype="html"> 10187 <trans-unit id="8278735427925094503" datatype="html">
10133 <source>Upload <x id="PH"/> </source> 10188 <source>Upload <x id="PH"/> </source>
10134 <target state="translated">Sali-d <x id="PH"/> </target> 10189 <target state="translated">Sali-d <x id="PH"/> </target>
10135 10190
10136 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10191 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10137 10192
10138 <trans-unit id="5981816353437801748" datatype="html"> 10193 <trans-unit id="5981816353437801748" datatype="html">
10139 <source>Video published.</source> 10194 <source>Video published.</source>
10140 <target state="translated">Tavidyut yettwasuffɣen.</target> 10195 <target state="translated">Tavidyut yettwasuffɣen.</target>
10141 10196
10142 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10197 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10143 10198
10144 10199
10145 <trans-unit id="764164089183618119" datatype="html"> 10200 <trans-unit id="764164089183618119" datatype="html">
@@ -10204,27 +10259,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10204 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10259 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10205 <target state="translated">Tavidyut-a ulac-itt deg tummant. Tebɣiḍ ad tettuwellheḍ ɣer tummant taneẓlit: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10260 <target state="translated">Tavidyut-a ulac-itt deg tummant. Tebɣiḍ ad tettuwellheḍ ɣer tummant taneẓlit: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10206 10261
10207 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10262 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10208 <trans-unit id="5761611056224181752" datatype="html"> 10263 <trans-unit id="5761611056224181752" datatype="html">
10209 <source>Redirection</source> 10264 <source>Redirection</source>
10210 <target state="translated">Allus n uwelleh</target> 10265 <target state="translated">Allus n uwelleh</target>
10211 10266
10212 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10267 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10213 <trans-unit id="8858527736400081688" datatype="html"> 10268 <trans-unit id="8858527736400081688" datatype="html">
10214 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10269 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10215 <target state="translated">Tavidyut-a deg-s agbur ai yimeqqranen neɣ agbur amḥulfu. D tidet tebɣiḍ ad t-twaliḍ?</target> 10270 <target state="translated">Tavidyut-a deg-s agbur ai yimeqqranen neɣ agbur amḥulfu. D tidet tebɣiḍ ad t-twaliḍ?</target>
10216 10271
10217 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10272 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10218 <trans-unit id="3937119019020041049" datatype="html"> 10273 <trans-unit id="3937119019020041049" datatype="html">
10219 <source>Mature or explicit content</source> 10274 <source>Mature or explicit content</source>
10220 <target state="translated">Agbur i yimeqqranen neq agbur amḥulfu</target> 10275 <target state="translated">Agbur i yimeqqranen neq agbur amḥulfu</target>
10221 10276
10222 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10277 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10223 <trans-unit id="1755474755114288376" datatype="html"> 10278 <trans-unit id="1755474755114288376" datatype="html">
10224 <source>Up Next</source> 10279 <source>Up Next</source>
10225 <target state="translated">Uḍfir</target> 10280 <target state="translated">Uḍfir</target>
10226 10281
10227 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10282 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10228 <trans-unit id="2159130950882492111" datatype="html"> 10283 <trans-unit id="2159130950882492111" datatype="html">
10229 <source>Cancel</source> 10284 <source>Cancel</source>
10230 <target state="translated">Sefsex</target> 10285 <target state="translated">Sefsex</target>
@@ -10234,62 +10289,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10234 <source>Autoplay is suspended</source> 10289 <source>Autoplay is suspended</source>
10235 <target state="translated">Taɣuri tawurmant tettwaseḥbes</target> 10290 <target state="translated">Taɣuri tawurmant tettwaseḥbes</target>
10236 10291
10237 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10292 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10238 <trans-unit id="7895294730547405228" datatype="html"> 10293 <trans-unit id="7895294730547405228" datatype="html">
10239 <source>Enter/exit fullscreen (requires player focus)</source> 10294 <source>Enter/exit fullscreen (requires player focus)</source>
10240 <target state="translated">Kcem/ffeɣ askar n ugdil aččuran (yesra afukus ɣef yimeɣri)</target> 10295 <target state="translated">Kcem/ffeɣ askar n ugdil aččuran (yesra afukus ɣef yimeɣri)</target>
10241 10296
10242 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10297 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10243 <trans-unit id="7618388257165864759" datatype="html"> 10298 <trans-unit id="7618388257165864759" datatype="html">
10244 <source>Play/Pause the video (requires player focus)</source> 10299 <source>Play/Pause the video (requires player focus)</source>
10245 <target state="translated">Taɣuri/Aḥbas n tvidyutPause(yesra afukus n tɣuri)</target> 10300 <target state="translated">Taɣuri/Aḥbas n tvidyutPause(yesra afukus n tɣuri)</target>
10246 10301
10247 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10302 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10248 <trans-unit id="7761890399634216630" datatype="html"> 10303 <trans-unit id="7761890399634216630" datatype="html">
10249 <source>Mute/unmute the video (requires player focus)</source> 10304 <source>Mute/unmute the video (requires player focus)</source>
10250 <target state="translated">Sgugem/kkes asgugem tavidyut (yesra afukus n tɣuri)</target> 10305 <target state="translated">Sgugem/kkes asgugem tavidyut (yesra afukus n tɣuri)</target>
10251 10306
10252 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10307 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10253 <trans-unit id="5996585232248234904" datatype="html"> 10308 <trans-unit id="5996585232248234904" datatype="html">
10254 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10309 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10255 <target state="translated">Ɛeddi ɣer ufmiḍi n tvidyut: 0 d 0% akked 9 d 90% (yesra afukus n tɣuri)</target> 10310 <target state="translated">Ɛeddi ɣer ufmiḍi n tvidyut: 0 d 0% akked 9 d 90% (yesra afukus n tɣuri)</target>
10256 10311
10257 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10312 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10258 <trans-unit id="3748765405903319998" datatype="html"> 10313 <trans-unit id="3748765405903319998" datatype="html">
10259 <source>Increase the volume (requires player focus)</source> 10314 <source>Increase the volume (requires player focus)</source>
10260 <target state="translated">Snerni ablaɣ (yesra afukus n tɣuri)</target> 10315 <target state="translated">Snerni ablaɣ (yesra afukus n tɣuri)</target>
10261 10316
10262 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10317 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10263 <trans-unit id="5810704036407159982" datatype="html"> 10318 <trans-unit id="5810704036407159982" datatype="html">
10264 <source>Decrease the volume (requires player focus)</source> 10319 <source>Decrease the volume (requires player focus)</source>
10265 <target state="translated">Senqes ableɣ (yesra afukus n tɣuri)</target> 10320 <target state="translated">Senqes ableɣ (yesra afukus n tɣuri)</target>
10266 10321
10267 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10322 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10268 <trans-unit id="2622048822548065691" datatype="html"> 10323 <trans-unit id="2622048822548065691" datatype="html">
10269 <source>Seek the video forward (requires player focus)</source> 10324 <source>Seek the video forward (requires player focus)</source>
10270 <target state="translated">Seddu tavidyut ɣer sdat (yesra afukus n tɣuri)</target> 10325 <target state="translated">Seddu tavidyut ɣer sdat (yesra afukus n tɣuri)</target>
10271 10326
10272 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10327 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10273 <trans-unit id="6540078205109221153" datatype="html"> 10328 <trans-unit id="6540078205109221153" datatype="html">
10274 <source>Seek the video backward (requires player focus)</source> 10329 <source>Seek the video backward (requires player focus)</source>
10275 <target state="translated">Err tavidyut ɣer deffir (yesra afukus n tɣuri)</target> 10330 <target state="translated">Err tavidyut ɣer deffir (yesra afukus n tɣuri)</target>
10276 10331
10277 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10332 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10278 <trans-unit id="1956491957766210808" datatype="html"> 10333 <trans-unit id="1956491957766210808" datatype="html">
10279 <source>Increase playback rate (requires player focus)</source> 10334 <source>Increase playback rate (requires player focus)</source>
10280 <target state="translated">Snerni arured n tɣuri (yesra afukus n tɣuri)</target> 10335 <target state="translated">Snerni arured n tɣuri (yesra afukus n tɣuri)</target>
10281 10336
10282 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10337 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10283 <trans-unit id="5495529997674803186" datatype="html"> 10338 <trans-unit id="5495529997674803186" datatype="html">
10284 <source>Decrease playback rate (requires player focus)</source> 10339 <source>Decrease playback rate (requires player focus)</source>
10285 <target state="translated">Senqes arured n tɣuri (yesra afukus n tɣuri)</target> 10340 <target state="translated">Senqes arured n tɣuri (yesra afukus n tɣuri)</target>
10286 10341
10287 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10342 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10288 <trans-unit id="3178343147230721210" datatype="html"> 10343 <trans-unit id="3178343147230721210" datatype="html">
10289 <source>Navigate in the video frame by frame (requires player focus)</source> 10344 <source>Navigate in the video frame by frame (requires player focus)</source>
10290 <target state="translated">Inig deg tvidyut tugna s tugna (yesra afukus ɣef yimeɣri)</target> 10345 <target state="translated">Inig deg tvidyut tugna s tugna (yesra afukus ɣef yimeɣri)</target>
10291 10346
10292 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10347 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10293 <trans-unit id="8025996572234182184" datatype="html"> 10348 <trans-unit id="8025996572234182184" datatype="html">
10294 <source>Like the video</source> 10349 <source>Like the video</source>
10295 <target state="translated">Teεǧeb-iyi tavidyut</target> 10350 <target state="translated">Teεǧeb-iyi tavidyut</target>
diff --git a/client/src/locale/angular.ko-KR.xlf b/client/src/locale/angular.ko-KR.xlf
index d02c433ac..af024c47c 100644
--- a/client/src/locale/angular.ko-KR.xlf
+++ b/client/src/locale/angular.ko-KR.xlf
@@ -256,7 +256,7 @@
256 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 256 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
257 </target> 257 </target>
258 258
259 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 259 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
260 <trans-unit id="1486537403020619891" datatype="html"> 260 <trans-unit id="1486537403020619891" datatype="html">
261 <source>My watch history</source> 261 <source>My watch history</source>
262 <target state="new">My watch history</target> 262 <target state="new">My watch history</target>
@@ -338,12 +338,12 @@
338 338
339 339
340 340
341 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 341 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
342 <trans-unit id="1006562256968398209" datatype="html"> 342 <trans-unit id="1006562256968398209" datatype="html">
343 <source>video</source> 343 <source>video</source>
344 <target state="translated">동영상</target> 344 <target state="translated">동영상</target>
345 345
346 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 346 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
347 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 347 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
348 348
349 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 349 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -407,10 +407,10 @@
407 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 407 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
408 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 408 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
409 409
410 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 410 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
411 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 411 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
412 412
413 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 413 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
414 414
415 <trans-unit id="5235042777215655908" datatype="html"> 415 <trans-unit id="5235042777215655908" datatype="html">
416 <source>subtitles</source> 416 <source>subtitles</source>
@@ -822,10 +822,10 @@
822 <trans-unit id="2602586221576511475"> 822 <trans-unit id="2602586221576511475">
823 <source>Video quota</source> 823 <source>Video quota</source>
824 <target>영상 업로드 제한</target> 824 <target>영상 업로드 제한</target>
825 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 825
826 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 826
827 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 827
828 </trans-unit> 828 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
829 <trans-unit id="1502595455339510144"> 829 <trans-unit id="1502595455339510144">
830 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 830 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
831 <target> 831 <target>
@@ -909,7 +909,31 @@
909 <source>Federation</source> 909 <source>Federation</source>
910 <target state="new">Federation</target> 910 <target state="new">Federation</target>
911 911
912 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 912 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
913 <source>Following</source><target state="new">Following</target>
914 <context-group purpose="location">
915 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
916 <context context-type="linenumber">29</context>
917 </context-group>
918 <context-group purpose="location">
919 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
920 <context context-type="linenumber">31</context>
921 </context-group>
922 <context-group purpose="location">
923 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
924 <context context-type="linenumber">28</context>
925 </context-group>
926 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
927 <source>Followers</source><target state="new">Followers</target>
928 <context-group purpose="location">
929 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
930 <context context-type="linenumber">34</context>
931 </context-group>
932 <context-group purpose="location">
933 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
934 <context context-type="linenumber">37</context>
935 </context-group>
936 </trans-unit>
913 <trans-unit id="3541687134897970106" datatype="html"> 937 <trans-unit id="3541687134897970106" datatype="html">
914 <source>followers</source> 938 <source>followers</source>
915 <target state="new">followers</target> 939 <target state="new">followers</target>
@@ -984,7 +1008,7 @@
984 1008
985 1009
986 1010
987 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 1011 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
988 <trans-unit id="3616223838716839702"> 1012 <trans-unit id="3616223838716839702">
989 <source>Ban this user</source> 1013 <source>Ban this user</source>
990 <target>이 사용자 강퇴</target> 1014 <target>이 사용자 강퇴</target>
@@ -1057,19 +1081,13 @@
1057 <trans-unit id="7252854992688790751" datatype="html"> 1081 <trans-unit id="7252854992688790751" datatype="html">
1058 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1082 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1059 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1083 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1060 <context-group purpose="location"> 1084
1061 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1085 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1062 <context context-type="linenumber">60,62</context>
1063 </context-group>
1064 </trans-unit>
1065 <trans-unit id="7215649348148521605" datatype="html"> 1086 <trans-unit id="7215649348148521605" datatype="html">
1066 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1087 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1067 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1088 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1068 <context-group purpose="location"> 1089
1069 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1090 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1070 <context context-type="linenumber">65,67</context>
1071 </context-group>
1072 </trans-unit>
1073 <trans-unit id="2392488717875840729"> 1091 <trans-unit id="2392488717875840729">
1074 <source>User</source> 1092 <source>User</source>
1075 <target>사용자</target> 1093 <target>사용자</target>
@@ -1080,68 +1098,68 @@
1080 <source>Username or email address</source> 1098 <source>Username or email address</source>
1081 <target>사용자명 혹은 이메일 주소</target> 1099 <target>사용자명 혹은 이메일 주소</target>
1082 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1100 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1101 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1102 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1103 <context-group purpose="location">
1104 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1105 <context context-type="linenumber">33,34</context>
1106 </context-group>
1083 </trans-unit> 1107 </trans-unit>
1084 <trans-unit id="1431416938026210429"> 1108 <trans-unit id="1431416938026210429">
1085 <source>Password</source> 1109 <source>Password</source>
1086 <target>암호</target> 1110 <target>암호</target>
1087 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1111
1088 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1112
1089 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1113
1090 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1114
1091 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1115
1092 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1116
1093 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1117
1094 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1118
1095 </trans-unit> 1119 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1096 <trans-unit id="8715156686857791956" datatype="html"> 1120 <trans-unit id="8715156686857791956" datatype="html">
1097 <source>Click here to reset your password</source> 1121 <source>Click here to reset your password</source>
1098 <target state="new">Click here to reset your password</target> 1122 <target state="new">Click here to reset your password</target>
1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1123
1100 </trans-unit> 1124 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1101 <trans-unit id="892063502898494584" datatype="html"> 1125 <trans-unit id="892063502898494584" datatype="html">
1102 <source>I forgot my password</source> 1126 <source>I forgot my password</source>
1103 <target state="new">I forgot my password</target> 1127 <target state="new">I forgot my password</target>
1104 <context-group purpose="location"> 1128
1105 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1129 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1106 <context context-type="linenumber">47</context>
1107 </context-group>
1108 </trans-unit>
1109 <trans-unit id="2101170466365500913" datatype="html"> 1130 <trans-unit id="2101170466365500913" datatype="html">
1110 <source>Logging into an account lets you publish content</source> 1131 <source>Logging into an account lets you publish content</source>
1111 <target state="new"> Logging into an account lets you publish content </target> 1132 <target state="new"> Logging into an account lets you publish content </target>
1112 <context-group purpose="location"> 1133
1113 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1134 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1114 <context context-type="linenumber">56,57</context>
1115 </context-group>
1116 </trans-unit>
1117 <trans-unit id="2454050363478003966"> 1135 <trans-unit id="2454050363478003966">
1118 <source>Login</source> 1136 <source>Login</source>
1119 <target>로그인</target> 1137 <target>로그인</target>
1120 1138
1121 1139
1122 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1140 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1123 <trans-unit id="3183213940445113677" datatype="html"> 1141 <trans-unit id="3183213940445113677" datatype="html">
1124 <source>Or sign in with</source> 1142 <source>Or sign in with</source>
1125 <target state="new">Or sign in with</target> 1143 <target state="new">Or sign in with</target>
1126 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1144
1127 </trans-unit> 1145 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1128 <trans-unit id="3238209155172574367"> 1146 <trans-unit id="3238209155172574367">
1129 <source>Forgot your password</source> 1147 <source>Forgot your password</source>
1130 <target>암호 잊음</target> 1148 <target>암호 잊음</target>
1131 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1149
1132 </trans-unit> 1150 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1133 <trans-unit id="87327320394367488" datatype="html"> 1151 <trans-unit id="87327320394367488" datatype="html">
1134 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1152 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1135 <target state="new"> 1153 <target state="new">
1136 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1154 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
1137 </target> 1155 </target>
1138 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1156
1139 </trans-unit> 1157 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1140 <trans-unit id="3188014010833256853" datatype="html"> 1158 <trans-unit id="3188014010833256853" datatype="html">
1141 <source>Enter your email address and we will send you a link to reset your password.</source> 1159 <source>Enter your email address and we will send you a link to reset your password.</source>
1142 <target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1160 <target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1143 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1161
1144 </trans-unit> 1162 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1145 <trans-unit id="1190256911880544559" datatype="html"> 1163 <trans-unit id="1190256911880544559" datatype="html">
1146 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1164 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1147The link will expire within 1 hour.</source> 1165The link will expire within 1 hour.</source>
@@ -1152,26 +1170,26 @@ The link will expire within 1 hour.</target>
1152 <trans-unit id="4768749765465246664"> 1170 <trans-unit id="4768749765465246664">
1153 <source>Email</source> 1171 <source>Email</source>
1154 <target>이메일</target> 1172 <target>이메일</target>
1155 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1173
1156 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1174
1157 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1175
1158 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1176
1159 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1177
1160 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1178
1161 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1179
1162 </trans-unit> 1180 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1163 <trans-unit id="3967269098753656610"> 1181 <trans-unit id="3967269098753656610">
1164 <source>Email address</source> 1182 <source>Email address</source>
1165 <target>이메일 주소</target> 1183 <target>이메일 주소</target>
1166 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1184
1167 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1185
1168 </trans-unit> 1186 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1169 <trans-unit id="7808756054397155068" datatype="html"> 1187 <trans-unit id="7808756054397155068" datatype="html">
1170 <source>Reset</source> 1188 <source>Reset</source>
1171 <target state="new">Reset</target> 1189 <target state="new">Reset</target>
1172 <note priority="1" from="description">Password reset button</note> 1190 <note priority="1" from="description">Password reset button</note>
1173 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1191
1174 </trans-unit> 1192 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1175 1193
1176 <trans-unit id="4319634264526091601" datatype="html"> 1194 <trans-unit id="4319634264526091601" datatype="html">
1177 <source>on this instance</source> 1195 <source>on this instance</source>
@@ -1544,7 +1562,7 @@ The link will expire within 1 hour.</target>
1544 <target>계정 만들기</target> 1562 <target>계정 만들기</target>
1545 1563
1546 1564
1547 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1565 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1548 1566
1549 <trans-unit id="3058024914967508975" datatype="html"> 1567 <trans-unit id="3058024914967508975" datatype="html">
1550 <source>My videos</source> 1568 <source>My videos</source>
@@ -1601,7 +1619,7 @@ The link will expire within 1 hour.</target>
1601 <source>VIDEOS</source> 1619 <source>VIDEOS</source>
1602 <target state="new">VIDEOS</target> 1620 <target state="new">VIDEOS</target>
1603 1621
1604 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 1622 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1605 <trans-unit id="667372110624203230" datatype="html"> 1623 <trans-unit id="667372110624203230" datatype="html">
1606 <source>Import jobs concurrency</source> 1624 <source>Import jobs concurrency</source>
1607 <target state="new">Import jobs concurrency</target> 1625 <target state="new">Import jobs concurrency</target>
@@ -1681,7 +1699,7 @@ The link will expire within 1 hour.</target>
1681 <source>I'm a teapot</source> 1699 <source>I'm a teapot</source>
1682 <target state="new">I'm a teapot</target> 1700 <target state="new">I'm a teapot</target>
1683 1701
1684 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit> 1702 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1685 <trans-unit id="1597262876035959248" datatype="html"> 1703 <trans-unit id="1597262876035959248" datatype="html">
1686 <source>That's an error.</source> 1704 <source>That's an error.</source>
1687 <target state="new">That's an error.</target> 1705 <target state="new">That's an error.</target>
@@ -1765,8 +1783,8 @@ The link will expire within 1 hour.</target>
1765 <trans-unit id="2971365540217107489" datatype="html"> 1783 <trans-unit id="2971365540217107489" datatype="html">
1766 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1784 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1767 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1785 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1768 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1786
1769 </trans-unit> 1787 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1770 1788
1771 <trans-unit id="5131854469652959713" datatype="html"> 1789 <trans-unit id="5131854469652959713" datatype="html">
1772 <source>GLOBAL SEARCH</source> 1790 <source>GLOBAL SEARCH</source>
@@ -2481,7 +2499,7 @@ The link will expire within 1 hour.</target>
2481 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2499 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2482 <source>Upload on hold</source><target state="new">Upload on hold</target> 2500 <source>Upload on hold</source><target state="new">Upload on hold</target>
2483 2501
2484 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2502 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2485 <trans-unit id="285180972645018518" datatype="html"> 2503 <trans-unit id="285180972645018518" datatype="html">
2486 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2504 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2487 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2505 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3228,11 +3246,7 @@ The link will expire within 1 hour.</target>
3228 <target state="new">ID</target> 3246 <target state="new">ID</target>
3229 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3247 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3230 </trans-unit> 3248 </trans-unit>
3231 <trans-unit id="2265605798180116441" datatype="html"> 3249
3232 <source>Follower handle</source>
3233 <target state="new">Follower handle</target>
3234
3235 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
3236 <trans-unit id="5911214550882917183" datatype="html"> 3250 <trans-unit id="5911214550882917183" datatype="html">
3237 <source>State</source> 3251 <source>State</source>
3238 <target state="new">State</target> 3252 <target state="new">State</target>
@@ -3307,11 +3321,7 @@ The link will expire within 1 hour.</target>
3307 </target> 3321 </target>
3308 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3322 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3309 </trans-unit> 3323 </trans-unit>
3310 <trans-unit id="6641024648411549335" datatype="html"> 3324
3311 <source>Host</source>
3312 <target state="new">Host</target>
3313
3314 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3315 <trans-unit id="6571718060636962350" datatype="html"> 3325 <trans-unit id="6571718060636962350" datatype="html">
3316 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3326 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3317 <target state="new">Redundancy allowed 3327 <target state="new">Redundancy allowed
@@ -3324,7 +3334,7 @@ The link will expire within 1 hour.</target>
3324 <source>Unfollow</source> 3334 <source>Unfollow</source>
3325 <target state="new">Unfollow</target> 3335 <target state="new">Unfollow</target>
3326 3336
3327 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3337 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3328 <trans-unit id="8246779176913476983" datatype="html"> 3338 <trans-unit id="8246779176913476983" datatype="html">
3329 <source>Open instance in a new tab</source> 3339 <source>Open instance in a new tab</source>
3330 <target state="new">Open instance in a new tab</target> 3340 <target state="new">Open instance in a new tab</target>
@@ -3336,12 +3346,12 @@ The link will expire within 1 hour.</target>
3336 <source>No host found matching current filters.</source> 3346 <source>No host found matching current filters.</source>
3337 <target state="new">No host found matching current filters.</target> 3347 <target state="new">No host found matching current filters.</target>
3338 3348
3339 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3349 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3340 <trans-unit id="7274241885665071790" datatype="html"> 3350 <trans-unit id="7274241885665071790" datatype="html">
3341 <source>Your instance is not following anyone.</source> 3351 <source>Your instance is not following anyone.</source>
3342 <target state="new">Your instance is not following anyone.</target> 3352 <target state="new">Your instance is not following anyone.</target>
3343 3353
3344 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3354 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3345 <trans-unit id="4774348799569692380" datatype="html"> 3355 <trans-unit id="4774348799569692380" datatype="html">
3346 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3356 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3347 <target state="new">Showing 3357 <target state="new">Showing
@@ -3351,16 +3361,8 @@ The link will expire within 1 hour.</target>
3351 </target> 3361 </target>
3352 3362
3353 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3363 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3354 <trans-unit id="6275803119759621687" datatype="html"> 3364
3355 <source>Follow domains</source> 3365 <trans-unit id="9216117865911519658" datatype="html">
3356 <target state="new">Follow domains</target>
3357
3358 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit>
3359 <trans-unit id="1268699198448750610" datatype="html">
3360 <source>Follow instances</source>
3361 <target state="new">Follow instances</target>
3362
3363 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3364 <source>Action</source><target state="new">Action</target> 3366 <source>Action</source><target state="new">Action</target>
3365 3367
3366 3368
@@ -3407,11 +3409,11 @@ The link will expire within 1 hour.</target>
3407 <trans-unit id="5248717555542428023" datatype="html"> 3409 <trans-unit id="5248717555542428023" datatype="html">
3408 <source>Username</source> 3410 <source>Username</source>
3409 <target state="new">Username</target> 3411 <target state="new">Username</target>
3410 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3412
3411 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3413
3412 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3414
3413 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3415
3414 </trans-unit> 3416 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3415 <trans-unit id="5428411040014095392" datatype="html"> 3417 <trans-unit id="5428411040014095392" datatype="html">
3416 <source>e.g. jane_doe</source> 3418 <source>e.g. jane_doe</source>
3417 <target state="new">e.g. jane_doe</target> 3419 <target state="new">e.g. jane_doe</target>
@@ -3441,9 +3443,9 @@ The link will expire within 1 hour.</target>
3441 <trans-unit id="4145496584631696119" datatype="html"> 3443 <trans-unit id="4145496584631696119" datatype="html">
3442 <source>Role</source> 3444 <source>Role</source>
3443 <target state="new">Role</target> 3445 <target state="new">Role</target>
3444 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3446
3445 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3447
3446 </trans-unit> 3448 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3447 <trans-unit id="7046347992315328430" datatype="html"> 3449 <trans-unit id="7046347992315328430" datatype="html">
3448 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3450 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3449 <target state="new"> 3451 <target state="new">
@@ -3468,15 +3470,9 @@ The link will expire within 1 hour.</target>
3468 <trans-unit id="2622255144026150901" datatype="html"> 3470 <trans-unit id="2622255144026150901" datatype="html">
3469 <source>Auth plugin</source> 3471 <source>Auth plugin</source>
3470 <target state="new">Auth plugin</target> 3472 <target state="new">Auth plugin</target>
3471 <context-group purpose="location"> 3473
3472 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3474
3473 <context context-type="linenumber">188</context> 3475 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3474 </context-group>
3475 <context-group purpose="location">
3476 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3477 <context context-type="linenumber">188</context>
3478 </context-group>
3479 </trans-unit>
3480 <trans-unit id="588099657508661970" datatype="html"> 3476 <trans-unit id="588099657508661970" datatype="html">
3481 <source>None (local authentication)</source> 3477 <source>None (local authentication)</source>
3482 <target state="new">None (local authentication)</target> 3478 <target state="new">None (local authentication)</target>
@@ -3735,7 +3731,13 @@ The link will expire within 1 hour.</target>
3735 3731
3736 3732
3737 3733
3738 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit> 3734 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3735 <source>Follower</source><target state="new">Follower</target>
3736 <context-group purpose="location">
3737 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3738 <context context-type="linenumber">24</context>
3739 </context-group>
3740 </trans-unit>
3739 <trans-unit id="4691552465058437520" datatype="html"> 3741 <trans-unit id="4691552465058437520" datatype="html">
3740 <source>Commented video</source> 3742 <source>Commented video</source>
3741 <target state="new">Commented video</target> 3743 <target state="new">Commented video</target>
@@ -4064,7 +4066,7 @@ The link will expire within 1 hour.</target>
4064 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 4066 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
4065 </target> 4067 </target>
4066 4068
4067 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 4069 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
4068 <trans-unit id="4058814854824495833" datatype="html"> 4070 <trans-unit id="4058814854824495833" datatype="html">
4069 <source>Mute domains</source> 4071 <source>Mute domains</source>
4070 <target state="new">Mute domains</target> 4072 <target state="new">Mute domains</target>
@@ -6026,11 +6028,8 @@ color: red;
6026 6028
6027 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 6029 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
6028 <source>CHANNELS</source><target state="new">CHANNELS</target> 6030 <source>CHANNELS</source><target state="new">CHANNELS</target>
6029 <context-group purpose="location"> 6031
6030 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 6032 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
6031 <context context-type="linenumber">82</context>
6032 </context-group>
6033 </trans-unit>
6034 6033
6035 <trans-unit id="3666829335406793239" datatype="html"> 6034 <trans-unit id="3666829335406793239" datatype="html">
6036 <source>This account does not have channels.</source> 6035 <source>This account does not have channels.</source>
@@ -6070,7 +6069,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6070It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 6069It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
6071channel with the same name (<x id="PH_2"/>)!</target> 6070channel with the same name (<x id="PH_2"/>)!</target>
6072 6071
6073 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 6072 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
6073 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
6074 <context-group purpose="location">
6075 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
6076 <context context-type="linenumber">48</context>
6077 </context-group>
6078 </trans-unit>
6074 <trans-unit id="5387007581996837469" datatype="html"> 6079 <trans-unit id="5387007581996837469" datatype="html">
6075 <source>My Channels</source> 6080 <source>My Channels</source>
6076 <target state="new">My Channels</target> 6081 <target state="new">My Channels</target>
@@ -6672,12 +6677,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6672 <source>Your message has been sent.</source> 6677 <source>Your message has been sent.</source>
6673 <target state="new">Your message has been sent.</target> 6678 <target state="new">Your message has been sent.</target>
6674 6679
6675 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6680 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6676 <trans-unit id="2072135752262464360" datatype="html"> 6681 <trans-unit id="2072135752262464360" datatype="html">
6677 <source>You already sent this form recently</source> 6682 <source>You already sent this form recently</source>
6678 <target state="new">You already sent this form recently</target> 6683 <target state="new">You already sent this form recently</target>
6679 6684
6680 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6685 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6681 6686
6682 <trans-unit id="819067926858619041" datatype="html"> 6687 <trans-unit id="819067926858619041" datatype="html">
6683 <source>Account videos</source> 6688 <source>Account videos</source>
@@ -6727,12 +6732,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6727 <x id="PH"/> direct account followers 6732 <x id="PH"/> direct account followers
6728 </target> 6733 </target>
6729 6734
6730 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit> 6735 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6731 <trans-unit id="6250999352462648289" datatype="html"> 6736 <trans-unit id="6250999352462648289" datatype="html">
6732 <source>Report this account</source> 6737 <source>Report this account</source>
6733 <target state="new">Report this account</target> 6738 <target state="new">Report this account</target>
6734 6739
6735 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6740 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6736 6741
6737 6742
6738 <trans-unit id="1504521795586863905" datatype="html"> 6743 <trans-unit id="1504521795586863905" datatype="html">
@@ -6747,27 +6752,19 @@ channel with the same name (<x id="PH_2"/>)!</target>
6747 <target state="new">Username copied</target> 6752 <target state="new">Username copied</target>
6748 6753
6749 6754
6750 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit> 6755 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6751 <trans-unit id="9221735175659318025" datatype="html"> 6756 <trans-unit id="9221735175659318025" datatype="html">
6752 <source>1 subscriber</source> 6757 <source>1 subscriber</source>
6753 <target state="new">1 subscriber</target> 6758 <target state="new">1 subscriber</target>
6754 6759
6755 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit> 6760 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6756 <trans-unit id="4097331874769079975" datatype="html"> 6761 <trans-unit id="4097331874769079975" datatype="html">
6757 <source><x id="PH"/> subscribers</source> 6762 <source><x id="PH"/> subscribers</source>
6758 <target state="new"><x id="PH"/> subscribers</target> 6763 <target state="new"><x id="PH"/> subscribers</target>
6759 6764
6760 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6765 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6761 <trans-unit id="4682675125751819107" datatype="html"> 6766
6762 <source>Instances you follow</source> 6767
6763 <target state="new">Instances you follow</target>
6764
6765 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6766 <trans-unit id="8899833753704589712" datatype="html">
6767 <source>Instances following you</source>
6768 <target state="new">Instances following you</target>
6769
6770 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6771 <trans-unit id="1035838766454786107" datatype="html"> 6768 <trans-unit id="1035838766454786107" datatype="html">
6772 <source>Audio-only</source> 6769 <source>Audio-only</source>
6773 <target state="new">Audio-only</target> 6770 <target state="new">Audio-only</target>
@@ -6817,6 +6814,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6817 <source>Auto (via ffmpeg)</source> 6814 <source>Auto (via ffmpeg)</source>
6818 <target state="new">Auto (via ffmpeg)</target> 6815 <target state="new">Auto (via ffmpeg)</target>
6819 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6816 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6817 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6818 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6819 <context-group purpose="location">
6820 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6821 <context context-type="linenumber">3</context>
6822 </context-group>
6820 </trans-unit> 6823 </trans-unit>
6821 <trans-unit id="931255636742351800" datatype="html"> 6824 <trans-unit id="931255636742351800" datatype="html">
6822 <source>No limit</source> 6825 <source>No limit</source>
@@ -6959,18 +6962,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
6959 <trans-unit id="2127446333083057097" datatype="html"> 6962 <trans-unit id="2127446333083057097" datatype="html">
6960 <source>Domain is required.</source> 6963 <source>Domain is required.</source>
6961 <target state="new">Domain is required.</target> 6964 <target state="new">Domain is required.</target>
6962 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6965
6963 </trans-unit> 6966 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6964 <trans-unit id="6780793142903080663" datatype="html"> 6967 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6965 <source>Domains entered are invalid.</source> 6968 <context-group purpose="location">
6966 <target state="new">Domains entered are invalid.</target> 6969 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6967 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6970 <context context-type="linenumber">93</context>
6968 </trans-unit> 6971 </context-group>
6969 <trans-unit id="5886492514458202177" datatype="html"> 6972 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6970 <source>Domains entered contain duplicates.</source> 6973 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6971 <target state="new">Domains entered contain duplicates.</target> 6974 <context-group purpose="location">
6972 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6975 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6976 <context context-type="linenumber">94</context>
6977 </context-group>
6978 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6979 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6980 <context-group purpose="location">
6981 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6982 <context context-type="linenumber">102</context>
6983 </context-group>
6984 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6985 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6986 <context-group purpose="location">
6987 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6988 <context context-type="linenumber">103</context>
6989 </context-group>
6973 </trans-unit> 6990 </trans-unit>
6991
6992
6974 <trans-unit id="240806681889331244" datatype="html"> 6993 <trans-unit id="240806681889331244" datatype="html">
6975 <source>Unlimited</source> 6994 <source>Unlimited</source>
6976 <target state="new">Unlimited</target> 6995 <target state="new">Unlimited</target>
@@ -7130,26 +7149,52 @@ channel with the same name (<x id="PH_2"/>)!</target>
7130 <x id="PH"/> removed from instance followers 7149 <x id="PH"/> removed from instance followers
7131 </target> 7150 </target>
7132 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 7151 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
7152 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
7153 <source>Follow</source><target state="new">Follow</target>
7154 <context-group purpose="location">
7155 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7156 <context context-type="linenumber">3</context>
7157 </context-group>
7158 <context-group purpose="location">
7159 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7160 <context context-type="linenumber">37</context>
7161 </context-group>
7162 <context-group purpose="location">
7163 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7164 <context context-type="linenumber">18</context>
7165 </context-group>
7166 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
7167 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
7168 <context-group purpose="location">
7169 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7170 <context context-type="linenumber">11</context>
7171 </context-group>
7133 </trans-unit> 7172 </trans-unit>
7134 <trans-unit id="2740793005745065895" datatype="html"> 7173 <trans-unit id="2740793005745065895" datatype="html">
7135 <source><x id="PH"/> is not valid </source> 7174 <source><x id="PH"/> is not valid </source>
7136 <target state="new"> 7175 <target state="new">
7137 <x id="PH"/> is not valid 7176 <x id="PH"/> is not valid
7138 </target> 7177 </target>
7139 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 7178
7140 </trans-unit> 7179 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
7141 <trans-unit id="2355066641781598196" datatype="html"> 7180 <trans-unit id="2355066641781598196" datatype="html">
7142 <source>Follow request(s) sent!</source> 7181 <source>Follow request(s) sent!</source>
7143 <target state="new">Follow request(s) sent!</target> 7182 <target state="new">Follow request(s) sent!</target>
7144 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 7183
7184 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
7185 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
7186 <context-group purpose="location">
7187 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7188 <context context-type="linenumber">3</context>
7189 </context-group>
7145 </trans-unit> 7190 </trans-unit>
7146 <trans-unit id="4245720728052819482" datatype="html"> 7191 <trans-unit id="4245720728052819482" datatype="html">
7147 <source>Do you really want to unfollow <x id="PH"/>?</source> 7192 <source>Do you really want to unfollow <x id="PH"/>?</source>
7148 <target state="new">Do you really want to unfollow 7193 <target state="new">Do you really want to unfollow
7149 <x id="PH"/>? 7194 <x id="PH"/>?
7150 </target> 7195 </target>
7151 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 7196
7152 </trans-unit> 7197 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
7153 <trans-unit id="9160510009013134726" datatype="html"> 7198 <trans-unit id="9160510009013134726" datatype="html">
7154 <source>Unfollow</source> 7199 <source>Unfollow</source>
7155 <target state="new">Unfollow</target> 7200 <target state="new">Unfollow</target>
@@ -7160,8 +7205,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7160 <target state="new">You are not following 7205 <target state="new">You are not following
7161 <x id="PH"/> anymore. 7206 <x id="PH"/> anymore.
7162 </target> 7207 </target>
7163 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 7208
7164 </trans-unit> 7209 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
7165 <trans-unit id="2593763089859685916" datatype="html"> 7210 <trans-unit id="2593763089859685916" datatype="html">
7166 <source>enabled</source> 7211 <source>enabled</source>
7167 <target state="new">enabled</target> 7212 <target state="new">enabled</target>
@@ -7628,9 +7673,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7628 <trans-unit id="1519954996184640001" datatype="html"> 7673 <trans-unit id="1519954996184640001" datatype="html">
7629 <source>Error</source> 7674 <source>Error</source>
7630 <target state="new">Error</target> 7675 <target state="new">Error</target>
7631 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7676
7632 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7677
7633 </trans-unit> 7678 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7634 <trans-unit id="5076187961693950167" datatype="html"> 7679 <trans-unit id="5076187961693950167" datatype="html">
7635 <source>Standard logs</source> 7680 <source>Standard logs</source>
7636 <target state="new">Standard logs</target> 7681 <target state="new">Standard logs</target>
@@ -7675,16 +7720,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7675 <target state="new">Update user password</target> 7720 <target state="new">Update user password</target>
7676 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7721 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7677 </trans-unit> 7722 </trans-unit>
7678 <trans-unit id="177544274549739411" datatype="html"> 7723
7679 <source>Following list</source> 7724
7680 <target state="new">Following list</target>
7681 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7682 </trans-unit>
7683 <trans-unit id="8092429110007204784" datatype="html">
7684 <source>Followers list</source>
7685 <target state="new">Followers list</target>
7686 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7687 </trans-unit>
7688 <trans-unit id="780323526182667308" datatype="html"> 7725 <trans-unit id="780323526182667308" datatype="html">
7689 <source>User <x id="PH"/> updated.</source> 7726 <source>User <x id="PH"/> updated.</source>
7690 <target state="new">User 7727 <target state="new">User
@@ -7724,16 +7761,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7724 <target state="new">Federation</target> 7761 <target state="new">Federation</target>
7725 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7762 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7726 </trans-unit> 7763 </trans-unit>
7727 <trans-unit id="4682675125751819107" datatype="html"> 7764
7728 <source>Instances you follow</source> 7765
7729 <target state="new">Instances you follow</target>
7730 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7731 </trans-unit>
7732 <trans-unit id="8899833753704589712" datatype="html">
7733 <source>Instances following you</source>
7734 <target state="new">Instances following you</target>
7735 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7736 </trans-unit>
7737 <trans-unit id="3767259920053407667" datatype="html"> 7766 <trans-unit id="3767259920053407667" datatype="html">
7738 <source>Videos will be deleted, comments will be tombstoned.</source> 7767 <source>Videos will be deleted, comments will be tombstoned.</source>
7739 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7768 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7764,7 +7793,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7764 <target state="new">Set Email as Verified</target> 7793 <target state="new">Set Email as Verified</target>
7765 7794
7766 7795
7767 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7796 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7797 <source>Created</source><target state="new">Created</target>
7798 <context-group purpose="location">
7799 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7800 <context context-type="linenumber">115</context>
7801 </context-group>
7802 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7803 <source>Daily quota</source><target state="new">Daily quota</target>
7804 <context-group purpose="location">
7805 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7806 <context context-type="linenumber">120</context>
7807 </context-group>
7808 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7809 <source>Last login</source><target state="new">Last login</target>
7810 <context-group purpose="location">
7811 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7812 <context context-type="linenumber">122</context>
7813 </context-group>
7814 </trans-unit>
7768 <trans-unit id="3403978719736970622" datatype="html"> 7815 <trans-unit id="3403978719736970622" datatype="html">
7769 <source>You cannot ban root.</source> 7816 <source>You cannot ban root.</source>
7770 <target state="new">You cannot ban root.</target> 7817 <target state="new">You cannot ban root.</target>
@@ -8076,12 +8123,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8076 <x id="PH"/> created. 8123 <x id="PH"/> created.
8077 </target> 8124 </target>
8078 8125
8079 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 8126 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
8080 <trans-unit id="8723777130353305761" datatype="html"> 8127 <trans-unit id="8723777130353305761" datatype="html">
8081 <source>This name already exists on this instance.</source> 8128 <source>This name already exists on this instance.</source>
8082 <target state="new">This name already exists on this instance.</target> 8129 <target state="new">This name already exists on this instance.</target>
8083 8130
8084 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 8131 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
8085 <trans-unit id="7589345916094713536" datatype="html"> 8132 <trans-unit id="7589345916094713536" datatype="html">
8086 <source>Video channel <x id="PH"/> updated.</source> 8133 <source>Video channel <x id="PH"/> updated.</source>
8087 <target state="new">Video channel 8134 <target state="new">Video channel
@@ -8098,13 +8145,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8098 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 8145 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
8099 8146
8100 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 8147 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
8101 <trans-unit id="2575302837003821736" datatype="html"> 8148
8102 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
8103 <target state="new">Please type the display name of the video channel (
8104 <x id="PH"/>) to confirm
8105 </target>
8106
8107 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
8108 <trans-unit id="624066830180032195" datatype="html"> 8149 <trans-unit id="624066830180032195" datatype="html">
8109 <source>Video channel <x id="PH"/> deleted.</source> 8150 <source>Video channel <x id="PH"/> deleted.</source>
8110 <target state="new">Video channel 8151 <target state="new">Video channel
@@ -8267,6 +8308,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8267 <source>Ownership change request sent.</source> 8308 <source>Ownership change request sent.</source>
8268 <target state="new">Ownership change request sent.</target> 8309 <target state="new">Ownership change request sent.</target>
8269 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8310 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8311 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8312 <source>Sort by</source><target state="new">Sort by</target>
8313 <context-group purpose="location">
8314 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8315 <context context-type="linenumber">26</context>
8316 </context-group>
8270 </trans-unit> 8317 </trans-unit>
8271 <trans-unit id="3245220240937722814" datatype="html"> 8318 <trans-unit id="3245220240937722814" datatype="html">
8272 <source>My channels</source> 8319 <source>My channels</source>
@@ -8372,7 +8419,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8372 <target state="new">Subscribe to the account</target> 8419 <target state="new">Subscribe to the account</target>
8373 8420
8374 8421
8375 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 8422 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
8376 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 8423 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
8377 <context-group purpose="location"> 8424 <context-group purpose="location">
8378 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 8425 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -8418,35 +8465,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8418 <trans-unit id="3779524668013120370" datatype="html"> 8465 <trans-unit id="3779524668013120370" datatype="html">
8419 <source>Go to my subscriptions</source> 8466 <source>Go to my subscriptions</source>
8420 <target state="new">Go to my subscriptions</target> 8467 <target state="new">Go to my subscriptions</target>
8421 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8468
8422 </trans-unit> 8469 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8423 <trans-unit id="1136469849928650779" datatype="html"> 8470 <trans-unit id="1136469849928650779" datatype="html">
8424 <source>Go to my videos</source> 8471 <source>Go to my videos</source>
8425 <target state="new">Go to my videos</target> 8472 <target state="new">Go to my videos</target>
8426 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8473
8427 </trans-unit> 8474 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8428 <trans-unit id="7836683738999600376" datatype="html"> 8475 <trans-unit id="7836683738999600376" datatype="html">
8429 <source>Go to my imports</source> 8476 <source>Go to my imports</source>
8430 <target state="new">Go to my imports</target> 8477 <target state="new">Go to my imports</target>
8431 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8478
8432 </trans-unit> 8479 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8433 <trans-unit id="7511292153332773503" datatype="html"> 8480 <trans-unit id="7511292153332773503" datatype="html">
8434 <source>Go to my channels</source> 8481 <source>Go to my channels</source>
8435 <target state="new">Go to my channels</target> 8482 <target state="new">Go to my channels</target>
8436 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8483
8437 </trans-unit> 8484 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8438 <trans-unit id="2013324644839511073" datatype="html"> 8485 <trans-unit id="2013324644839511073" datatype="html">
8439 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8486 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8440Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8487Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8441 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8488 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8442Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8489Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8443 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8490
8444 </trans-unit> 8491 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8445 <trans-unit id="375263728166936544" datatype="html"> 8492 <trans-unit id="375263728166936544" datatype="html">
8446 <source>You need to reconnect.</source> 8493 <source>You need to reconnect.</source>
8447 <target state="new">You need to reconnect.</target> 8494 <target state="new">You need to reconnect.</target>
8448 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8495
8449 </trans-unit> 8496 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8450 <trans-unit id="2206638022166154361" datatype="html"> 8497 <trans-unit id="2206638022166154361" datatype="html">
8451 <source>Keyboard Shortcuts:</source> 8498 <source>Keyboard Shortcuts:</source>
8452 <target state="new">Keyboard Shortcuts:</target> 8499 <target state="new">Keyboard Shortcuts:</target>
@@ -8457,6 +8504,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8457 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8504 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8458 <context context-type="linenumber">98</context> 8505 <context context-type="linenumber">98</context>
8459 </context-group> 8506 </context-group>
8507 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8508 <source>In my library</source><target state="new">In my library</target>
8509 <context-group purpose="location">
8510 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8511 <context context-type="linenumber">104</context>
8512 </context-group>
8460 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 8513 </trans-unit><trans-unit id="232050922346936574" datatype="html">
8461 <source>Trending</source><target state="new">Trending</target> 8514 <source>Trending</source><target state="new">Trending</target>
8462 8515
@@ -8480,38 +8533,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8480 <source>Incorrect username or password.</source> 8533 <source>Incorrect username or password.</source>
8481 <target state="new">Incorrect username or password.</target> 8534 <target state="new">Incorrect username or password.</target>
8482 8535
8483 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 8536 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8484 <trans-unit id="6974874606619467663" datatype="html"> 8537 <trans-unit id="6974874606619467663" datatype="html">
8485 <source>Your account is blocked.</source> 8538 <source>Your account is blocked.</source>
8486 <target state="new">Your account is blocked.</target> 8539 <target state="new">Your account is blocked.</target>
8487 8540
8488 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 8541 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8489 <trans-unit id="7939914198003891823" datatype="html"> 8542 <trans-unit id="7939914198003891823" datatype="html">
8490 <source>any language</source> 8543 <source>any language</source>
8491 <target state="new">any language</target> 8544 <target state="new">any language</target>
8492 8545
8493 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8546 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8494 8547
8495 <trans-unit id="5633144232269377096" datatype="html"> 8548 <trans-unit id="5633144232269377096" datatype="html">
8496 <source>hide</source> 8549 <source>hide</source>
8497 <target state="new">hide</target> 8550 <target state="new">hide</target>
8498 8551
8499 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8552 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8500 <trans-unit id="8603861867909474404" datatype="html"> 8553 <trans-unit id="8603861867909474404" datatype="html">
8501 <source>blur</source> 8554 <source>blur</source>
8502 <target state="new">blur</target> 8555 <target state="new">blur</target>
8503 8556
8504 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8557 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8505 <trans-unit id="4534458451100881847" datatype="html"> 8558 <trans-unit id="4534458451100881847" datatype="html">
8506 <source>display</source> 8559 <source>display</source>
8507 <target state="new">display</target> 8560 <target state="new">display</target>
8508 8561
8509 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8562 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8510 <trans-unit id="4467323362722952678" datatype="html"> 8563 <trans-unit id="4467323362722952678" datatype="html">
8511 <source>Unknown</source> 8564 <source>Unknown</source>
8512 <target state="new">Unknown</target> 8565 <target state="new">Unknown</target>
8513 8566
8514 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8567 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8515 <trans-unit id="8781423666414310853" datatype="html"> 8568 <trans-unit id="8781423666414310853" datatype="html">
8516 <source>Your password has been successfully reset!</source> 8569 <source>Your password has been successfully reset!</source>
8517 <target state="new">Your password has been successfully reset!</target> 8570 <target state="new">Your password has been successfully reset!</target>
@@ -10097,18 +10150,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10097 <target state="new">Too many attempts, please try again after 10150 <target state="new">Too many attempts, please try again after
10098 <x id="PH"/> minutes. 10151 <x id="PH"/> minutes.
10099 </target> 10152 </target>
10100 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 10153
10101 </trans-unit> 10154 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
10102 <trans-unit id="4965472196059235310" datatype="html"> 10155 <trans-unit id="4965472196059235310" datatype="html">
10103 <source>Too many attempts, please try again later.</source> 10156 <source>Too many attempts, please try again later.</source>
10104 <target state="new">Too many attempts, please try again later.</target> 10157 <target state="new">Too many attempts, please try again later.</target>
10105 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 10158
10106 </trans-unit> 10159 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
10107 <trans-unit id="1693549688987384699" datatype="html"> 10160 <trans-unit id="1693549688987384699" datatype="html">
10108 <source>Server error. Please retry later.</source> 10161 <source>Server error. Please retry later.</source>
10109 <target state="new">Server error. Please retry later.</target> 10162 <target state="new">Server error. Please retry later.</target>
10110 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 10163
10111 </trans-unit> 10164 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
10112 <trans-unit id="5927402622550505067" datatype="html"> 10165 <trans-unit id="5927402622550505067" datatype="html">
10113 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 10166 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
10114 <target state="new">Subscribed to all current channels of 10167 <target state="new">Subscribed to all current channels of
@@ -10702,35 +10755,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10702 <source>Your video was uploaded to your account and is private.</source> 10755 <source>Your video was uploaded to your account and is private.</source>
10703 <target state="new">Your video was uploaded to your account and is private.</target> 10756 <target state="new">Your video was uploaded to your account and is private.</target>
10704 10757
10705 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10758 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10706 <trans-unit id="5699822024600815733" datatype="html"> 10759 <trans-unit id="5699822024600815733" datatype="html">
10707 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10760 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10708 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target> 10761 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target>
10709 10762
10710 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10763 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10711 <trans-unit id="1219739004043110649" datatype="html"> 10764 <trans-unit id="1219739004043110649" datatype="html">
10712 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10765 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10713 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target> 10766 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target>
10714 10767
10715 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10768 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10716 <trans-unit id="6932865105766151309" datatype="html"> 10769 <trans-unit id="6932865105766151309" datatype="html">
10717 <source>Upload</source> 10770 <source>Upload</source>
10718 <target state="new">Upload</target> 10771 <target state="new">Upload</target>
10719 10772
10720 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10773 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10721 <trans-unit id="8278735427925094503" datatype="html"> 10774 <trans-unit id="8278735427925094503" datatype="html">
10722 <source>Upload <x id="PH"/> </source> 10775 <source>Upload <x id="PH"/> </source>
10723 <target state="new">Upload 10776 <target state="new">Upload
10724 <x id="PH"/> 10777 <x id="PH"/>
10725 </target> 10778 </target>
10726 10779
10727 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10780 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10728 10781
10729 <trans-unit id="5981816353437801748" datatype="html"> 10782 <trans-unit id="5981816353437801748" datatype="html">
10730 <source>Video published.</source> 10783 <source>Video published.</source>
10731 <target state="new">Video published.</target> 10784 <target state="new">Video published.</target>
10732 10785
10733 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10786 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10734 10787
10735 10788
10736 <trans-unit id="764164089183618119" datatype="html"> 10789 <trans-unit id="764164089183618119" datatype="html">
@@ -10780,27 +10833,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10780 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10833 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10781 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10834 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10782 10835
10783 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10836 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10784 <trans-unit id="5761611056224181752" datatype="html"> 10837 <trans-unit id="5761611056224181752" datatype="html">
10785 <source>Redirection</source> 10838 <source>Redirection</source>
10786 <target state="new">Redirection</target> 10839 <target state="new">Redirection</target>
10787 10840
10788 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10841 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10789 <trans-unit id="8858527736400081688" datatype="html"> 10842 <trans-unit id="8858527736400081688" datatype="html">
10790 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10843 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10791 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target> 10844 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target>
10792 10845
10793 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10846 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10794 <trans-unit id="3937119019020041049" datatype="html"> 10847 <trans-unit id="3937119019020041049" datatype="html">
10795 <source>Mature or explicit content</source> 10848 <source>Mature or explicit content</source>
10796 <target state="new">Mature or explicit content</target> 10849 <target state="new">Mature or explicit content</target>
10797 10850
10798 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10851 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10799 <trans-unit id="1755474755114288376" datatype="html"> 10852 <trans-unit id="1755474755114288376" datatype="html">
10800 <source>Up Next</source> 10853 <source>Up Next</source>
10801 <target state="new">Up Next</target> 10854 <target state="new">Up Next</target>
10802 10855
10803 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10856 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10804 <trans-unit id="2159130950882492111" datatype="html"> 10857 <trans-unit id="2159130950882492111" datatype="html">
10805 <source>Cancel</source> 10858 <source>Cancel</source>
10806 <target state="new">Cancel</target> 10859 <target state="new">Cancel</target>
@@ -10810,62 +10863,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10810 <source>Autoplay is suspended</source> 10863 <source>Autoplay is suspended</source>
10811 <target state="new">Autoplay is suspended</target> 10864 <target state="new">Autoplay is suspended</target>
10812 10865
10813 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10866 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10814 <trans-unit id="7895294730547405228" datatype="html"> 10867 <trans-unit id="7895294730547405228" datatype="html">
10815 <source>Enter/exit fullscreen (requires player focus)</source> 10868 <source>Enter/exit fullscreen (requires player focus)</source>
10816 <target state="new">Enter/exit fullscreen (requires player focus)</target> 10869 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10817 10870
10818 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10871 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10819 <trans-unit id="7618388257165864759" datatype="html"> 10872 <trans-unit id="7618388257165864759" datatype="html">
10820 <source>Play/Pause the video (requires player focus)</source> 10873 <source>Play/Pause the video (requires player focus)</source>
10821 <target state="new">Play/Pause the video (requires player focus)</target> 10874 <target state="new">Play/Pause the video (requires player focus)</target>
10822 10875
10823 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10876 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10824 <trans-unit id="7761890399634216630" datatype="html"> 10877 <trans-unit id="7761890399634216630" datatype="html">
10825 <source>Mute/unmute the video (requires player focus)</source> 10878 <source>Mute/unmute the video (requires player focus)</source>
10826 <target state="new">Mute/unmute the video (requires player focus)</target> 10879 <target state="new">Mute/unmute the video (requires player focus)</target>
10827 10880
10828 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10881 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10829 <trans-unit id="5996585232248234904" datatype="html"> 10882 <trans-unit id="5996585232248234904" datatype="html">
10830 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10883 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10831 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10884 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10832 10885
10833 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10886 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10834 <trans-unit id="3748765405903319998" datatype="html"> 10887 <trans-unit id="3748765405903319998" datatype="html">
10835 <source>Increase the volume (requires player focus)</source> 10888 <source>Increase the volume (requires player focus)</source>
10836 <target state="new">Increase the volume (requires player focus)</target> 10889 <target state="new">Increase the volume (requires player focus)</target>
10837 10890
10838 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10891 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10839 <trans-unit id="5810704036407159982" datatype="html"> 10892 <trans-unit id="5810704036407159982" datatype="html">
10840 <source>Decrease the volume (requires player focus)</source> 10893 <source>Decrease the volume (requires player focus)</source>
10841 <target state="new">Decrease the volume (requires player focus)</target> 10894 <target state="new">Decrease the volume (requires player focus)</target>
10842 10895
10843 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10896 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10844 <trans-unit id="2622048822548065691" datatype="html"> 10897 <trans-unit id="2622048822548065691" datatype="html">
10845 <source>Seek the video forward (requires player focus)</source> 10898 <source>Seek the video forward (requires player focus)</source>
10846 <target state="new">Seek the video forward (requires player focus)</target> 10899 <target state="new">Seek the video forward (requires player focus)</target>
10847 10900
10848 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10901 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10849 <trans-unit id="6540078205109221153" datatype="html"> 10902 <trans-unit id="6540078205109221153" datatype="html">
10850 <source>Seek the video backward (requires player focus)</source> 10903 <source>Seek the video backward (requires player focus)</source>
10851 <target state="new">Seek the video backward (requires player focus)</target> 10904 <target state="new">Seek the video backward (requires player focus)</target>
10852 10905
10853 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10906 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10854 <trans-unit id="1956491957766210808" datatype="html"> 10907 <trans-unit id="1956491957766210808" datatype="html">
10855 <source>Increase playback rate (requires player focus)</source> 10908 <source>Increase playback rate (requires player focus)</source>
10856 <target state="new">Increase playback rate (requires player focus)</target> 10909 <target state="new">Increase playback rate (requires player focus)</target>
10857 10910
10858 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10911 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10859 <trans-unit id="5495529997674803186" datatype="html"> 10912 <trans-unit id="5495529997674803186" datatype="html">
10860 <source>Decrease playback rate (requires player focus)</source> 10913 <source>Decrease playback rate (requires player focus)</source>
10861 <target state="new">Decrease playback rate (requires player focus)</target> 10914 <target state="new">Decrease playback rate (requires player focus)</target>
10862 10915
10863 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10916 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10864 <trans-unit id="3178343147230721210" datatype="html"> 10917 <trans-unit id="3178343147230721210" datatype="html">
10865 <source>Navigate in the video frame by frame (requires player focus)</source> 10918 <source>Navigate in the video frame by frame (requires player focus)</source>
10866 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 10919 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10867 10920
10868 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10921 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10869 <trans-unit id="8025996572234182184" datatype="html"> 10922 <trans-unit id="8025996572234182184" datatype="html">
10870 <source>Like the video</source> 10923 <source>Like the video</source>
10871 <target state="new">Like the video</target> 10924 <target state="new">Like the video</target>
diff --git a/client/src/locale/angular.lt-LT.xlf b/client/src/locale/angular.lt-LT.xlf
index 6c94a9e35..1c1a2860b 100644
--- a/client/src/locale/angular.lt-LT.xlf
+++ b/client/src/locale/angular.lt-LT.xlf
@@ -293,7 +293,7 @@
293 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 293 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
294 </target> 294 </target>
295 295
296 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html"> 296 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html">
297 <source>My watch history</source><target state="new">My watch history</target> 297 <source>My watch history</source><target state="new">My watch history</target>
298 298
299 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 299 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
@@ -374,12 +374,12 @@
374 374
375 375
376 376
377 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 377 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
378 <trans-unit id="1006562256968398209" datatype="html"> 378 <trans-unit id="1006562256968398209" datatype="html">
379 <source>video</source> 379 <source>video</source>
380 <target state="new">video</target> 380 <target state="new">video</target>
381 381
382 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 382 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
383 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 383 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
384 384
385 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 385 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -441,10 +441,10 @@
441 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 441 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
442 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 442 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
443 443
444 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 444 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
445 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 445 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
446 446
447 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html"> 447 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html">
448 <source>subtitles</source><target state="new">subtitles</target> 448 <source>subtitles</source><target state="new">subtitles</target>
449 449
450 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 450 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
@@ -827,7 +827,7 @@
827 827
828 828
829 829
830 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group></trans-unit> 830 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
831 <trans-unit id="1502595455339510144" datatype="html"> 831 <trans-unit id="1502595455339510144" datatype="html">
832 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 832 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
833 <target state="new"> 833 <target state="new">
@@ -910,7 +910,31 @@
910 <source>Federation</source> 910 <source>Federation</source>
911 <target state="new">Federation</target> 911 <target state="new">Federation</target>
912 912
913 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 913 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
914 <source>Following</source><target state="new">Following</target>
915 <context-group purpose="location">
916 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
917 <context context-type="linenumber">29</context>
918 </context-group>
919 <context-group purpose="location">
920 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
921 <context context-type="linenumber">31</context>
922 </context-group>
923 <context-group purpose="location">
924 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
925 <context context-type="linenumber">28</context>
926 </context-group>
927 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
928 <source>Followers</source><target state="new">Followers</target>
929 <context-group purpose="location">
930 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
931 <context context-type="linenumber">34</context>
932 </context-group>
933 <context-group purpose="location">
934 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
935 <context context-type="linenumber">37</context>
936 </context-group>
937 </trans-unit>
914 <trans-unit id="3541687134897970106" datatype="html"> 938 <trans-unit id="3541687134897970106" datatype="html">
915 <source>followers</source> 939 <source>followers</source>
916 <target state="new">followers</target> 940 <target state="new">followers</target>
@@ -972,7 +996,7 @@
972 996
973 997
974 998
975 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 999 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
976 <trans-unit id="3616223838716839702" datatype="html"> 1000 <trans-unit id="3616223838716839702" datatype="html">
977 <source>Ban this user</source> 1001 <source>Ban this user</source>
978 <target state="new">Ban this user</target> 1002 <target state="new">Ban this user</target>
@@ -1038,17 +1062,11 @@
1038 1062
1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html"> 1063 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html">
1040 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1064 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1041 <context-group purpose="location"> 1065
1042 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1066 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7215649348148521605" datatype="html">
1043 <context context-type="linenumber">60,62</context>
1044 </context-group>
1045 </trans-unit><trans-unit id="7215649348148521605" datatype="html">
1046 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1067 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1047 <context-group purpose="location"> 1068
1048 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1069 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1049 <context context-type="linenumber">65,67</context>
1050 </context-group>
1051 </trans-unit>
1052 <trans-unit id="2392488717875840729" datatype="html"> 1070 <trans-unit id="2392488717875840729" datatype="html">
1053 <source>User</source> 1071 <source>User</source>
1054 <target state="new">User</target> 1072 <target state="new">User</target>
@@ -1059,7 +1077,13 @@
1059 <source>Username or email address</source> 1077 <source>Username or email address</source>
1060 <target state="new">Username or email address</target> 1078 <target state="new">Username or email address</target>
1061 1079
1062 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit> 1080 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="1758058452376026925" datatype="html">
1081 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1082 <context-group purpose="location">
1083 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1084 <context context-type="linenumber">33,34</context>
1085 </context-group>
1086 </trans-unit>
1063 1087
1064 <trans-unit id="1431416938026210429" datatype="html"> 1088 <trans-unit id="1431416938026210429" datatype="html">
1065 <source>Password</source> 1089 <source>Password</source>
@@ -1072,50 +1096,44 @@
1072 1096
1073 1097
1074 1098
1075 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group></trans-unit> 1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1076 <trans-unit id="8715156686857791956" datatype="html"> 1100 <trans-unit id="8715156686857791956" datatype="html">
1077 <source>Click here to reset your password</source> 1101 <source>Click here to reset your password</source>
1078 <target state="new">Click here to reset your password</target> 1102 <target state="new">Click here to reset your password</target>
1079 1103
1080 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html"> 1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html">
1081 <source>I forgot my password</source><target state="new">I forgot my password</target> 1105 <source>I forgot my password</source><target state="new">I forgot my password</target>
1082 <context-group purpose="location"> 1106
1083 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1107 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="2101170466365500913" datatype="html">
1084 <context context-type="linenumber">47</context>
1085 </context-group>
1086 </trans-unit><trans-unit id="2101170466365500913" datatype="html">
1087 <source> Logging into an account lets you publish content </source><target state="new"> Logging into an account lets you publish content </target> 1108 <source> Logging into an account lets you publish content </source><target state="new"> Logging into an account lets you publish content </target>
1088 <context-group purpose="location"> 1109
1089 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1110 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1090 <context context-type="linenumber">56,57</context>
1091 </context-group>
1092 </trans-unit>
1093 <trans-unit id="2454050363478003966" datatype="html"> 1111 <trans-unit id="2454050363478003966" datatype="html">
1094 <source>Login</source> 1112 <source>Login</source>
1095 <target state="new">Login</target> 1113 <target state="new">Login</target>
1096 1114
1097 1115
1098 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1116 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1099 <trans-unit id="3183213940445113677" datatype="html"> 1117 <trans-unit id="3183213940445113677" datatype="html">
1100 <source>Or sign in with</source> 1118 <source>Or sign in with</source>
1101 <target state="new">Or sign in with</target> 1119 <target state="new">Or sign in with</target>
1102 1120
1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit> 1121 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1104 <trans-unit id="3238209155172574367" datatype="html"> 1122 <trans-unit id="3238209155172574367" datatype="html">
1105 <source>Forgot your password</source> 1123 <source>Forgot your password</source>
1106 <target state="new">Forgot your password</target> 1124 <target state="new">Forgot your password</target>
1107 1125
1108 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group></trans-unit> 1126 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1109 <trans-unit id="87327320394367488" datatype="html"> 1127 <trans-unit id="87327320394367488" datatype="html">
1110 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1128 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1111 <target state="new"> 1129 <target state="new">
1112 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1130 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
1113 </target> 1131 </target>
1114 1132
1115 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html"> 1133 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html">
1116 <source> Enter your email address and we will send you a link to reset your password. </source><target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1134 <source> Enter your email address and we will send you a link to reset your password. </source><target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1117 1135
1118 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html"> 1136 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html">
1119 <source>An email with the reset password instructions will be sent to <x id="PH"/>. 1137 <source>An email with the reset password instructions will be sent to <x id="PH"/>.
1120The link will expire within 1 hour.</source><target state="new">An email with the reset password instructions will be sent to <x id="PH"/>. 1138The link will expire within 1 hour.</source><target state="new">An email with the reset password instructions will be sent to <x id="PH"/>.
1121The link will expire within 1 hour.</target> 1139The link will expire within 1 hour.</target>
@@ -1131,17 +1149,17 @@ The link will expire within 1 hour.</target>
1131 1149
1132 1150
1133 1151
1134 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group></trans-unit> 1152 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1135 <trans-unit id="3967269098753656610" datatype="html"> 1153 <trans-unit id="3967269098753656610" datatype="html">
1136 <source>Email address</source> 1154 <source>Email address</source>
1137 <target state="new">Email address</target> 1155 <target state="new">Email address</target>
1138 1156
1139 1157
1140 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html"> 1158 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html">
1141 <source>Reset</source><target state="new">Reset</target> 1159 <source>Reset</source><target state="new">Reset</target>
1142 1160
1143 <note priority="1" from="description">Password reset button</note> 1161 <note priority="1" from="description">Password reset button</note>
1144 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group></trans-unit> 1162 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1145 1163
1146 1164
1147 <trans-unit id="4319634264526091601" datatype="html"> 1165 <trans-unit id="4319634264526091601" datatype="html">
@@ -1503,7 +1521,7 @@ The link will expire within 1 hour.</target>
1503 <source>Create an account</source> 1521 <source>Create an account</source>
1504 <target state="new">Create an account</target> 1522 <target state="new">Create an account</target>
1505 1523
1506 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1524 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1507 1525
1508 <trans-unit id="3058024914967508975" datatype="html"> 1526 <trans-unit id="3058024914967508975" datatype="html">
1509 <source>My videos</source><target state="new">My videos</target> 1527 <source>My videos</source><target state="new">My videos</target>
@@ -1549,7 +1567,7 @@ The link will expire within 1 hour.</target>
1549 <target state="new">VIDEOS</target> 1567 <target state="new">VIDEOS</target>
1550 1568
1551 1569
1552 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html"> 1570 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html">
1553 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target> 1571 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target>
1554 1572
1555 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html"> 1573 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html">
@@ -1617,7 +1635,7 @@ The link will expire within 1 hour.</target>
1617 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html"> 1635 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html">
1618 <source>I'm a teapot</source><target state="new">I'm a teapot</target> 1636 <source>I'm a teapot</source><target state="new">I'm a teapot</target>
1619 1637
1620 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html"> 1638 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html">
1621 <source>That's an error.</source><target state="new">That's an error.</target> 1639 <source>That's an error.</source><target state="new">That's an error.</target>
1622 <context-group purpose="location"> 1640 <context-group purpose="location">
1623 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context> 1641 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context>
@@ -1683,7 +1701,7 @@ The link will expire within 1 hour.</target>
1683 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html"> 1701 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html">
1684 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1702 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1685 1703
1686 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group></trans-unit> 1704 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1687 1705
1688 <trans-unit id="5131854469652959713" datatype="html"> 1706 <trans-unit id="5131854469652959713" datatype="html">
1689 <source>GLOBAL SEARCH</source> 1707 <source>GLOBAL SEARCH</source>
@@ -2377,7 +2395,7 @@ The link will expire within 1 hour.</target>
2377 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2395 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2378 <source>Upload on hold</source><target state="new">Upload on hold</target> 2396 <source>Upload on hold</source><target state="new">Upload on hold</target>
2379 2397
2380 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2398 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2381 <trans-unit id="285180972645018518" datatype="html"> 2399 <trans-unit id="285180972645018518" datatype="html">
2382 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2400 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2383 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2401 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3031,11 +3049,7 @@ The link will expire within 1 hour.</target>
3031 <target state="new">ID</target> 3049 <target state="new">ID</target>
3032 3050
3033 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit> 3051 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit>
3034 <trans-unit id="2265605798180116441" datatype="html"> 3052
3035 <source>Follower handle</source>
3036 <target state="new">Follower handle</target>
3037
3038 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
3039 <trans-unit id="5911214550882917183" datatype="html"> 3053 <trans-unit id="5911214550882917183" datatype="html">
3040 <source>State</source> 3054 <source>State</source>
3041 <target state="new">State</target> 3055 <target state="new">State</target>
@@ -3117,11 +3131,7 @@ The link will expire within 1 hour.</target>
3117 </target> 3131 </target>
3118 3132
3119 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit> 3133 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
3120 <trans-unit id="6641024648411549335" datatype="html"> 3134
3121 <source>Host</source>
3122 <target state="new">Host</target>
3123
3124 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3125 <trans-unit id="6571718060636962350" datatype="html"> 3135 <trans-unit id="6571718060636962350" datatype="html">
3126 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3136 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3127 <target state="new">Redundancy allowed 3137 <target state="new">Redundancy allowed
@@ -3132,7 +3142,7 @@ The link will expire within 1 hour.</target>
3132 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html"> 3142 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html">
3133 <source>Unfollow</source><target state="new">Unfollow</target> 3143 <source>Unfollow</source><target state="new">Unfollow</target>
3134 3144
3135 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3145 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3136 <trans-unit id="8246779176913476983" datatype="html"> 3146 <trans-unit id="8246779176913476983" datatype="html">
3137 <source>Open instance in a new tab</source> 3147 <source>Open instance in a new tab</source>
3138 <target state="new">Open instance in a new tab</target> 3148 <target state="new">Open instance in a new tab</target>
@@ -3144,12 +3154,12 @@ The link will expire within 1 hour.</target>
3144 <source>No host found matching current filters.</source> 3154 <source>No host found matching current filters.</source>
3145 <target state="new">No host found matching current filters.</target> 3155 <target state="new">No host found matching current filters.</target>
3146 3156
3147 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3157 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3148 <trans-unit id="7274241885665071790" datatype="html"> 3158 <trans-unit id="7274241885665071790" datatype="html">
3149 <source>Your instance is not following anyone.</source> 3159 <source>Your instance is not following anyone.</source>
3150 <target state="new">Your instance is not following anyone.</target> 3160 <target state="new">Your instance is not following anyone.</target>
3151 3161
3152 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3162 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3153 <trans-unit id="4774348799569692380" datatype="html"> 3163 <trans-unit id="4774348799569692380" datatype="html">
3154 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3164 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3155 <target state="new">Showing 3165 <target state="new">Showing
@@ -3159,14 +3169,7 @@ The link will expire within 1 hour.</target>
3159 </target> 3169 </target>
3160 3170
3161 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3171 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3162 <trans-unit id="6275803119759621687" datatype="html"> 3172 <trans-unit id="9216117865911519658" datatype="html">
3163 <source>Follow domains</source>
3164 <target state="new">Follow domains</target>
3165
3166 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit><trans-unit id="1268699198448750610" datatype="html">
3167 <source>Follow instances</source><target state="new">Follow instances</target>
3168
3169 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3170 <source>Action</source><target state="new">Action</target> 3173 <source>Action</source><target state="new">Action</target>
3171 3174
3172 3175
@@ -3216,7 +3219,7 @@ The link will expire within 1 hour.</target>
3216 3219
3217 3220
3218 3221
3219 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html"> 3222 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html">
3220 <source>e.g. jane_doe</source><target state="new">e.g. jane_doe</target> 3223 <source>e.g. jane_doe</source><target state="new">e.g. jane_doe</target>
3221 3224
3222 <note priority="1" from="description">Username choice placeholder in the registration form</note> 3225 <note priority="1" from="description">Username choice placeholder in the registration form</note>
@@ -3248,7 +3251,7 @@ The link will expire within 1 hour.</target>
3248 <target state="new">Role</target> 3251 <target state="new">Role</target>
3249 3252
3250 3253
3251 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group></trans-unit> 3254 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3252 <trans-unit id="7046347992315328430" datatype="html"> 3255 <trans-unit id="7046347992315328430" datatype="html">
3253 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3256 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3254 <target state="new"> 3257 <target state="new">
@@ -3271,15 +3274,9 @@ The link will expire within 1 hour.</target>
3271 3274
3272 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html"> 3275 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html">
3273 <source>Auth plugin</source><target state="new">Auth plugin</target> 3276 <source>Auth plugin</source><target state="new">Auth plugin</target>
3274 <context-group purpose="location"> 3277
3275 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3278
3276 <context context-type="linenumber">188</context> 3279 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit><trans-unit id="588099657508661970" datatype="html">
3277 </context-group>
3278 <context-group purpose="location">
3279 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3280 <context context-type="linenumber">188</context>
3281 </context-group>
3282 </trans-unit><trans-unit id="588099657508661970" datatype="html">
3283 <source>None (local authentication)</source><target state="new">None (local authentication)</target> 3280 <source>None (local authentication)</source><target state="new">None (local authentication)</target>
3284 <context-group purpose="location"> 3281 <context-group purpose="location">
3285 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3282 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
@@ -3504,7 +3501,13 @@ The link will expire within 1 hour.</target>
3504 3501
3505 3502
3506 3503
3507 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="4691552465058437520" datatype="html"> 3504 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3505 <source>Follower</source><target state="new">Follower</target>
3506 <context-group purpose="location">
3507 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3508 <context context-type="linenumber">24</context>
3509 </context-group>
3510 </trans-unit><trans-unit id="4691552465058437520" datatype="html">
3508 <source>Commented video</source><target state="new">Commented video</target> 3511 <source>Commented video</source><target state="new">Commented video</target>
3509 3512
3510 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html"> 3513 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html">
@@ -3826,7 +3829,7 @@ The link will expire within 1 hour.</target>
3826 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 3829 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
3827 </target> 3830 </target>
3828 3831
3829 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3832 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3830 <trans-unit id="4058814854824495833" datatype="html"> 3833 <trans-unit id="4058814854824495833" datatype="html">
3831 <source>Mute domains</source> 3834 <source>Mute domains</source>
3832 <target state="new">Mute domains</target> 3835 <target state="new">Mute domains</target>
@@ -5631,11 +5634,8 @@ color: red;
5631 5634
5632 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5635 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5633 <source>CHANNELS</source><target state="new">CHANNELS</target> 5636 <source>CHANNELS</source><target state="new">CHANNELS</target>
5634 <context-group purpose="location"> 5637
5635 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5638 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5636 <context context-type="linenumber">82</context>
5637 </context-group>
5638 </trans-unit>
5639 5639
5640 <trans-unit id="3666829335406793239" datatype="html"> 5640 <trans-unit id="3666829335406793239" datatype="html">
5641 <source>This account does not have channels.</source> 5641 <source>This account does not have channels.</source>
@@ -5673,7 +5673,13 @@ channel with the same name (<x id="PH_2"/>)!</source><target state="new">Do you
5673It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 5673It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
5674channel with the same name (<x id="PH_2"/>)!</target> 5674channel with the same name (<x id="PH_2"/>)!</target>
5675 5675
5676 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5676 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5677 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5678 <context-group purpose="location">
5679 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5680 <context context-type="linenumber">48</context>
5681 </context-group>
5682 </trans-unit>
5677 <trans-unit id="5387007581996837469" datatype="html"> 5683 <trans-unit id="5387007581996837469" datatype="html">
5678 <source>My Channels</source> 5684 <source>My Channels</source>
5679 <target state="new">My Channels</target> 5685 <target state="new">My Channels</target>
@@ -6263,12 +6269,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6263 <source>Your message has been sent.</source> 6269 <source>Your message has been sent.</source>
6264 <target state="new">Your message has been sent.</target> 6270 <target state="new">Your message has been sent.</target>
6265 6271
6266 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6272 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6267 <trans-unit id="2072135752262464360" datatype="html"> 6273 <trans-unit id="2072135752262464360" datatype="html">
6268 <source>You already sent this form recently</source> 6274 <source>You already sent this form recently</source>
6269 <target state="new">You already sent this form recently</target> 6275 <target state="new">You already sent this form recently</target>
6270 6276
6271 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6277 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6272 <trans-unit id="819067926858619041" datatype="html"> 6278 <trans-unit id="819067926858619041" datatype="html">
6273 <source>Account videos</source><target state="new">Account videos</target> 6279 <source>Account videos</source><target state="new">Account videos</target>
6274 6280
@@ -6303,10 +6309,10 @@ channel with the same name (<x id="PH_2"/>)!</target>
6303 <x id="PH"/> direct account followers 6309 <x id="PH"/> direct account followers
6304 </target> 6310 </target>
6305 6311
6306 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html"> 6312 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html">
6307 <source>Report this account</source><target state="new">Report this account</target> 6313 <source>Report this account</source><target state="new">Report this account</target>
6308 6314
6309 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6315 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6310 6316
6311 <trans-unit id="1504521795586863905" datatype="html"> 6317 <trans-unit id="1504521795586863905" datatype="html">
6312 <source>VIDEOS</source><target state="new">VIDEOS</target> 6318 <source>VIDEOS</source><target state="new">VIDEOS</target>
@@ -6318,24 +6324,16 @@ channel with the same name (<x id="PH_2"/>)!</target>
6318 <target state="new">Username copied</target> 6324 <target state="new">Username copied</target>
6319 6325
6320 6326
6321 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html"> 6327 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html">
6322 <source>1 subscriber</source><target state="new">1 subscriber</target> 6328 <source>1 subscriber</source><target state="new">1 subscriber</target>
6323 6329
6324 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html"> 6330 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html">
6325 <source><x id="PH"/> subscribers</source><target state="new"><x id="PH"/> subscribers</target> 6331 <source><x id="PH"/> subscribers</source><target state="new"><x id="PH"/> subscribers</target>
6326 6332
6327 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6333 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6334
6335
6328 6336
6329 <trans-unit id="4682675125751819107" datatype="html">
6330 <source>Instances you follow</source>
6331 <target state="new">Instances you follow</target>
6332
6333 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6334 <trans-unit id="8899833753704589712" datatype="html">
6335 <source>Instances following you</source>
6336 <target state="new">Instances following you</target>
6337
6338 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6339 <trans-unit id="1035838766454786107" datatype="html"> 6337 <trans-unit id="1035838766454786107" datatype="html">
6340 <source>Audio-only</source> 6338 <source>Audio-only</source>
6341 <target state="new">Audio-only</target> 6339 <target state="new">Audio-only</target>
@@ -6383,7 +6381,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6383 <source>Auto (via ffmpeg)</source> 6381 <source>Auto (via ffmpeg)</source>
6384 <target state="new">Auto (via ffmpeg)</target> 6382 <target state="new">Auto (via ffmpeg)</target>
6385 6383
6386 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="931255636742351800" datatype="html"> 6384 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="3642770981085338761" datatype="html">
6385 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6386 <context-group purpose="location">
6387 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6388 <context context-type="linenumber">3</context>
6389 </context-group>
6390 </trans-unit><trans-unit id="931255636742351800" datatype="html">
6387 <source>No limit</source><target state="new">No limit</target> 6391 <source>No limit</source><target state="new">No limit</target>
6388 6392
6389 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html"> 6393 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html">
@@ -6502,17 +6506,33 @@ channel with the same name (<x id="PH_2"/>)!</target>
6502 <source>Domain is required.</source> 6506 <source>Domain is required.</source>
6503 <target state="new">Domain is required.</target> 6507 <target state="new">Domain is required.</target>
6504 6508
6505 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group></trans-unit> 6509 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6506 <trans-unit id="6780793142903080663" datatype="html"> 6510 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6507 <source>Domains entered are invalid.</source> 6511 <context-group purpose="location">
6508 <target state="new">Domains entered are invalid.</target> 6512 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6509 6513 <context context-type="linenumber">93</context>
6510 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6514 </context-group>
6511 <trans-unit id="5886492514458202177" datatype="html"> 6515 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6512 <source>Domains entered contain duplicates.</source> 6516 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6513 <target state="new">Domains entered contain duplicates.</target> 6517 <context-group purpose="location">
6514 6518 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6515 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 6519 <context context-type="linenumber">94</context>
6520 </context-group>
6521 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6522 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6523 <context-group purpose="location">
6524 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6525 <context context-type="linenumber">102</context>
6526 </context-group>
6527 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6528 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6529 <context-group purpose="location">
6530 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6531 <context context-type="linenumber">103</context>
6532 </context-group>
6533 </trans-unit>
6534
6535
6516 <trans-unit id="240806681889331244" datatype="html"> 6536 <trans-unit id="240806681889331244" datatype="html">
6517 <source>Unlimited</source> 6537 <source>Unlimited</source>
6518 <target state="new">Unlimited</target> 6538 <target state="new">Unlimited</target>
@@ -6643,7 +6663,27 @@ channel with the same name (<x id="PH_2"/>)!</target>
6643 <x id="PH"/> removed from instance followers 6663 <x id="PH"/> removed from instance followers
6644 </target> 6664 </target>
6645 6665
6646 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit> 6666 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit><trans-unit id="6018246591673612412" datatype="html">
6667 <source>Follow</source><target state="new">Follow</target>
6668 <context-group purpose="location">
6669 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6670 <context context-type="linenumber">3</context>
6671 </context-group>
6672 <context-group purpose="location">
6673 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6674 <context context-type="linenumber">37</context>
6675 </context-group>
6676 <context-group purpose="location">
6677 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6678 <context context-type="linenumber">18</context>
6679 </context-group>
6680 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6681 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6682 <context-group purpose="location">
6683 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6684 <context context-type="linenumber">11</context>
6685 </context-group>
6686 </trans-unit>
6647 <trans-unit id="2740793005745065895" datatype="html"> 6687 <trans-unit id="2740793005745065895" datatype="html">
6648 <source> 6688 <source>
6649 <x id="PH"/> is not valid 6689 <x id="PH"/> is not valid
@@ -6652,19 +6692,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
6652 <x id="PH"/> is not valid 6692 <x id="PH"/> is not valid
6653 </target> 6693 </target>
6654 6694
6655 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group></trans-unit> 6695 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6656 <trans-unit id="2355066641781598196" datatype="html"> 6696 <trans-unit id="2355066641781598196" datatype="html">
6657 <source>Follow request(s) sent!</source> 6697 <source>Follow request(s) sent!</source>
6658 <target state="new">Follow request(s) sent!</target> 6698 <target state="new">Follow request(s) sent!</target>
6659 6699
6660 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit> 6700 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6701 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6702 <context-group purpose="location">
6703 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6704 <context context-type="linenumber">3</context>
6705 </context-group>
6706 </trans-unit>
6661 <trans-unit id="4245720728052819482" datatype="html"> 6707 <trans-unit id="4245720728052819482" datatype="html">
6662 <source>Do you really want to unfollow <x id="PH"/>?</source> 6708 <source>Do you really want to unfollow <x id="PH"/>?</source>
6663 <target state="new">Do you really want to unfollow 6709 <target state="new">Do you really want to unfollow
6664 <x id="PH"/>? 6710 <x id="PH"/>?
6665 </target> 6711 </target>
6666 6712
6667 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6713 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6668 <trans-unit id="9160510009013134726" datatype="html"> 6714 <trans-unit id="9160510009013134726" datatype="html">
6669 <source>Unfollow</source> 6715 <source>Unfollow</source>
6670 <target state="new">Unfollow</target> 6716 <target state="new">Unfollow</target>
@@ -6676,7 +6722,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
6676 <x id="PH"/> anymore. 6722 <x id="PH"/> anymore.
6677 </target> 6723 </target>
6678 6724
6679 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 6725 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6680 <trans-unit id="2593763089859685916" datatype="html"> 6726 <trans-unit id="2593763089859685916" datatype="html">
6681 <source>enabled</source> 6727 <source>enabled</source>
6682 <target state="new">enabled</target> 6728 <target state="new">enabled</target>
@@ -7134,7 +7180,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7134 7180
7135 7181
7136 7182
7137 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit> 7183 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7138 <trans-unit id="5076187961693950167" datatype="html"> 7184 <trans-unit id="5076187961693950167" datatype="html">
7139 <source>Standard logs</source> 7185 <source>Standard logs</source>
7140 <target state="new">Standard logs</target> 7186 <target state="new">Standard logs</target>
@@ -7172,13 +7218,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7172 <source>Update user password</source> 7218 <source>Update user password</source>
7173 <target state="new">Update user password</target> 7219 <target state="new">Update user password</target>
7174 7220
7175 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit><trans-unit id="177544274549739411" datatype="html"> 7221 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit>
7176 <source>Following list</source><target state="new">Following list</target>
7177
7178 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group></trans-unit><trans-unit id="8092429110007204784" datatype="html">
7179 <source>Followers list</source><target state="new">Followers list</target>
7180
7181 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group></trans-unit>
7182 <trans-unit id="780323526182667308" datatype="html"> 7222 <trans-unit id="780323526182667308" datatype="html">
7183 <source>User <x id="PH"/> updated.</source> 7223 <source>User <x id="PH"/> updated.</source>
7184 <target state="new">User 7224 <target state="new">User
@@ -7209,13 +7249,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7209 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html"> 7249 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html">
7210 <source>Federation</source><target state="new">Federation</target> 7250 <source>Federation</source><target state="new">Federation</target>
7211 7251
7212 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="4682675125751819107" datatype="html"> 7252 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit>
7213 <source>Instances you follow</source><target state="new">Instances you follow</target>
7214
7215 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group></trans-unit><trans-unit id="8899833753704589712" datatype="html">
7216 <source>Instances following you</source><target state="new">Instances following you</target>
7217
7218 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit>
7219 <trans-unit id="3767259920053407667" datatype="html"> 7253 <trans-unit id="3767259920053407667" datatype="html">
7220 <source>Videos will be deleted, comments will be tombstoned.</source> 7254 <source>Videos will be deleted, comments will be tombstoned.</source>
7221 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7255 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7243,7 +7277,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7243 <target state="new">Set Email as Verified</target> 7277 <target state="new">Set Email as Verified</target>
7244 7278
7245 7279
7246 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7280 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7281 <source>Created</source><target state="new">Created</target>
7282 <context-group purpose="location">
7283 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7284 <context context-type="linenumber">115</context>
7285 </context-group>
7286 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7287 <source>Daily quota</source><target state="new">Daily quota</target>
7288 <context-group purpose="location">
7289 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7290 <context context-type="linenumber">120</context>
7291 </context-group>
7292 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7293 <source>Last login</source><target state="new">Last login</target>
7294 <context-group purpose="location">
7295 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7296 <context context-type="linenumber">122</context>
7297 </context-group>
7298 </trans-unit>
7247 <trans-unit id="3403978719736970622" datatype="html"> 7299 <trans-unit id="3403978719736970622" datatype="html">
7248 <source>You cannot ban root.</source> 7300 <source>You cannot ban root.</source>
7249 <target state="new">You cannot ban root.</target> 7301 <target state="new">You cannot ban root.</target>
@@ -7548,12 +7600,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
7548 <x id="PH"/> created. 7600 <x id="PH"/> created.
7549 </target> 7601 </target>
7550 7602
7551 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7603 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7552 <trans-unit id="8723777130353305761" datatype="html"> 7604 <trans-unit id="8723777130353305761" datatype="html">
7553 <source>This name already exists on this instance.</source> 7605 <source>This name already exists on this instance.</source>
7554 <target state="new">This name already exists on this instance.</target> 7606 <target state="new">This name already exists on this instance.</target>
7555 7607
7556 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7608 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7557 <trans-unit id="7589345916094713536" datatype="html"> 7609 <trans-unit id="7589345916094713536" datatype="html">
7558 <source>Video channel <x id="PH"/> updated.</source> 7610 <source>Video channel <x id="PH"/> updated.</source>
7559 <target state="new">Video channel 7611 <target state="new">Video channel
@@ -7570,13 +7622,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7570 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 7622 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7571 7623
7572 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7624 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7573 <trans-unit id="2575302837003821736" datatype="html"> 7625
7574 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7575 <target state="new">Please type the display name of the video channel (
7576 <x id="PH"/>) to confirm
7577 </target>
7578
7579 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7580 <trans-unit id="624066830180032195" datatype="html"> 7626 <trans-unit id="624066830180032195" datatype="html">
7581 <source>Video channel <x id="PH"/> deleted.</source> 7627 <source>Video channel <x id="PH"/> deleted.</source>
7582 <target state="new">Video channel 7628 <target state="new">Video channel
@@ -7719,7 +7765,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
7719 <source>Ownership change request sent.</source> 7765 <source>Ownership change request sent.</source>
7720 <target state="new">Ownership change request sent.</target> 7766 <target state="new">Ownership change request sent.</target>
7721 7767
7722 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 7768 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7699622144571229146" datatype="html">
7769 <source>Sort by</source><target state="new">Sort by</target>
7770 <context-group purpose="location">
7771 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7772 <context context-type="linenumber">26</context>
7773 </context-group>
7774 </trans-unit>
7723 <trans-unit id="3245220240937722814" datatype="html"> 7775 <trans-unit id="3245220240937722814" datatype="html">
7724 <source>My channels</source> 7776 <source>My channels</source>
7725 <target state="new">My channels</target> 7777 <target state="new">My channels</target>
@@ -7814,7 +7866,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7814 <target state="new">Subscribe to the account</target> 7866 <target state="new">Subscribe to the account</target>
7815 7867
7816 7868
7817 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 7869 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
7818 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 7870 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
7819 <context-group purpose="location"> 7871 <context-group purpose="location">
7820 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 7872 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -7861,34 +7913,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
7861 <source>Go to my subscriptions</source> 7913 <source>Go to my subscriptions</source>
7862 <target state="new">Go to my subscriptions</target> 7914 <target state="new">Go to my subscriptions</target>
7863 7915
7864 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 7916 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
7865 <trans-unit id="1136469849928650779" datatype="html"> 7917 <trans-unit id="1136469849928650779" datatype="html">
7866 <source>Go to my videos</source> 7918 <source>Go to my videos</source>
7867 <target state="new">Go to my videos</target> 7919 <target state="new">Go to my videos</target>
7868 7920
7869 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit> 7921 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
7870 <trans-unit id="7836683738999600376" datatype="html"> 7922 <trans-unit id="7836683738999600376" datatype="html">
7871 <source>Go to my imports</source> 7923 <source>Go to my imports</source>
7872 <target state="new">Go to my imports</target> 7924 <target state="new">Go to my imports</target>
7873 7925
7874 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 7926 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
7875 <trans-unit id="7511292153332773503" datatype="html"> 7927 <trans-unit id="7511292153332773503" datatype="html">
7876 <source>Go to my channels</source> 7928 <source>Go to my channels</source>
7877 <target state="new">Go to my channels</target> 7929 <target state="new">Go to my channels</target>
7878 7930
7879 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html"> 7931 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html">
7880 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>. 7932 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7881Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 7933Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7882Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 7934Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
7883 7935
7884 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group></trans-unit> 7936 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
7885 7937
7886 7938
7887 <trans-unit id="375263728166936544" datatype="html"> 7939 <trans-unit id="375263728166936544" datatype="html">
7888 <source>You need to reconnect.</source> 7940 <source>You need to reconnect.</source>
7889 <target state="new">You need to reconnect.</target> 7941 <target state="new">You need to reconnect.</target>
7890 7942
7891 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group></trans-unit> 7943 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
7892 <trans-unit id="2206638022166154361" datatype="html"> 7944 <trans-unit id="2206638022166154361" datatype="html">
7893 <source>Keyboard Shortcuts:</source> 7945 <source>Keyboard Shortcuts:</source>
7894 <target state="new">Keyboard Shortcuts:</target> 7946 <target state="new">Keyboard Shortcuts:</target>
@@ -7899,6 +7951,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7899 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 7951 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7900 <context context-type="linenumber">98</context> 7952 <context context-type="linenumber">98</context>
7901 </context-group> 7953 </context-group>
7954 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
7955 <source>In my library</source><target state="new">In my library</target>
7956 <context-group purpose="location">
7957 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7958 <context context-type="linenumber">104</context>
7959 </context-group>
7902 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 7960 </trans-unit><trans-unit id="232050922346936574" datatype="html">
7903 <source>Trending</source><target state="new">Trending</target> 7961 <source>Trending</source><target state="new">Trending</target>
7904 7962
@@ -7922,38 +7980,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7922 <source>Incorrect username or password.</source> 7980 <source>Incorrect username or password.</source>
7923 <target state="new">Incorrect username or password.</target> 7981 <target state="new">Incorrect username or password.</target>
7924 7982
7925 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 7983 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
7926 <trans-unit id="6974874606619467663" datatype="html"> 7984 <trans-unit id="6974874606619467663" datatype="html">
7927 <source>Your account is blocked.</source> 7985 <source>Your account is blocked.</source>
7928 <target state="new">Your account is blocked.</target> 7986 <target state="new">Your account is blocked.</target>
7929 7987
7930 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 7988 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
7931 7989
7932 <trans-unit id="7939914198003891823" datatype="html"> 7990 <trans-unit id="7939914198003891823" datatype="html">
7933 <source>any language</source> 7991 <source>any language</source>
7934 <target state="new">any language</target> 7992 <target state="new">any language</target>
7935 7993
7936 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 7994 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
7937 <trans-unit id="5633144232269377096" datatype="html"> 7995 <trans-unit id="5633144232269377096" datatype="html">
7938 <source>hide</source> 7996 <source>hide</source>
7939 <target state="new">hide</target> 7997 <target state="new">hide</target>
7940 7998
7941 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 7999 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
7942 <trans-unit id="8603861867909474404" datatype="html"> 8000 <trans-unit id="8603861867909474404" datatype="html">
7943 <source>blur</source> 8001 <source>blur</source>
7944 <target state="new">blur</target> 8002 <target state="new">blur</target>
7945 8003
7946 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8004 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
7947 <trans-unit id="4534458451100881847" datatype="html"> 8005 <trans-unit id="4534458451100881847" datatype="html">
7948 <source>display</source> 8006 <source>display</source>
7949 <target state="new">display</target> 8007 <target state="new">display</target>
7950 8008
7951 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8009 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
7952 <trans-unit id="4467323362722952678" datatype="html"> 8010 <trans-unit id="4467323362722952678" datatype="html">
7953 <source>Unknown</source> 8011 <source>Unknown</source>
7954 <target state="new">Unknown</target> 8012 <target state="new">Unknown</target>
7955 8013
7956 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8014 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
7957 <trans-unit id="8781423666414310853" datatype="html"> 8015 <trans-unit id="8781423666414310853" datatype="html">
7958 <source>Your password has been successfully reset!</source> 8016 <source>Your password has been successfully reset!</source>
7959 <target state="new">Your password has been successfully reset!</target> 8017 <target state="new">Your password has been successfully reset!</target>
@@ -9497,17 +9555,17 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9497 <x id="PH"/> minutes. 9555 <x id="PH"/> minutes.
9498 </target> 9556 </target>
9499 9557
9500 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 9558 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9501 <trans-unit id="4965472196059235310" datatype="html"> 9559 <trans-unit id="4965472196059235310" datatype="html">
9502 <source>Too many attempts, please try again later.</source> 9560 <source>Too many attempts, please try again later.</source>
9503 <target state="new">Too many attempts, please try again later.</target> 9561 <target state="new">Too many attempts, please try again later.</target>
9504 9562
9505 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group></trans-unit> 9563 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9506 <trans-unit id="1693549688987384699" datatype="html"> 9564 <trans-unit id="1693549688987384699" datatype="html">
9507 <source>Server error. Please retry later.</source> 9565 <source>Server error. Please retry later.</source>
9508 <target state="new">Server error. Please retry later.</target> 9566 <target state="new">Server error. Please retry later.</target>
9509 9567
9510 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 9568 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9511 <trans-unit id="5927402622550505067" datatype="html"> 9569 <trans-unit id="5927402622550505067" datatype="html">
9512 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9570 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9513 <target state="new">Subscribed to all current channels of 9571 <target state="new">Subscribed to all current channels of
@@ -10005,20 +10063,20 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10005 <source>Your video was uploaded to your account and is private.</source> 10063 <source>Your video was uploaded to your account and is private.</source>
10006 <target state="new">Your video was uploaded to your account and is private.</target> 10064 <target state="new">Your video was uploaded to your account and is private.</target>
10007 10065
10008 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10066 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10009 <trans-unit id="5699822024600815733" datatype="html"> 10067 <trans-unit id="5699822024600815733" datatype="html">
10010 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10068 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10011 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target> 10069 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target>
10012 10070
10013 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10071 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10014 <trans-unit id="1219739004043110649" datatype="html"> 10072 <trans-unit id="1219739004043110649" datatype="html">
10015 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10073 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10016 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target> 10074 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target>
10017 10075
10018 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html"> 10076 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html">
10019 <source>Upload</source><target state="new">Upload</target> 10077 <source>Upload</source><target state="new">Upload</target>
10020 10078
10021 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10079 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10022 <trans-unit id="8278735427925094503" datatype="html"> 10080 <trans-unit id="8278735427925094503" datatype="html">
10023 <source>Upload 10081 <source>Upload
10024 <x id="PH"/> 10082 <x id="PH"/>
@@ -10027,13 +10085,13 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10027 <x id="PH"/> 10085 <x id="PH"/>
10028 </target> 10086 </target>
10029 10087
10030 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10088 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10031 10089
10032 <trans-unit id="5981816353437801748" datatype="html"> 10090 <trans-unit id="5981816353437801748" datatype="html">
10033 <source>Video published.</source> 10091 <source>Video published.</source>
10034 <target state="new">Video published.</target> 10092 <target state="new">Video published.</target>
10035 10093
10036 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10094 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10037 10095
10038 10096
10039 <trans-unit id="764164089183618119" datatype="html"> 10097 <trans-unit id="764164089183618119" datatype="html">
@@ -10096,26 +10154,26 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10096 <trans-unit id="961774488937452220" datatype="html"> 10154 <trans-unit id="961774488937452220" datatype="html">
10097 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10155 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10098 10156
10099 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html"> 10157 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html">
10100 <source>Redirection</source><target state="new">Redirection</target> 10158 <source>Redirection</source><target state="new">Redirection</target>
10101 10159
10102 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10160 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10103 10161
10104 <trans-unit id="8858527736400081688" datatype="html"> 10162 <trans-unit id="8858527736400081688" datatype="html">
10105 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10163 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10106 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target> 10164 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target>
10107 10165
10108 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10166 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10109 <trans-unit id="3937119019020041049" datatype="html"> 10167 <trans-unit id="3937119019020041049" datatype="html">
10110 <source>Mature or explicit content</source> 10168 <source>Mature or explicit content</source>
10111 <target state="new">Mature or explicit content</target> 10169 <target state="new">Mature or explicit content</target>
10112 10170
10113 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10171 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10114 <trans-unit id="1755474755114288376" datatype="html"> 10172 <trans-unit id="1755474755114288376" datatype="html">
10115 <source>Up Next</source> 10173 <source>Up Next</source>
10116 <target state="new">Up Next</target> 10174 <target state="new">Up Next</target>
10117 10175
10118 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html"> 10176 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html">
10119 <source>Cancel</source><target state="new">Cancel</target> 10177 <source>Cancel</source><target state="new">Cancel</target>
10120 10178
10121 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit> 10179 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit>
@@ -10123,62 +10181,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10123 <source>Autoplay is suspended</source> 10181 <source>Autoplay is suspended</source>
10124 <target state="new">Autoplay is suspended</target> 10182 <target state="new">Autoplay is suspended</target>
10125 10183
10126 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10184 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10127 <trans-unit id="7895294730547405228" datatype="html"> 10185 <trans-unit id="7895294730547405228" datatype="html">
10128 <source>Enter/exit fullscreen (requires player focus)</source> 10186 <source>Enter/exit fullscreen (requires player focus)</source>
10129 <target state="new">Enter/exit fullscreen (requires player focus)</target> 10187 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10130 10188
10131 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10189 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10132 <trans-unit id="7618388257165864759" datatype="html"> 10190 <trans-unit id="7618388257165864759" datatype="html">
10133 <source>Play/Pause the video (requires player focus)</source> 10191 <source>Play/Pause the video (requires player focus)</source>
10134 <target state="new">Play/Pause the video (requires player focus)</target> 10192 <target state="new">Play/Pause the video (requires player focus)</target>
10135 10193
10136 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10194 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10137 <trans-unit id="7761890399634216630" datatype="html"> 10195 <trans-unit id="7761890399634216630" datatype="html">
10138 <source>Mute/unmute the video (requires player focus)</source> 10196 <source>Mute/unmute the video (requires player focus)</source>
10139 <target state="new">Mute/unmute the video (requires player focus)</target> 10197 <target state="new">Mute/unmute the video (requires player focus)</target>
10140 10198
10141 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10199 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10142 <trans-unit id="5996585232248234904" datatype="html"> 10200 <trans-unit id="5996585232248234904" datatype="html">
10143 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10201 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10144 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10202 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10145 10203
10146 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10204 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10147 <trans-unit id="3748765405903319998" datatype="html"> 10205 <trans-unit id="3748765405903319998" datatype="html">
10148 <source>Increase the volume (requires player focus)</source> 10206 <source>Increase the volume (requires player focus)</source>
10149 <target state="new">Increase the volume (requires player focus)</target> 10207 <target state="new">Increase the volume (requires player focus)</target>
10150 10208
10151 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10209 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10152 <trans-unit id="5810704036407159982" datatype="html"> 10210 <trans-unit id="5810704036407159982" datatype="html">
10153 <source>Decrease the volume (requires player focus)</source> 10211 <source>Decrease the volume (requires player focus)</source>
10154 <target state="new">Decrease the volume (requires player focus)</target> 10212 <target state="new">Decrease the volume (requires player focus)</target>
10155 10213
10156 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10214 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10157 <trans-unit id="2622048822548065691" datatype="html"> 10215 <trans-unit id="2622048822548065691" datatype="html">
10158 <source>Seek the video forward (requires player focus)</source> 10216 <source>Seek the video forward (requires player focus)</source>
10159 <target state="new">Seek the video forward (requires player focus)</target> 10217 <target state="new">Seek the video forward (requires player focus)</target>
10160 10218
10161 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10219 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10162 <trans-unit id="6540078205109221153" datatype="html"> 10220 <trans-unit id="6540078205109221153" datatype="html">
10163 <source>Seek the video backward (requires player focus)</source> 10221 <source>Seek the video backward (requires player focus)</source>
10164 <target state="new">Seek the video backward (requires player focus)</target> 10222 <target state="new">Seek the video backward (requires player focus)</target>
10165 10223
10166 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10224 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10167 <trans-unit id="1956491957766210808" datatype="html"> 10225 <trans-unit id="1956491957766210808" datatype="html">
10168 <source>Increase playback rate (requires player focus)</source> 10226 <source>Increase playback rate (requires player focus)</source>
10169 <target state="new">Increase playback rate (requires player focus)</target> 10227 <target state="new">Increase playback rate (requires player focus)</target>
10170 10228
10171 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10229 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10172 <trans-unit id="5495529997674803186" datatype="html"> 10230 <trans-unit id="5495529997674803186" datatype="html">
10173 <source>Decrease playback rate (requires player focus)</source> 10231 <source>Decrease playback rate (requires player focus)</source>
10174 <target state="new">Decrease playback rate (requires player focus)</target> 10232 <target state="new">Decrease playback rate (requires player focus)</target>
10175 10233
10176 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10234 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10177 <trans-unit id="3178343147230721210" datatype="html"> 10235 <trans-unit id="3178343147230721210" datatype="html">
10178 <source>Navigate in the video frame by frame (requires player focus)</source> 10236 <source>Navigate in the video frame by frame (requires player focus)</source>
10179 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 10237 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10180 10238
10181 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10239 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10182 <trans-unit id="8025996572234182184" datatype="html"> 10240 <trans-unit id="8025996572234182184" datatype="html">
10183 <source>Like the video</source> 10241 <source>Like the video</source>
10184 <target state="new">Like the video</target> 10242 <target state="new">Like the video</target>
diff --git a/client/src/locale/angular.nb-NO.xlf b/client/src/locale/angular.nb-NO.xlf
index 494d4c982..b02226725 100644
--- a/client/src/locale/angular.nb-NO.xlf
+++ b/client/src/locale/angular.nb-NO.xlf
@@ -160,7 +160,7 @@
160 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 160 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
161 </target> 161 </target>
162 162
163 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 163 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
164 <trans-unit id="1486537403020619891" datatype="html"> 164 <trans-unit id="1486537403020619891" datatype="html">
165 <source>My watch history</source> 165 <source>My watch history</source>
166 <target state="translated">Min seer historikk</target> 166 <target state="translated">Min seer historikk</target>
@@ -235,12 +235,12 @@
235 235
236 236
237 237
238 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 238 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
239 <trans-unit id="1006562256968398209" datatype="html"> 239 <trans-unit id="1006562256968398209" datatype="html">
240 <source>video</source> 240 <source>video</source>
241 <target state="translated">video</target> 241 <target state="translated">video</target>
242 242
243 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 243 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
244 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 244 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
245 245
246 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 246 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -301,10 +301,10 @@
301 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 301 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
302 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 302 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
303 303
304 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 304 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
305 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 305 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
306 306
307 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 307 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
308 <trans-unit id="5235042777215655908" datatype="html"> 308 <trans-unit id="5235042777215655908" datatype="html">
309 <source>subtitles</source> 309 <source>subtitles</source>
310 <target state="translated">undertekster</target> 310 <target state="translated">undertekster</target>
@@ -690,10 +690,10 @@
690 <trans-unit id="2602586221576511475" datatype="html"> 690 <trans-unit id="2602586221576511475" datatype="html">
691 <source>Video quota</source> 691 <source>Video quota</source>
692 <target state="new">Video quota</target> 692 <target state="new">Video quota</target>
693 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 693
694 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 694
695 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 695
696 </trans-unit> 696 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
697 <trans-unit id="1502595455339510144" datatype="html"> 697 <trans-unit id="1502595455339510144" datatype="html">
698 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 698 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
699 <target state="new"> 699 <target state="new">
@@ -777,7 +777,31 @@
777 <source>Federation</source> 777 <source>Federation</source>
778 <target state="new">Federation</target> 778 <target state="new">Federation</target>
779 779
780 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 780 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
781 <source>Following</source><target state="new">Following</target>
782 <context-group purpose="location">
783 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
784 <context context-type="linenumber">29</context>
785 </context-group>
786 <context-group purpose="location">
787 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
788 <context context-type="linenumber">31</context>
789 </context-group>
790 <context-group purpose="location">
791 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
792 <context context-type="linenumber">28</context>
793 </context-group>
794 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
795 <source>Followers</source><target state="new">Followers</target>
796 <context-group purpose="location">
797 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
798 <context context-type="linenumber">34</context>
799 </context-group>
800 <context-group purpose="location">
801 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
802 <context context-type="linenumber">37</context>
803 </context-group>
804 </trans-unit>
781 <trans-unit id="3541687134897970106" datatype="html"> 805 <trans-unit id="3541687134897970106" datatype="html">
782 <source>followers</source> 806 <source>followers</source>
783 <target state="new">followers</target> 807 <target state="new">followers</target>
@@ -852,7 +876,7 @@
852 876
853 877
854 878
855 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 879 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
856 <trans-unit id="3616223838716839702" datatype="html"> 880 <trans-unit id="3616223838716839702" datatype="html">
857 <source>Ban this user</source> 881 <source>Ban this user</source>
858 <target state="new">Ban this user</target> 882 <target state="new">Ban this user</target>
@@ -925,19 +949,13 @@
925 <trans-unit id="7252854992688790751" datatype="html"> 949 <trans-unit id="7252854992688790751" datatype="html">
926 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 950 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
927 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 951 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
928 <context-group purpose="location"> 952
929 <context context-type="sourcefile">src/app/+login/login.component.html</context> 953 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
930 <context context-type="linenumber">60,62</context>
931 </context-group>
932 </trans-unit>
933 <trans-unit id="7215649348148521605" datatype="html"> 954 <trans-unit id="7215649348148521605" datatype="html">
934 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 955 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
935 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 956 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
936 <context-group purpose="location"> 957
937 <context context-type="sourcefile">src/app/+login/login.component.html</context> 958 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
938 <context context-type="linenumber">65,67</context>
939 </context-group>
940 </trans-unit>
941 <trans-unit id="2392488717875840729" datatype="html"> 959 <trans-unit id="2392488717875840729" datatype="html">
942 <source>User</source> 960 <source>User</source>
943 <target state="new">User</target> 961 <target state="new">User</target>
@@ -948,66 +966,66 @@
948 <source>Username or email address</source> 966 <source>Username or email address</source>
949 <target state="new">Username or email address</target> 967 <target state="new">Username or email address</target>
950 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 968 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
969 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
970 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
971 <context-group purpose="location">
972 <context context-type="sourcefile">src/app/+login/login.component.html</context>
973 <context context-type="linenumber">33,34</context>
974 </context-group>
951 </trans-unit> 975 </trans-unit>
952 <trans-unit id="1431416938026210429" datatype="html"> 976 <trans-unit id="1431416938026210429" datatype="html">
953 <source>Password</source> 977 <source>Password</source>
954 <target state="new">Password</target> 978 <target state="new">Password</target>
955 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 979
956 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 980
957 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 981
958 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 982
959 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 983
960 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 984
961 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 985
962 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 986
963 </trans-unit> 987 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
964 <trans-unit id="8715156686857791956" datatype="html"> 988 <trans-unit id="8715156686857791956" datatype="html">
965 <source>Click here to reset your password</source> 989 <source>Click here to reset your password</source>
966 <target state="new">Click here to reset your password</target> 990 <target state="new">Click here to reset your password</target>
967 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 991
968 </trans-unit><trans-unit id="892063502898494584" datatype="html"> 992 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html">
969 <source>I forgot my password</source><target state="new">I forgot my password</target> 993 <source>I forgot my password</source><target state="new">I forgot my password</target>
970 <context-group purpose="location"> 994
971 <context context-type="sourcefile">src/app/+login/login.component.html</context> 995 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
972 <context context-type="linenumber">47</context>
973 </context-group>
974 </trans-unit>
975 <trans-unit id="2101170466365500913" datatype="html"> 996 <trans-unit id="2101170466365500913" datatype="html">
976 <source>Logging into an account lets you publish content</source> 997 <source>Logging into an account lets you publish content</source>
977 <target state="new"> Logging into an account lets you publish content </target> 998 <target state="new"> Logging into an account lets you publish content </target>
978 <context-group purpose="location"> 999
979 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1000 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
980 <context context-type="linenumber">56,57</context>
981 </context-group>
982 </trans-unit>
983 <trans-unit id="2454050363478003966" datatype="html"> 1001 <trans-unit id="2454050363478003966" datatype="html">
984 <source>Login</source> 1002 <source>Login</source>
985 <target state="new">Login</target> 1003 <target state="new">Login</target>
986 1004
987 1005
988 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1006 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
989 <trans-unit id="3183213940445113677" datatype="html"> 1007 <trans-unit id="3183213940445113677" datatype="html">
990 <source>Or sign in with</source> 1008 <source>Or sign in with</source>
991 <target state="new">Or sign in with</target> 1009 <target state="new">Or sign in with</target>
992 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1010
993 </trans-unit> 1011 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
994 <trans-unit id="3238209155172574367" datatype="html"> 1012 <trans-unit id="3238209155172574367" datatype="html">
995 <source>Forgot your password</source> 1013 <source>Forgot your password</source>
996 <target state="new">Forgot your password</target> 1014 <target state="new">Forgot your password</target>
997 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1015
998 </trans-unit> 1016 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
999 <trans-unit id="87327320394367488" datatype="html"> 1017 <trans-unit id="87327320394367488" datatype="html">
1000 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1018 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1001 <target state="new"> 1019 <target state="new">
1002 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1020 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
1003 </target> 1021 </target>
1004 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1022
1005 </trans-unit> 1023 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1006 <trans-unit id="3188014010833256853" datatype="html"> 1024 <trans-unit id="3188014010833256853" datatype="html">
1007 <source>Enter your email address and we will send you a link to reset your password.</source> 1025 <source>Enter your email address and we will send you a link to reset your password.</source>
1008 <target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1026 <target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1009 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1027
1010 </trans-unit> 1028 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1011 <trans-unit id="1190256911880544559" datatype="html"> 1029 <trans-unit id="1190256911880544559" datatype="html">
1012 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1030 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1013The link will expire within 1 hour.</source> 1031The link will expire within 1 hour.</source>
@@ -1018,26 +1036,26 @@ The link will expire within 1 hour.</target>
1018 <trans-unit id="4768749765465246664" datatype="html"> 1036 <trans-unit id="4768749765465246664" datatype="html">
1019 <source>Email</source> 1037 <source>Email</source>
1020 <target state="new">Email</target> 1038 <target state="new">Email</target>
1021 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1039
1022 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1040
1023 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1041
1024 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1042
1025 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1043
1026 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1044
1027 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1045
1028 </trans-unit> 1046 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1029 <trans-unit id="3967269098753656610" datatype="html"> 1047 <trans-unit id="3967269098753656610" datatype="html">
1030 <source>Email address</source> 1048 <source>Email address</source>
1031 <target state="new">Email address</target> 1049 <target state="new">Email address</target>
1032 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1050
1033 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1051
1034 </trans-unit> 1052 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1035 <trans-unit id="7808756054397155068" datatype="html"> 1053 <trans-unit id="7808756054397155068" datatype="html">
1036 <source>Reset</source> 1054 <source>Reset</source>
1037 <target state="new">Reset</target> 1055 <target state="new">Reset</target>
1038 <note priority="1" from="description">Password reset button</note> 1056 <note priority="1" from="description">Password reset button</note>
1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1057
1040 </trans-unit> 1058 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1041 1059
1042 <trans-unit id="4319634264526091601" datatype="html"> 1060 <trans-unit id="4319634264526091601" datatype="html">
1043 <source>on this instance</source> 1061 <source>on this instance</source>
@@ -1410,7 +1428,7 @@ The link will expire within 1 hour.</target>
1410 <target state="new">Create an account</target> 1428 <target state="new">Create an account</target>
1411 1429
1412 1430
1413 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1431 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1414 1432
1415 <trans-unit id="3058024914967508975" datatype="html"> 1433 <trans-unit id="3058024914967508975" datatype="html">
1416 <source>My videos</source> 1434 <source>My videos</source>
@@ -1467,7 +1485,7 @@ The link will expire within 1 hour.</target>
1467 <source>VIDEOS</source> 1485 <source>VIDEOS</source>
1468 <target state="new">VIDEOS</target> 1486 <target state="new">VIDEOS</target>
1469 1487
1470 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html"> 1488 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html">
1471 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target> 1489 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target>
1472 1490
1473 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html"> 1491 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html">
@@ -1542,7 +1560,7 @@ The link will expire within 1 hour.</target>
1542 <source>I'm a teapot</source> 1560 <source>I'm a teapot</source>
1543 <target state="new">I'm a teapot</target> 1561 <target state="new">I'm a teapot</target>
1544 1562
1545 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit> 1563 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1546 <trans-unit id="1597262876035959248" datatype="html"> 1564 <trans-unit id="1597262876035959248" datatype="html">
1547 <source>That's an error.</source> 1565 <source>That's an error.</source>
1548 <target state="new">That's an error.</target> 1566 <target state="new">That's an error.</target>
@@ -1623,8 +1641,8 @@ The link will expire within 1 hour.</target>
1623 <trans-unit id="2971365540217107489" datatype="html"> 1641 <trans-unit id="2971365540217107489" datatype="html">
1624 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1642 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1625 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1643 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1626 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1644
1627 </trans-unit> 1645 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1628 1646
1629 <trans-unit id="5131854469652959713" datatype="html"> 1647 <trans-unit id="5131854469652959713" datatype="html">
1630 <source>GLOBAL SEARCH</source> 1648 <source>GLOBAL SEARCH</source>
@@ -2340,7 +2358,7 @@ The link will expire within 1 hour.</target>
2340 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2358 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2341 <source>Upload on hold</source><target state="new">Upload on hold</target> 2359 <source>Upload on hold</source><target state="new">Upload on hold</target>
2342 2360
2343 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2361 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2344 <trans-unit id="285180972645018518" datatype="html"> 2362 <trans-unit id="285180972645018518" datatype="html">
2345 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2363 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2346 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2364 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3068,11 +3086,7 @@ The link will expire within 1 hour.</target>
3068 <target state="new">ID</target> 3086 <target state="new">ID</target>
3069 3087
3070 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit> 3088 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit>
3071 <trans-unit id="2265605798180116441" datatype="html"> 3089
3072 <source>Follower handle</source>
3073 <target state="new">Follower handle</target>
3074
3075 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
3076 <trans-unit id="5911214550882917183" datatype="html"> 3090 <trans-unit id="5911214550882917183" datatype="html">
3077 <source>State</source> 3091 <source>State</source>
3078 <target state="new">State</target> 3092 <target state="new">State</target>
@@ -3147,11 +3161,7 @@ The link will expire within 1 hour.</target>
3147 </target> 3161 </target>
3148 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3162 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3149 </trans-unit> 3163 </trans-unit>
3150 <trans-unit id="6641024648411549335" datatype="html"> 3164
3151 <source>Host</source>
3152 <target state="new">Host</target>
3153
3154 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3155 <trans-unit id="6571718060636962350" datatype="html"> 3165 <trans-unit id="6571718060636962350" datatype="html">
3156 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3166 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3157 <target state="new">Redundancy allowed 3167 <target state="new">Redundancy allowed
@@ -3164,7 +3174,7 @@ The link will expire within 1 hour.</target>
3164 <source>Unfollow</source> 3174 <source>Unfollow</source>
3165 <target state="new">Unfollow</target> 3175 <target state="new">Unfollow</target>
3166 3176
3167 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3177 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3168 <trans-unit id="8246779176913476983" datatype="html"> 3178 <trans-unit id="8246779176913476983" datatype="html">
3169 <source>Open instance in a new tab</source> 3179 <source>Open instance in a new tab</source>
3170 <target state="new">Open instance in a new tab</target> 3180 <target state="new">Open instance in a new tab</target>
@@ -3176,12 +3186,12 @@ The link will expire within 1 hour.</target>
3176 <source>No host found matching current filters.</source> 3186 <source>No host found matching current filters.</source>
3177 <target state="new">No host found matching current filters.</target> 3187 <target state="new">No host found matching current filters.</target>
3178 3188
3179 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3189 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3180 <trans-unit id="7274241885665071790" datatype="html"> 3190 <trans-unit id="7274241885665071790" datatype="html">
3181 <source>Your instance is not following anyone.</source> 3191 <source>Your instance is not following anyone.</source>
3182 <target state="new">Your instance is not following anyone.</target> 3192 <target state="new">Your instance is not following anyone.</target>
3183 3193
3184 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3194 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3185 <trans-unit id="4774348799569692380" datatype="html"> 3195 <trans-unit id="4774348799569692380" datatype="html">
3186 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3196 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3187 <target state="new">Showing 3197 <target state="new">Showing
@@ -3191,16 +3201,8 @@ The link will expire within 1 hour.</target>
3191 </target> 3201 </target>
3192 3202
3193 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3203 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3194 <trans-unit id="6275803119759621687" datatype="html"> 3204
3195 <source>Follow domains</source> 3205 <trans-unit id="9216117865911519658" datatype="html">
3196 <target state="new">Follow domains</target>
3197
3198 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit>
3199 <trans-unit id="1268699198448750610" datatype="html">
3200 <source>Follow instances</source>
3201 <target state="new">Follow instances</target>
3202
3203 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3204 <source>Action</source><target state="new">Action</target> 3206 <source>Action</source><target state="new">Action</target>
3205 3207
3206 3208
@@ -3250,7 +3252,7 @@ The link will expire within 1 hour.</target>
3250 3252
3251 3253
3252 3254
3253 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group></trans-unit> 3255 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3254 <trans-unit id="5428411040014095392" datatype="html"> 3256 <trans-unit id="5428411040014095392" datatype="html">
3255 <source>e.g. jane_doe</source> 3257 <source>e.g. jane_doe</source>
3256 <target state="new">e.g. jane_doe</target> 3258 <target state="new">e.g. jane_doe</target>
@@ -3280,9 +3282,9 @@ The link will expire within 1 hour.</target>
3280 <trans-unit id="4145496584631696119" datatype="html"> 3282 <trans-unit id="4145496584631696119" datatype="html">
3281 <source>Role</source> 3283 <source>Role</source>
3282 <target state="new">Role</target> 3284 <target state="new">Role</target>
3283 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3285
3284 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3286
3285 </trans-unit> 3287 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3286 <trans-unit id="7046347992315328430" datatype="html"> 3288 <trans-unit id="7046347992315328430" datatype="html">
3287 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3289 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3288 <target state="new"> 3290 <target state="new">
@@ -3305,15 +3307,9 @@ The link will expire within 1 hour.</target>
3305 3307
3306 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html"> 3308 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html">
3307 <source>Auth plugin</source><target state="new">Auth plugin</target> 3309 <source>Auth plugin</source><target state="new">Auth plugin</target>
3308 <context-group purpose="location"> 3310
3309 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3311
3310 <context context-type="linenumber">188</context> 3312 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit><trans-unit id="588099657508661970" datatype="html">
3311 </context-group>
3312 <context-group purpose="location">
3313 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3314 <context context-type="linenumber">188</context>
3315 </context-group>
3316 </trans-unit><trans-unit id="588099657508661970" datatype="html">
3317 <source>None (local authentication)</source><target state="new">None (local authentication)</target> 3313 <source>None (local authentication)</source><target state="new">None (local authentication)</target>
3318 <context-group purpose="location"> 3314 <context-group purpose="location">
3319 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3315 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
@@ -3570,7 +3566,13 @@ The link will expire within 1 hour.</target>
3570 3566
3571 3567
3572 3568
3573 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit> 3569 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3570 <source>Follower</source><target state="new">Follower</target>
3571 <context-group purpose="location">
3572 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3573 <context context-type="linenumber">24</context>
3574 </context-group>
3575 </trans-unit>
3574 <trans-unit id="4691552465058437520" datatype="html"> 3576 <trans-unit id="4691552465058437520" datatype="html">
3575 <source>Commented video</source> 3577 <source>Commented video</source>
3576 <target state="new">Commented video</target> 3578 <target state="new">Commented video</target>
@@ -3899,7 +3901,7 @@ The link will expire within 1 hour.</target>
3899 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 3901 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
3900 </target> 3902 </target>
3901 3903
3902 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3904 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3903 <trans-unit id="4058814854824495833" datatype="html"> 3905 <trans-unit id="4058814854824495833" datatype="html">
3904 <source>Mute domains</source> 3906 <source>Mute domains</source>
3905 <target state="new">Mute domains</target> 3907 <target state="new">Mute domains</target>
@@ -5818,11 +5820,8 @@ color: red;
5818 5820
5819 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5821 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5820 <source>CHANNELS</source><target state="new">CHANNELS</target> 5822 <source>CHANNELS</source><target state="new">CHANNELS</target>
5821 <context-group purpose="location"> 5823
5822 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5824 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5823 <context context-type="linenumber">82</context>
5824 </context-group>
5825 </trans-unit>
5826 5825
5827 <trans-unit id="3666829335406793239" datatype="html"> 5826 <trans-unit id="3666829335406793239" datatype="html">
5828 <source>This account does not have channels.</source> 5827 <source>This account does not have channels.</source>
@@ -5862,7 +5861,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
5862It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 5861It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
5863channel with the same name (<x id="PH_2"/>)!</target> 5862channel with the same name (<x id="PH_2"/>)!</target>
5864 5863
5865 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5864 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5865 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5866 <context-group purpose="location">
5867 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5868 <context context-type="linenumber">48</context>
5869 </context-group>
5870 </trans-unit>
5866 <trans-unit id="5387007581996837469" datatype="html"> 5871 <trans-unit id="5387007581996837469" datatype="html">
5867 <source>My Channels</source> 5872 <source>My Channels</source>
5868 <target state="new">My Channels</target> 5873 <target state="new">My Channels</target>
@@ -6465,12 +6470,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6465 <source>Your message has been sent.</source> 6470 <source>Your message has been sent.</source>
6466 <target state="new">Your message has been sent.</target> 6471 <target state="new">Your message has been sent.</target>
6467 6472
6468 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6473 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6469 <trans-unit id="2072135752262464360" datatype="html"> 6474 <trans-unit id="2072135752262464360" datatype="html">
6470 <source>You already sent this form recently</source> 6475 <source>You already sent this form recently</source>
6471 <target state="new">You already sent this form recently</target> 6476 <target state="new">You already sent this form recently</target>
6472 6477
6473 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6478 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6474 6479
6475 <trans-unit id="819067926858619041" datatype="html"> 6480 <trans-unit id="819067926858619041" datatype="html">
6476 <source>Account videos</source> 6481 <source>Account videos</source>
@@ -6517,12 +6522,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6517 <x id="PH"/> direct account followers 6522 <x id="PH"/> direct account followers
6518 </target> 6523 </target>
6519 6524
6520 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit> 6525 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6521 <trans-unit id="6250999352462648289" datatype="html"> 6526 <trans-unit id="6250999352462648289" datatype="html">
6522 <source>Report this account</source> 6527 <source>Report this account</source>
6523 <target state="new">Report this account</target> 6528 <target state="new">Report this account</target>
6524 6529
6525 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6530 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6526 6531
6527 <trans-unit id="1504521795586863905" datatype="html"> 6532 <trans-unit id="1504521795586863905" datatype="html">
6528 <source>VIDEOS</source> 6533 <source>VIDEOS</source>
@@ -6536,27 +6541,19 @@ channel with the same name (<x id="PH_2"/>)!</target>
6536 <target state="new">Username copied</target> 6541 <target state="new">Username copied</target>
6537 6542
6538 6543
6539 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit> 6544 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6540 <trans-unit id="9221735175659318025" datatype="html"> 6545 <trans-unit id="9221735175659318025" datatype="html">
6541 <source>1 subscriber</source> 6546 <source>1 subscriber</source>
6542 <target state="new">1 subscriber</target> 6547 <target state="new">1 subscriber</target>
6543 6548
6544 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit> 6549 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6545 <trans-unit id="4097331874769079975" datatype="html"> 6550 <trans-unit id="4097331874769079975" datatype="html">
6546 <source><x id="PH"/> subscribers</source> 6551 <source><x id="PH"/> subscribers</source>
6547 <target state="new"><x id="PH"/> subscribers</target> 6552 <target state="new"><x id="PH"/> subscribers</target>
6548 6553
6549 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6554 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6550 <trans-unit id="4682675125751819107" datatype="html"> 6555
6551 <source>Instances you follow</source> 6556
6552 <target state="new">Instances you follow</target>
6553
6554 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6555 <trans-unit id="8899833753704589712" datatype="html">
6556 <source>Instances following you</source>
6557 <target state="new">Instances following you</target>
6558
6559 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6560 <trans-unit id="1035838766454786107" datatype="html"> 6557 <trans-unit id="1035838766454786107" datatype="html">
6561 <source>Audio-only</source> 6558 <source>Audio-only</source>
6562 <target state="new">Audio-only</target> 6559 <target state="new">Audio-only</target>
@@ -6606,7 +6603,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6606 <source>Auto (via ffmpeg)</source> 6603 <source>Auto (via ffmpeg)</source>
6607 <target state="new">Auto (via ffmpeg)</target> 6604 <target state="new">Auto (via ffmpeg)</target>
6608 6605
6609 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit> 6606 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="3642770981085338761" datatype="html">
6607 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6608 <context-group purpose="location">
6609 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6610 <context context-type="linenumber">3</context>
6611 </context-group>
6612 </trans-unit>
6610 <trans-unit id="931255636742351800" datatype="html"> 6613 <trans-unit id="931255636742351800" datatype="html">
6611 <source>No limit</source> 6614 <source>No limit</source>
6612 <target state="new">No limit</target> 6615 <target state="new">No limit</target>
@@ -6738,18 +6741,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
6738 <trans-unit id="2127446333083057097" datatype="html"> 6741 <trans-unit id="2127446333083057097" datatype="html">
6739 <source>Domain is required.</source> 6742 <source>Domain is required.</source>
6740 <target state="new">Domain is required.</target> 6743 <target state="new">Domain is required.</target>
6741 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6744
6742 </trans-unit> 6745 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6743 <trans-unit id="6780793142903080663" datatype="html"> 6746 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6744 <source>Domains entered are invalid.</source> 6747 <context-group purpose="location">
6745 <target state="new">Domains entered are invalid.</target> 6748 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6746 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6749 <context context-type="linenumber">93</context>
6747 </trans-unit> 6750 </context-group>
6748 <trans-unit id="5886492514458202177" datatype="html"> 6751 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6749 <source>Domains entered contain duplicates.</source> 6752 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6750 <target state="new">Domains entered contain duplicates.</target> 6753 <context-group purpose="location">
6751 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6754 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6755 <context context-type="linenumber">94</context>
6756 </context-group>
6757 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6758 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6759 <context-group purpose="location">
6760 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6761 <context context-type="linenumber">102</context>
6762 </context-group>
6763 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6764 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6765 <context-group purpose="location">
6766 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6767 <context context-type="linenumber">103</context>
6768 </context-group>
6752 </trans-unit> 6769 </trans-unit>
6770
6771
6753 <trans-unit id="240806681889331244" datatype="html"> 6772 <trans-unit id="240806681889331244" datatype="html">
6754 <source>Unlimited</source> 6773 <source>Unlimited</source>
6755 <target state="new">Unlimited</target> 6774 <target state="new">Unlimited</target>
@@ -6899,26 +6918,52 @@ channel with the same name (<x id="PH_2"/>)!</target>
6899 <x id="PH"/> removed from instance followers 6918 <x id="PH"/> removed from instance followers
6900 </target> 6919 </target>
6901 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6920 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6921 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6922 <source>Follow</source><target state="new">Follow</target>
6923 <context-group purpose="location">
6924 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6925 <context context-type="linenumber">3</context>
6926 </context-group>
6927 <context-group purpose="location">
6928 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6929 <context context-type="linenumber">37</context>
6930 </context-group>
6931 <context-group purpose="location">
6932 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6933 <context context-type="linenumber">18</context>
6934 </context-group>
6935 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6936 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6937 <context-group purpose="location">
6938 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6939 <context context-type="linenumber">11</context>
6940 </context-group>
6902 </trans-unit> 6941 </trans-unit>
6903 <trans-unit id="2740793005745065895" datatype="html"> 6942 <trans-unit id="2740793005745065895" datatype="html">
6904 <source><x id="PH"/> is not valid </source> 6943 <source><x id="PH"/> is not valid </source>
6905 <target state="new"> 6944 <target state="new">
6906 <x id="PH"/> is not valid 6945 <x id="PH"/> is not valid
6907 </target> 6946 </target>
6908 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6947
6909 </trans-unit> 6948 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6910 <trans-unit id="2355066641781598196" datatype="html"> 6949 <trans-unit id="2355066641781598196" datatype="html">
6911 <source>Follow request(s) sent!</source> 6950 <source>Follow request(s) sent!</source>
6912 <target state="new">Follow request(s) sent!</target> 6951 <target state="new">Follow request(s) sent!</target>
6913 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6952
6953 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6954 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6955 <context-group purpose="location">
6956 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6957 <context context-type="linenumber">3</context>
6958 </context-group>
6914 </trans-unit> 6959 </trans-unit>
6915 <trans-unit id="4245720728052819482" datatype="html"> 6960 <trans-unit id="4245720728052819482" datatype="html">
6916 <source>Do you really want to unfollow <x id="PH"/>?</source> 6961 <source>Do you really want to unfollow <x id="PH"/>?</source>
6917 <target state="new">Do you really want to unfollow 6962 <target state="new">Do you really want to unfollow
6918 <x id="PH"/>? 6963 <x id="PH"/>?
6919 </target> 6964 </target>
6920 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6965
6921 </trans-unit> 6966 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6922 <trans-unit id="9160510009013134726" datatype="html"> 6967 <trans-unit id="9160510009013134726" datatype="html">
6923 <source>Unfollow</source> 6968 <source>Unfollow</source>
6924 <target state="new">Unfollow</target> 6969 <target state="new">Unfollow</target>
@@ -6929,8 +6974,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
6929 <target state="new">You are not following 6974 <target state="new">You are not following
6930 <x id="PH"/> anymore. 6975 <x id="PH"/> anymore.
6931 </target> 6976 </target>
6932 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6977
6933 </trans-unit> 6978 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6934 <trans-unit id="2593763089859685916" datatype="html"> 6979 <trans-unit id="2593763089859685916" datatype="html">
6935 <source>enabled</source> 6980 <source>enabled</source>
6936 <target state="new">enabled</target> 6981 <target state="new">enabled</target>
@@ -7397,9 +7442,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7397 <trans-unit id="1519954996184640001" datatype="html"> 7442 <trans-unit id="1519954996184640001" datatype="html">
7398 <source>Error</source> 7443 <source>Error</source>
7399 <target state="new">Error</target> 7444 <target state="new">Error</target>
7400 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7445
7401 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7446
7402 </trans-unit> 7447 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7403 <trans-unit id="5076187961693950167" datatype="html"> 7448 <trans-unit id="5076187961693950167" datatype="html">
7404 <source>Standard logs</source> 7449 <source>Standard logs</source>
7405 <target state="new">Standard logs</target> 7450 <target state="new">Standard logs</target>
@@ -7444,16 +7489,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7444 <target state="new">Update user password</target> 7489 <target state="new">Update user password</target>
7445 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7490 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7446 </trans-unit> 7491 </trans-unit>
7447 <trans-unit id="177544274549739411" datatype="html"> 7492
7448 <source>Following list</source> 7493
7449 <target state="new">Following list</target>
7450 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7451 </trans-unit>
7452 <trans-unit id="8092429110007204784" datatype="html">
7453 <source>Followers list</source>
7454 <target state="new">Followers list</target>
7455 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7456 </trans-unit>
7457 <trans-unit id="780323526182667308" datatype="html"> 7494 <trans-unit id="780323526182667308" datatype="html">
7458 <source>User <x id="PH"/> updated.</source> 7495 <source>User <x id="PH"/> updated.</source>
7459 <target state="new">User 7496 <target state="new">User
@@ -7493,16 +7530,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7493 <target state="new">Federation</target> 7530 <target state="new">Federation</target>
7494 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7531 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7495 </trans-unit> 7532 </trans-unit>
7496 <trans-unit id="4682675125751819107" datatype="html"> 7533
7497 <source>Instances you follow</source> 7534
7498 <target state="new">Instances you follow</target>
7499 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7500 </trans-unit>
7501 <trans-unit id="8899833753704589712" datatype="html">
7502 <source>Instances following you</source>
7503 <target state="new">Instances following you</target>
7504 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7505 </trans-unit>
7506 <trans-unit id="3767259920053407667" datatype="html"> 7535 <trans-unit id="3767259920053407667" datatype="html">
7507 <source>Videos will be deleted, comments will be tombstoned.</source> 7536 <source>Videos will be deleted, comments will be tombstoned.</source>
7508 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7537 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7533,7 +7562,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7533 <target state="new">Set Email as Verified</target> 7562 <target state="new">Set Email as Verified</target>
7534 7563
7535 7564
7536 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7565 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7566 <source>Created</source><target state="new">Created</target>
7567 <context-group purpose="location">
7568 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7569 <context context-type="linenumber">115</context>
7570 </context-group>
7571 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7572 <source>Daily quota</source><target state="new">Daily quota</target>
7573 <context-group purpose="location">
7574 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7575 <context context-type="linenumber">120</context>
7576 </context-group>
7577 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7578 <source>Last login</source><target state="new">Last login</target>
7579 <context-group purpose="location">
7580 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7581 <context context-type="linenumber">122</context>
7582 </context-group>
7583 </trans-unit>
7537 <trans-unit id="3403978719736970622" datatype="html"> 7584 <trans-unit id="3403978719736970622" datatype="html">
7538 <source>You cannot ban root.</source> 7585 <source>You cannot ban root.</source>
7539 <target state="new">You cannot ban root.</target> 7586 <target state="new">You cannot ban root.</target>
@@ -7842,12 +7889,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
7842 <x id="PH"/> created. 7889 <x id="PH"/> created.
7843 </target> 7890 </target>
7844 7891
7845 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7892 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7846 <trans-unit id="8723777130353305761" datatype="html"> 7893 <trans-unit id="8723777130353305761" datatype="html">
7847 <source>This name already exists on this instance.</source> 7894 <source>This name already exists on this instance.</source>
7848 <target state="new">This name already exists on this instance.</target> 7895 <target state="new">This name already exists on this instance.</target>
7849 7896
7850 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7897 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7851 <trans-unit id="7589345916094713536" datatype="html"> 7898 <trans-unit id="7589345916094713536" datatype="html">
7852 <source>Video channel <x id="PH"/> updated.</source> 7899 <source>Video channel <x id="PH"/> updated.</source>
7853 <target state="new">Video channel 7900 <target state="new">Video channel
@@ -7864,13 +7911,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7864 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 7911 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7865 7912
7866 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7913 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7867 <trans-unit id="2575302837003821736" datatype="html"> 7914
7868 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7869 <target state="new">Please type the display name of the video channel (
7870 <x id="PH"/>) to confirm
7871 </target>
7872
7873 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7874 <trans-unit id="624066830180032195" datatype="html"> 7915 <trans-unit id="624066830180032195" datatype="html">
7875 <source>Video channel <x id="PH"/> deleted.</source> 7916 <source>Video channel <x id="PH"/> deleted.</source>
7876 <target state="new">Video channel 7917 <target state="new">Video channel
@@ -8027,6 +8068,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8027 <source>Ownership change request sent.</source> 8068 <source>Ownership change request sent.</source>
8028 <target state="new">Ownership change request sent.</target> 8069 <target state="new">Ownership change request sent.</target>
8029 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8070 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8071 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8072 <source>Sort by</source><target state="new">Sort by</target>
8073 <context-group purpose="location">
8074 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8075 <context context-type="linenumber">26</context>
8076 </context-group>
8030 </trans-unit> 8077 </trans-unit>
8031 <trans-unit id="3245220240937722814" datatype="html"> 8078 <trans-unit id="3245220240937722814" datatype="html">
8032 <source>My channels</source> 8079 <source>My channels</source>
@@ -8132,7 +8179,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8132 <target state="new">Subscribe to the account</target> 8179 <target state="new">Subscribe to the account</target>
8133 8180
8134 8181
8135 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 8182 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
8136 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 8183 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
8137 <context-group purpose="location"> 8184 <context-group purpose="location">
8138 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 8185 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -8178,35 +8225,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8178 <trans-unit id="3779524668013120370" datatype="html"> 8225 <trans-unit id="3779524668013120370" datatype="html">
8179 <source>Go to my subscriptions</source> 8226 <source>Go to my subscriptions</source>
8180 <target state="new">Go to my subscriptions</target> 8227 <target state="new">Go to my subscriptions</target>
8181 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8228
8182 </trans-unit> 8229 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8183 <trans-unit id="1136469849928650779" datatype="html"> 8230 <trans-unit id="1136469849928650779" datatype="html">
8184 <source>Go to my videos</source> 8231 <source>Go to my videos</source>
8185 <target state="new">Go to my videos</target> 8232 <target state="new">Go to my videos</target>
8186 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8233
8187 </trans-unit> 8234 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8188 <trans-unit id="7836683738999600376" datatype="html"> 8235 <trans-unit id="7836683738999600376" datatype="html">
8189 <source>Go to my imports</source> 8236 <source>Go to my imports</source>
8190 <target state="new">Go to my imports</target> 8237 <target state="new">Go to my imports</target>
8191 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8238
8192 </trans-unit> 8239 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8193 <trans-unit id="7511292153332773503" datatype="html"> 8240 <trans-unit id="7511292153332773503" datatype="html">
8194 <source>Go to my channels</source> 8241 <source>Go to my channels</source>
8195 <target state="new">Go to my channels</target> 8242 <target state="new">Go to my channels</target>
8196 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8243
8197 </trans-unit> 8244 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8198 <trans-unit id="2013324644839511073" datatype="html"> 8245 <trans-unit id="2013324644839511073" datatype="html">
8199 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8246 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8200Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8247Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8201 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8248 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8202Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8249Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8203 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8250
8204 </trans-unit> 8251 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8205 <trans-unit id="375263728166936544" datatype="html"> 8252 <trans-unit id="375263728166936544" datatype="html">
8206 <source>You need to reconnect.</source> 8253 <source>You need to reconnect.</source>
8207 <target state="new">You need to reconnect.</target> 8254 <target state="new">You need to reconnect.</target>
8208 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8255
8209 </trans-unit> 8256 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8210 <trans-unit id="2206638022166154361" datatype="html"> 8257 <trans-unit id="2206638022166154361" datatype="html">
8211 <source>Keyboard Shortcuts:</source> 8258 <source>Keyboard Shortcuts:</source>
8212 <target state="new">Keyboard Shortcuts:</target> 8259 <target state="new">Keyboard Shortcuts:</target>
@@ -8217,6 +8264,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8217 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8264 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8218 <context context-type="linenumber">98</context> 8265 <context context-type="linenumber">98</context>
8219 </context-group> 8266 </context-group>
8267 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8268 <source>In my library</source><target state="new">In my library</target>
8269 <context-group purpose="location">
8270 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8271 <context context-type="linenumber">104</context>
8272 </context-group>
8220 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 8273 </trans-unit><trans-unit id="232050922346936574" datatype="html">
8221 <source>Trending</source><target state="new">Trending</target> 8274 <source>Trending</source><target state="new">Trending</target>
8222 8275
@@ -8240,38 +8293,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8240 <source>Incorrect username or password.</source> 8293 <source>Incorrect username or password.</source>
8241 <target state="new">Incorrect username or password.</target> 8294 <target state="new">Incorrect username or password.</target>
8242 8295
8243 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 8296 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8244 <trans-unit id="6974874606619467663" datatype="html"> 8297 <trans-unit id="6974874606619467663" datatype="html">
8245 <source>Your account is blocked.</source> 8298 <source>Your account is blocked.</source>
8246 <target state="new">Your account is blocked.</target> 8299 <target state="new">Your account is blocked.</target>
8247 8300
8248 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 8301 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8249 <trans-unit id="7939914198003891823" datatype="html"> 8302 <trans-unit id="7939914198003891823" datatype="html">
8250 <source>any language</source> 8303 <source>any language</source>
8251 <target state="new">any language</target> 8304 <target state="new">any language</target>
8252 8305
8253 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8306 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8254 8307
8255 <trans-unit id="5633144232269377096" datatype="html"> 8308 <trans-unit id="5633144232269377096" datatype="html">
8256 <source>hide</source> 8309 <source>hide</source>
8257 <target state="new">hide</target> 8310 <target state="new">hide</target>
8258 8311
8259 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8312 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8260 <trans-unit id="8603861867909474404" datatype="html"> 8313 <trans-unit id="8603861867909474404" datatype="html">
8261 <source>blur</source> 8314 <source>blur</source>
8262 <target state="new">blur</target> 8315 <target state="new">blur</target>
8263 8316
8264 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8317 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8265 <trans-unit id="4534458451100881847" datatype="html"> 8318 <trans-unit id="4534458451100881847" datatype="html">
8266 <source>display</source> 8319 <source>display</source>
8267 <target state="new">display</target> 8320 <target state="new">display</target>
8268 8321
8269 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8322 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8270 <trans-unit id="4467323362722952678" datatype="html"> 8323 <trans-unit id="4467323362722952678" datatype="html">
8271 <source>Unknown</source> 8324 <source>Unknown</source>
8272 <target state="new">Unknown</target> 8325 <target state="new">Unknown</target>
8273 8326
8274 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8327 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8275 <trans-unit id="8781423666414310853" datatype="html"> 8328 <trans-unit id="8781423666414310853" datatype="html">
8276 <source>Your password has been successfully reset!</source> 8329 <source>Your password has been successfully reset!</source>
8277 <target state="new">Your password has been successfully reset!</target> 8330 <target state="new">Your password has been successfully reset!</target>
@@ -9832,18 +9885,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9832 <target state="new">Too many attempts, please try again after 9885 <target state="new">Too many attempts, please try again after
9833 <x id="PH"/> minutes. 9886 <x id="PH"/> minutes.
9834 </target> 9887 </target>
9835 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9888
9836 </trans-unit> 9889 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9837 <trans-unit id="4965472196059235310" datatype="html"> 9890 <trans-unit id="4965472196059235310" datatype="html">
9838 <source>Too many attempts, please try again later.</source> 9891 <source>Too many attempts, please try again later.</source>
9839 <target state="new">Too many attempts, please try again later.</target> 9892 <target state="new">Too many attempts, please try again later.</target>
9840 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9893
9841 </trans-unit> 9894 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9842 <trans-unit id="1693549688987384699" datatype="html"> 9895 <trans-unit id="1693549688987384699" datatype="html">
9843 <source>Server error. Please retry later.</source> 9896 <source>Server error. Please retry later.</source>
9844 <target state="new">Server error. Please retry later.</target> 9897 <target state="new">Server error. Please retry later.</target>
9845 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9898
9846 </trans-unit> 9899 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9847 <trans-unit id="5927402622550505067" datatype="html"> 9900 <trans-unit id="5927402622550505067" datatype="html">
9848 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9901 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9849 <target state="new">Subscribed to all current channels of 9902 <target state="new">Subscribed to all current channels of
@@ -10438,35 +10491,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10438 <source>Your video was uploaded to your account and is private.</source> 10491 <source>Your video was uploaded to your account and is private.</source>
10439 <target state="new">Your video was uploaded to your account and is private.</target> 10492 <target state="new">Your video was uploaded to your account and is private.</target>
10440 10493
10441 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10494 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10442 <trans-unit id="5699822024600815733" datatype="html"> 10495 <trans-unit id="5699822024600815733" datatype="html">
10443 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10496 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10444 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target> 10497 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target>
10445 10498
10446 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10499 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10447 <trans-unit id="1219739004043110649" datatype="html"> 10500 <trans-unit id="1219739004043110649" datatype="html">
10448 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10501 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10449 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target> 10502 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target>
10450 10503
10451 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10504 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10452 <trans-unit id="6932865105766151309" datatype="html"> 10505 <trans-unit id="6932865105766151309" datatype="html">
10453 <source>Upload</source> 10506 <source>Upload</source>
10454 <target state="new">Upload</target> 10507 <target state="new">Upload</target>
10455 10508
10456 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10509 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10457 <trans-unit id="8278735427925094503" datatype="html"> 10510 <trans-unit id="8278735427925094503" datatype="html">
10458 <source>Upload <x id="PH"/> </source> 10511 <source>Upload <x id="PH"/> </source>
10459 <target state="new">Upload 10512 <target state="new">Upload
10460 <x id="PH"/> 10513 <x id="PH"/>
10461 </target> 10514 </target>
10462 10515
10463 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10516 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10464 10517
10465 <trans-unit id="5981816353437801748" datatype="html"> 10518 <trans-unit id="5981816353437801748" datatype="html">
10466 <source>Video published.</source> 10519 <source>Video published.</source>
10467 <target state="new">Video published.</target> 10520 <target state="new">Video published.</target>
10468 10521
10469 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10522 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10470 10523
10471 10524
10472 <trans-unit id="764164089183618119" datatype="html"> 10525 <trans-unit id="764164089183618119" datatype="html">
@@ -10534,27 +10587,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10534 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10587 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10535 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10588 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10536 10589
10537 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10590 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10538 <trans-unit id="5761611056224181752" datatype="html"> 10591 <trans-unit id="5761611056224181752" datatype="html">
10539 <source>Redirection</source> 10592 <source>Redirection</source>
10540 <target state="new">Redirection</target> 10593 <target state="new">Redirection</target>
10541 10594
10542 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10595 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10543 <trans-unit id="8858527736400081688" datatype="html"> 10596 <trans-unit id="8858527736400081688" datatype="html">
10544 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10597 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10545 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target> 10598 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target>
10546 10599
10547 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10600 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10548 <trans-unit id="3937119019020041049" datatype="html"> 10601 <trans-unit id="3937119019020041049" datatype="html">
10549 <source>Mature or explicit content</source> 10602 <source>Mature or explicit content</source>
10550 <target state="new">Mature or explicit content</target> 10603 <target state="new">Mature or explicit content</target>
10551 10604
10552 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10605 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10553 <trans-unit id="1755474755114288376" datatype="html"> 10606 <trans-unit id="1755474755114288376" datatype="html">
10554 <source>Up Next</source> 10607 <source>Up Next</source>
10555 <target state="new">Up Next</target> 10608 <target state="new">Up Next</target>
10556 10609
10557 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10610 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10558 <trans-unit id="2159130950882492111" datatype="html"> 10611 <trans-unit id="2159130950882492111" datatype="html">
10559 <source>Cancel</source> 10612 <source>Cancel</source>
10560 <target state="new">Cancel</target> 10613 <target state="new">Cancel</target>
@@ -10564,62 +10617,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10564 <source>Autoplay is suspended</source> 10617 <source>Autoplay is suspended</source>
10565 <target state="new">Autoplay is suspended</target> 10618 <target state="new">Autoplay is suspended</target>
10566 10619
10567 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10620 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10568 <trans-unit id="7895294730547405228" datatype="html"> 10621 <trans-unit id="7895294730547405228" datatype="html">
10569 <source>Enter/exit fullscreen (requires player focus)</source> 10622 <source>Enter/exit fullscreen (requires player focus)</source>
10570 <target state="new">Enter/exit fullscreen (requires player focus)</target> 10623 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10571 10624
10572 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10625 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10573 <trans-unit id="7618388257165864759" datatype="html"> 10626 <trans-unit id="7618388257165864759" datatype="html">
10574 <source>Play/Pause the video (requires player focus)</source> 10627 <source>Play/Pause the video (requires player focus)</source>
10575 <target state="new">Play/Pause the video (requires player focus)</target> 10628 <target state="new">Play/Pause the video (requires player focus)</target>
10576 10629
10577 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10630 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10578 <trans-unit id="7761890399634216630" datatype="html"> 10631 <trans-unit id="7761890399634216630" datatype="html">
10579 <source>Mute/unmute the video (requires player focus)</source> 10632 <source>Mute/unmute the video (requires player focus)</source>
10580 <target state="new">Mute/unmute the video (requires player focus)</target> 10633 <target state="new">Mute/unmute the video (requires player focus)</target>
10581 10634
10582 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10635 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10583 <trans-unit id="5996585232248234904" datatype="html"> 10636 <trans-unit id="5996585232248234904" datatype="html">
10584 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10637 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10585 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10638 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10586 10639
10587 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10640 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10588 <trans-unit id="3748765405903319998" datatype="html"> 10641 <trans-unit id="3748765405903319998" datatype="html">
10589 <source>Increase the volume (requires player focus)</source> 10642 <source>Increase the volume (requires player focus)</source>
10590 <target state="new">Increase the volume (requires player focus)</target> 10643 <target state="new">Increase the volume (requires player focus)</target>
10591 10644
10592 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10645 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10593 <trans-unit id="5810704036407159982" datatype="html"> 10646 <trans-unit id="5810704036407159982" datatype="html">
10594 <source>Decrease the volume (requires player focus)</source> 10647 <source>Decrease the volume (requires player focus)</source>
10595 <target state="new">Decrease the volume (requires player focus)</target> 10648 <target state="new">Decrease the volume (requires player focus)</target>
10596 10649
10597 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10650 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10598 <trans-unit id="2622048822548065691" datatype="html"> 10651 <trans-unit id="2622048822548065691" datatype="html">
10599 <source>Seek the video forward (requires player focus)</source> 10652 <source>Seek the video forward (requires player focus)</source>
10600 <target state="new">Seek the video forward (requires player focus)</target> 10653 <target state="new">Seek the video forward (requires player focus)</target>
10601 10654
10602 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10655 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10603 <trans-unit id="6540078205109221153" datatype="html"> 10656 <trans-unit id="6540078205109221153" datatype="html">
10604 <source>Seek the video backward (requires player focus)</source> 10657 <source>Seek the video backward (requires player focus)</source>
10605 <target state="new">Seek the video backward (requires player focus)</target> 10658 <target state="new">Seek the video backward (requires player focus)</target>
10606 10659
10607 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10660 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10608 <trans-unit id="1956491957766210808" datatype="html"> 10661 <trans-unit id="1956491957766210808" datatype="html">
10609 <source>Increase playback rate (requires player focus)</source> 10662 <source>Increase playback rate (requires player focus)</source>
10610 <target state="new">Increase playback rate (requires player focus)</target> 10663 <target state="new">Increase playback rate (requires player focus)</target>
10611 10664
10612 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10665 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10613 <trans-unit id="5495529997674803186" datatype="html"> 10666 <trans-unit id="5495529997674803186" datatype="html">
10614 <source>Decrease playback rate (requires player focus)</source> 10667 <source>Decrease playback rate (requires player focus)</source>
10615 <target state="new">Decrease playback rate (requires player focus)</target> 10668 <target state="new">Decrease playback rate (requires player focus)</target>
10616 10669
10617 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10670 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10618 <trans-unit id="3178343147230721210" datatype="html"> 10671 <trans-unit id="3178343147230721210" datatype="html">
10619 <source>Navigate in the video frame by frame (requires player focus)</source> 10672 <source>Navigate in the video frame by frame (requires player focus)</source>
10620 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 10673 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10621 10674
10622 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10675 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10623 <trans-unit id="8025996572234182184" datatype="html"> 10676 <trans-unit id="8025996572234182184" datatype="html">
10624 <source>Like the video</source> 10677 <source>Like the video</source>
10625 <target state="new">Like the video</target> 10678 <target state="new">Like the video</target>
diff --git a/client/src/locale/angular.nl-NL.xlf b/client/src/locale/angular.nl-NL.xlf
index 317265555..ea82323b4 100644
--- a/client/src/locale/angular.nl-NL.xlf
+++ b/client/src/locale/angular.nl-NL.xlf
@@ -160,7 +160,7 @@
160 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source> 160 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source>
161 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target> 161 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target>
162 162
163 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html"> 163 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html">
164 <source>My watch history</source><target state="new">My watch history</target> 164 <source>My watch history</source><target state="new">My watch history</target>
165 165
166 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 166 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
@@ -233,12 +233,12 @@
233 233
234 234
235 235
236 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 236 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
237 <trans-unit id="1006562256968398209" datatype="html"> 237 <trans-unit id="1006562256968398209" datatype="html">
238 <source>video</source> 238 <source>video</source>
239 <target state="translated">video</target> 239 <target state="translated">video</target>
240 240
241 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 241 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
242 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 242 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
243 243
244 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 244 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -299,10 +299,10 @@
299 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 299 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
300 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 300 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
301 301
302 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 302 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
303 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 303 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
304 304
305 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 305 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
306 <trans-unit id="5235042777215655908" datatype="html"> 306 <trans-unit id="5235042777215655908" datatype="html">
307 <source>subtitles</source> 307 <source>subtitles</source>
308 <target state="translated">ondertitels</target> 308 <target state="translated">ondertitels</target>
@@ -683,10 +683,10 @@ Annuleren</target>
683 <trans-unit id="2602586221576511475"> 683 <trans-unit id="2602586221576511475">
684 <source>Video quota</source> 684 <source>Video quota</source>
685 <target>Videoquotum</target> 685 <target>Videoquotum</target>
686 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 686
687 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 687
688 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 688
689 </trans-unit> 689 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
690 <trans-unit id="1502595455339510144"> 690 <trans-unit id="1502595455339510144">
691 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 691 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
692 <target>Oneindig <x id="START_TAG_NG-CONTAINER" ctype="x-ng-container" equiv-text="&lt;ng-container>"/>( <x id="INTERPOLATION" equiv-text="{{ dailyUserVideoQuota | bytes: 0 }}"/> per dag) <x id="CLOSE_TAG_NG-CONTAINER" ctype="x-ng-container" equiv-text="&lt;/ng-container>"/></target> 692 <target>Oneindig <x id="START_TAG_NG-CONTAINER" ctype="x-ng-container" equiv-text="&lt;ng-container>"/>( <x id="INTERPOLATION" equiv-text="{{ dailyUserVideoQuota | bytes: 0 }}"/> per dag) <x id="CLOSE_TAG_NG-CONTAINER" ctype="x-ng-container" equiv-text="&lt;/ng-container>"/></target>
@@ -765,7 +765,31 @@ Annuleren</target>
765 <source>Federation</source> 765 <source>Federation</source>
766 <target state="translated">Federatie</target> 766 <target state="translated">Federatie</target>
767 767
768 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 768 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
769 <source>Following</source><target state="new">Following</target>
770 <context-group purpose="location">
771 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
772 <context context-type="linenumber">29</context>
773 </context-group>
774 <context-group purpose="location">
775 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
776 <context context-type="linenumber">31</context>
777 </context-group>
778 <context-group purpose="location">
779 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
780 <context context-type="linenumber">28</context>
781 </context-group>
782 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
783 <source>Followers</source><target state="new">Followers</target>
784 <context-group purpose="location">
785 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
786 <context context-type="linenumber">34</context>
787 </context-group>
788 <context-group purpose="location">
789 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
790 <context context-type="linenumber">37</context>
791 </context-group>
792 </trans-unit>
769 <trans-unit id="3541687134897970106" datatype="html"> 793 <trans-unit id="3541687134897970106" datatype="html">
770 <source>followers</source> 794 <source>followers</source>
771 <target state="translated">volgers</target> 795 <target state="translated">volgers</target>
@@ -839,7 +863,7 @@ Een verbannen gebruiker kan niet langer inloggen.</target>
839 863
840 864
841 865
842 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 866 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
843 <trans-unit id="3616223838716839702"> 867 <trans-unit id="3616223838716839702">
844 <source>Ban this user</source> 868 <source>Ban this user</source>
845 <target>Verban deze gebruiker</target> 869 <target>Verban deze gebruiker</target>
@@ -904,19 +928,13 @@ Aanmelden</target>
904 <trans-unit id="7252854992688790751" datatype="html"> 928 <trans-unit id="7252854992688790751" datatype="html">
905 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 929 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
906 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 930 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
907 <context-group purpose="location"> 931
908 <context context-type="sourcefile">src/app/+login/login.component.html</context> 932 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
909 <context context-type="linenumber">60,62</context>
910 </context-group>
911 </trans-unit>
912 <trans-unit id="7215649348148521605" datatype="html"> 933 <trans-unit id="7215649348148521605" datatype="html">
913 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 934 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
914 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 935 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
915 <context-group purpose="location"> 936
916 <context context-type="sourcefile">src/app/+login/login.component.html</context> 937 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
917 <context context-type="linenumber">65,67</context>
918 </context-group>
919 </trans-unit>
920 <trans-unit id="2392488717875840729"> 938 <trans-unit id="2392488717875840729">
921 <source>User</source> 939 <source>User</source>
922 <target>Gebruiker</target> 940 <target>Gebruiker</target>
@@ -927,64 +945,64 @@ Aanmelden</target>
927 <source>Username or email address</source> 945 <source>Username or email address</source>
928 <target>Gebruikersnaam of e-mailadres</target> 946 <target>Gebruikersnaam of e-mailadres</target>
929 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 947 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
948 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
949 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
950 <context-group purpose="location">
951 <context context-type="sourcefile">src/app/+login/login.component.html</context>
952 <context context-type="linenumber">33,34</context>
953 </context-group>
930 </trans-unit> 954 </trans-unit>
931 <trans-unit id="1431416938026210429"> 955 <trans-unit id="1431416938026210429">
932 <source>Password</source> 956 <source>Password</source>
933 <target>Wachtwoord</target> 957 <target>Wachtwoord</target>
934 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 958
935 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 959
936 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 960
937 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 961
938 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 962
939 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 963
940 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 964
941 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 965
942 </trans-unit> 966 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
943 <trans-unit id="8715156686857791956" datatype="html"> 967 <trans-unit id="8715156686857791956" datatype="html">
944 <source>Click here to reset your password</source> 968 <source>Click here to reset your password</source>
945 <target state="translated">Hier klikken om je wachtwoord opnieuw in te stellen</target> 969 <target state="translated">Hier klikken om je wachtwoord opnieuw in te stellen</target>
946 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 970
947 </trans-unit><trans-unit id="892063502898494584" datatype="html"> 971 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html">
948 <source>I forgot my password</source><target state="new">I forgot my password</target> 972 <source>I forgot my password</source><target state="new">I forgot my password</target>
949 <context-group purpose="location"> 973
950 <context context-type="sourcefile">src/app/+login/login.component.html</context> 974 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
951 <context context-type="linenumber">47</context>
952 </context-group>
953 </trans-unit>
954 <trans-unit id="2101170466365500913" datatype="html"> 975 <trans-unit id="2101170466365500913" datatype="html">
955 <source>Logging into an account lets you publish content</source> 976 <source>Logging into an account lets you publish content</source>
956 <target state="translated">Door in te loggen op een account, kunt u content publiceren</target> 977 <target state="translated">Door in te loggen op een account, kunt u content publiceren</target>
957 <context-group purpose="location"> 978
958 <context context-type="sourcefile">src/app/+login/login.component.html</context> 979 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
959 <context context-type="linenumber">56,57</context>
960 </context-group>
961 </trans-unit>
962 <trans-unit id="2454050363478003966"> 980 <trans-unit id="2454050363478003966">
963 <source>Login</source> 981 <source>Login</source>
964 <target>Aanmelden</target> 982 <target>Aanmelden</target>
965 983
966 984
967 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 985 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
968 <trans-unit id="3183213940445113677" datatype="html"> 986 <trans-unit id="3183213940445113677" datatype="html">
969 <source>Or sign in with</source> 987 <source>Or sign in with</source>
970 <target state="translated">Of meld je aan met</target> 988 <target state="translated">Of meld je aan met</target>
971 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 989
972 </trans-unit> 990 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
973 <trans-unit id="3238209155172574367"> 991 <trans-unit id="3238209155172574367">
974 <source>Forgot your password</source> 992 <source>Forgot your password</source>
975 <target>Jouw wachtwoord vergeten</target> 993 <target>Jouw wachtwoord vergeten</target>
976 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 994
977 </trans-unit> 995 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
978 <trans-unit id="87327320394367488" datatype="html"> 996 <trans-unit id="87327320394367488" datatype="html">
979 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 997 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
980 <target state="translated">Het spijt ons, maar we kunnen je wachtwoord niet herstellen. De beheerder van je exemplaar van PeerTube heeft het PeerTube-emailsysteem niet ingesteld.</target> 998 <target state="translated">Het spijt ons, maar we kunnen je wachtwoord niet herstellen. De beheerder van je exemplaar van PeerTube heeft het PeerTube-emailsysteem niet ingesteld.</target>
981 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 999
982 </trans-unit> 1000 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
983 <trans-unit id="3188014010833256853" datatype="html"> 1001 <trans-unit id="3188014010833256853" datatype="html">
984 <source>Enter your email address and we will send you a link to reset your password.</source> 1002 <source>Enter your email address and we will send you a link to reset your password.</source>
985 <target state="translated">Je email-adres invoeren en je krijgt van ons instructies om je wachtwoord opnieuw in te stellen.</target> 1003 <target state="translated">Je email-adres invoeren en je krijgt van ons instructies om je wachtwoord opnieuw in te stellen.</target>
986 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1004
987 </trans-unit> 1005 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
988 <trans-unit id="1190256911880544559" datatype="html"> 1006 <trans-unit id="1190256911880544559" datatype="html">
989 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1007 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
990The link will expire within 1 hour.</source> 1008The link will expire within 1 hour.</source>
@@ -994,26 +1012,26 @@ The link will expire within 1 hour.</source>
994 <trans-unit id="4768749765465246664"> 1012 <trans-unit id="4768749765465246664">
995 <source>Email</source> 1013 <source>Email</source>
996 <target>E-mail</target> 1014 <target>E-mail</target>
997 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1015
998 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1016
999 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1017
1000 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1018
1001 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1019
1002 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1020
1003 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1021
1004 </trans-unit> 1022 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1005 <trans-unit id="3967269098753656610"> 1023 <trans-unit id="3967269098753656610">
1006 <source>Email address</source> 1024 <source>Email address</source>
1007 <target>E-mailadres</target> 1025 <target>E-mailadres</target>
1008 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1026
1009 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1027
1010 </trans-unit> 1028 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1011 <trans-unit id="7808756054397155068" datatype="html"> 1029 <trans-unit id="7808756054397155068" datatype="html">
1012 <source>Reset</source> 1030 <source>Reset</source>
1013 <target state="translated">Herinstellen</target> 1031 <target state="translated">Herinstellen</target>
1014 <note priority="1" from="description">Password reset button</note> 1032 <note priority="1" from="description">Password reset button</note>
1015 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1033
1016 </trans-unit> 1034 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1017 1035
1018 <trans-unit id="4319634264526091601" datatype="html"> 1036 <trans-unit id="4319634264526091601" datatype="html">
1019 <source>on this instance</source> 1037 <source>on this instance</source>
@@ -1337,7 +1355,7 @@ Geen resultaten gevonden</target>
1337 <target>Account maken</target> 1355 <target>Account maken</target>
1338 1356
1339 1357
1340 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1358 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1341 1359
1342 <trans-unit id="3058024914967508975" datatype="html"> 1360 <trans-unit id="3058024914967508975" datatype="html">
1343 <source>My videos</source> 1361 <source>My videos</source>
@@ -1394,7 +1412,7 @@ Geen resultaten gevonden</target>
1394 <source>VIDEOS</source> 1412 <source>VIDEOS</source>
1395 <target state="translated">VIDEO'S</target> 1413 <target state="translated">VIDEO'S</target>
1396 1414
1397 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html"> 1415 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html">
1398 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target> 1416 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target>
1399 1417
1400 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html"> 1418 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html">
@@ -1469,7 +1487,7 @@ Geen resultaten gevonden</target>
1469 <source>I'm a teapot</source> 1487 <source>I'm a teapot</source>
1470 <target state="translated">Ik ben een theepot</target> 1488 <target state="translated">Ik ben een theepot</target>
1471 1489
1472 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit> 1490 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1473 <trans-unit id="1597262876035959248" datatype="html"> 1491 <trans-unit id="1597262876035959248" datatype="html">
1474 <source>That's an error.</source> 1492 <source>That's an error.</source>
1475 <target state="translated">Dat is een fout.</target> 1493 <target state="translated">Dat is een fout.</target>
@@ -1550,8 +1568,8 @@ Geen resultaten gevonden</target>
1550 <trans-unit id="2971365540217107489" datatype="html"> 1568 <trans-unit id="2971365540217107489" datatype="html">
1551 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1569 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1552 <target state="translated">Media te groot voor de server. Gelieve je beheerder te contacteren als je de groottelimiet wil verhogen.</target> 1570 <target state="translated">Media te groot voor de server. Gelieve je beheerder te contacteren als je de groottelimiet wil verhogen.</target>
1553 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1571
1554 </trans-unit> 1572 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1555 1573
1556 <trans-unit id="5131854469652959713" datatype="html"> 1574 <trans-unit id="5131854469652959713" datatype="html">
1557 <source>GLOBAL SEARCH</source> 1575 <source>GLOBAL SEARCH</source>
@@ -2239,7 +2257,7 @@ Gefeliciteerd, de video achter
2239 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2257 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2240 <source>Upload on hold</source><target state="new">Upload on hold</target> 2258 <source>Upload on hold</source><target state="new">Upload on hold</target>
2241 2259
2242 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2260 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2243 <trans-unit id="285180972645018518" datatype="html"> 2261 <trans-unit id="285180972645018518" datatype="html">
2244 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2262 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2245 <target state="translated">Sorry, uploaden is uitgeschakeld voor je account. Als je een video wil toevoegen, dan moet een beheerder je quotum ontgrendelen.</target> 2263 <target state="translated">Sorry, uploaden is uitgeschakeld voor je account. Als je een video wil toevoegen, dan moet een beheerder je quotum ontgrendelen.</target>
@@ -2951,11 +2969,7 @@ Je kan nu al informatie toevoegen over deze video.
2951 <target>ID</target> 2969 <target>ID</target>
2952 2970
2953 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit> 2971 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit>
2954 <trans-unit id="2265605798180116441"> 2972
2955 <source>Follower handle</source>
2956 <target>Naam volger</target>
2957
2958 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
2959 <trans-unit id="5911214550882917183"> 2973 <trans-unit id="5911214550882917183">
2960 <source>State</source> 2974 <source>State</source>
2961 <target>Status</target> 2975 <target>Status</target>
@@ -3021,11 +3035,7 @@ Je kan nu al informatie toevoegen over deze video.
3021 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target> 3035 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target>
3022 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3036 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3023 </trans-unit> 3037 </trans-unit>
3024 <trans-unit id="6641024648411549335"> 3038
3025 <source>Host</source>
3026 <target>Host</target>
3027
3028 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3029 <trans-unit id="6571718060636962350" datatype="html"> 3039 <trans-unit id="6571718060636962350" datatype="html">
3030 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3040 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3031 <target state="translated">Surpluskopie toegelaten <x id="START_TAG_P-SORTICON" ctype="x-p-sortIcon" equiv-text="&lt;p-sortIcon>"/> <x id="CLOSE_TAG_P-SORTICON" ctype="x-p-sortIcon" equiv-text="&lt;/p-sortIcon>"/></target> 3041 <target state="translated">Surpluskopie toegelaten <x id="START_TAG_P-SORTICON" ctype="x-p-sortIcon" equiv-text="&lt;p-sortIcon>"/> <x id="CLOSE_TAG_P-SORTICON" ctype="x-p-sortIcon" equiv-text="&lt;/p-sortIcon>"/></target>
@@ -3033,7 +3043,7 @@ Je kan nu al informatie toevoegen over deze video.
3033 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html"> 3043 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html">
3034 <source>Unfollow</source><target state="new">Unfollow</target> 3044 <source>Unfollow</source><target state="new">Unfollow</target>
3035 3045
3036 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3046 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3037 <trans-unit id="8246779176913476983" datatype="html"> 3047 <trans-unit id="8246779176913476983" datatype="html">
3038 <source>Open instance in a new tab</source> 3048 <source>Open instance in a new tab</source>
3039 <target state="translated">Exemplaar van PeerTube openen in nieuwe tab</target> 3049 <target state="translated">Exemplaar van PeerTube openen in nieuwe tab</target>
@@ -3045,25 +3055,18 @@ Je kan nu al informatie toevoegen over deze video.
3045 <source>No host found matching current filters.</source> 3055 <source>No host found matching current filters.</source>
3046 <target state="translated">Geen host gevonden op basis van huidige filters.</target> 3056 <target state="translated">Geen host gevonden op basis van huidige filters.</target>
3047 3057
3048 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3058 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3049 <trans-unit id="7274241885665071790" datatype="html"> 3059 <trans-unit id="7274241885665071790" datatype="html">
3050 <source>Your instance is not following anyone.</source> 3060 <source>Your instance is not following anyone.</source>
3051 <target state="translated">Je exemplaar van PeerTube volgt niemand.</target> 3061 <target state="translated">Je exemplaar van PeerTube volgt niemand.</target>
3052 3062
3053 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3063 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3054 <trans-unit id="4774348799569692380" datatype="html"> 3064 <trans-unit id="4774348799569692380" datatype="html">
3055 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3065 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3056 <target state="translated">Nu te zien: <x id="INTERPOLATION" equiv-text="{{'{first}'}}"/> tot <x id="INTERPOLATION_1" equiv-text="{{'{last}'}}"/> van <x id="INTERPOLATION_2" equiv-text="{{'{totalRecords}'}}"/> hosts</target> 3066 <target state="translated">Nu te zien: <x id="INTERPOLATION" equiv-text="{{'{first}'}}"/> tot <x id="INTERPOLATION_1" equiv-text="{{'{last}'}}"/> van <x id="INTERPOLATION_2" equiv-text="{{'{totalRecords}'}}"/> hosts</target>
3057 3067
3058 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3068 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3059 <trans-unit id="6275803119759621687" datatype="html"> 3069 <trans-unit id="9216117865911519658" datatype="html">
3060 <source>Follow domains</source>
3061 <target state="translated">Domeinen volgen</target>
3062
3063 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit><trans-unit id="1268699198448750610" datatype="html">
3064 <source>Follow instances</source><target state="new">Follow instances</target>
3065
3066 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3067 <source>Action</source><target state="new">Action</target> 3070 <source>Action</source><target state="new">Action</target>
3068 3071
3069 3072
@@ -3113,7 +3116,7 @@ Je kan nu al informatie toevoegen over deze video.
3113 3116
3114 3117
3115 3118
3116 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group></trans-unit> 3119 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3117 <trans-unit id="5428411040014095392" datatype="html"> 3120 <trans-unit id="5428411040014095392" datatype="html">
3118 <source>e.g. jane_doe</source> 3121 <source>e.g. jane_doe</source>
3119 <target state="translated">vb. piet_fluwijn</target> 3122 <target state="translated">vb. piet_fluwijn</target>
@@ -3141,9 +3144,9 @@ Je kan nu al informatie toevoegen over deze video.
3141 <trans-unit id="4145496584631696119"> 3144 <trans-unit id="4145496584631696119">
3142 <source>Role</source> 3145 <source>Role</source>
3143 <target>Rol</target> 3146 <target>Rol</target>
3144 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3147
3145 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3148
3146 </trans-unit> 3149 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3147 <trans-unit id="7046347992315328430" datatype="html"> 3150 <trans-unit id="7046347992315328430" datatype="html">
3148 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3151 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3149 <target state="translated">Transcoding is ingeschakeld. De videoquota houden enkel rekening met de grootte van de <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong>"/>originele <x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong>"/> video. <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br/>"/> Deze gebruiker kan maximaal ~ <x id="INTERPOLATION" equiv-text="{{ computeQuotaWithTranscoding() | bytes: 0 }}"/> uploaden. </target> 3152 <target state="translated">Transcoding is ingeschakeld. De videoquota houden enkel rekening met de grootte van de <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong>"/>originele <x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong>"/> video. <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br/>"/> Deze gebruiker kan maximaal ~ <x id="INTERPOLATION" equiv-text="{{ computeQuotaWithTranscoding() | bytes: 0 }}"/> uploaden. </target>
@@ -3158,15 +3161,9 @@ Je kan nu al informatie toevoegen over deze video.
3158 3161
3159 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html"> 3162 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html">
3160 <source>Auth plugin</source><target state="new">Auth plugin</target> 3163 <source>Auth plugin</source><target state="new">Auth plugin</target>
3161 <context-group purpose="location"> 3164
3162 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3165
3163 <context context-type="linenumber">188</context> 3166 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit><trans-unit id="588099657508661970" datatype="html">
3164 </context-group>
3165 <context-group purpose="location">
3166 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3167 <context context-type="linenumber">188</context>
3168 </context-group>
3169 </trans-unit><trans-unit id="588099657508661970" datatype="html">
3170 <source>None (local authentication)</source><target state="new">None (local authentication)</target> 3167 <source>None (local authentication)</source><target state="new">None (local authentication)</target>
3171 <context-group purpose="location"> 3168 <context-group purpose="location">
3172 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3169 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
@@ -3409,7 +3406,13 @@ Je kan nu al informatie toevoegen over deze video.
3409 3406
3410 3407
3411 3408
3412 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit> 3409 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3410 <source>Follower</source><target state="new">Follower</target>
3411 <context-group purpose="location">
3412 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3413 <context context-type="linenumber">24</context>
3414 </context-group>
3415 </trans-unit>
3413 <trans-unit id="4691552465058437520" datatype="html"> 3416 <trans-unit id="4691552465058437520" datatype="html">
3414 <source>Commented video</source> 3417 <source>Commented video</source>
3415 <target state="translated">Video met reacties</target> 3418 <target state="translated">Video met reacties</target>
@@ -3698,7 +3701,7 @@ Je kan nu al informatie toevoegen over deze video.
3698 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3701 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3699 <target state="translated">Het lijkt dat je niet op een HTTPS-server zit. Om een andere server te volgen is TLS op jouw webserver vereist.</target> 3702 <target state="translated">Het lijkt dat je niet op een HTTPS-server zit. Om een andere server te volgen is TLS op jouw webserver vereist.</target>
3700 3703
3701 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3704 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3702 <trans-unit id="4058814854824495833" datatype="html"> 3705 <trans-unit id="4058814854824495833" datatype="html">
3703 <source>Mute domains</source> 3706 <source>Mute domains</source>
3704 <target state="translated">Domeinen dempen</target> 3707 <target state="translated">Domeinen dempen</target>
@@ -5571,11 +5574,8 @@ Vraag e-mail voor accountverificatie aan</target>
5571 5574
5572 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5575 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5573 <source>CHANNELS</source><target state="new">CHANNELS</target> 5576 <source>CHANNELS</source><target state="new">CHANNELS</target>
5574 <context-group purpose="location"> 5577
5575 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5578 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5576 <context context-type="linenumber">82</context>
5577 </context-group>
5578 </trans-unit>
5579 5579
5580 <trans-unit id="3666829335406793239"> 5580 <trans-unit id="3666829335406793239">
5581 <source>This account does not have channels.</source> 5581 <source>This account does not have channels.</source>
@@ -5609,7 +5609,13 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
5609channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 5609channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
5610 <target state="translated">Wil je echt <x id="PH"/> verwijderen? Dat verwijdert <x id="PH_1"/> video's die in dit kanaal geüpload zijn. Je kan ook geen nieuw kanaal meer maken met dezelfde naam. (<x id="PH_2"/>)!</target> 5610 <target state="translated">Wil je echt <x id="PH"/> verwijderen? Dat verwijdert <x id="PH_1"/> video's die in dit kanaal geüpload zijn. Je kan ook geen nieuw kanaal meer maken met dezelfde naam. (<x id="PH_2"/>)!</target>
5611 5611
5612 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5612 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5613 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5614 <context-group purpose="location">
5615 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5616 <context context-type="linenumber">48</context>
5617 </context-group>
5618 </trans-unit>
5613 <trans-unit id="5387007581996837469" datatype="html"> 5619 <trans-unit id="5387007581996837469" datatype="html">
5614 <source>My Channels</source> 5620 <source>My Channels</source>
5615 <target state="translated">Mijn Kanalen</target> 5621 <target state="translated">Mijn Kanalen</target>
@@ -6125,12 +6131,12 @@ Account aanmaken</target>
6125 <source>Your message has been sent.</source> 6131 <source>Your message has been sent.</source>
6126 <target>Jouw bericht is verstuurd.</target> 6132 <target>Jouw bericht is verstuurd.</target>
6127 6133
6128 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6134 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6129 <trans-unit id="2072135752262464360"> 6135 <trans-unit id="2072135752262464360">
6130 <source>You already sent this form recently</source> 6136 <source>You already sent this form recently</source>
6131 <target>U hebt dit formulier onlangs al verzonden</target> 6137 <target>U hebt dit formulier onlangs al verzonden</target>
6132 6138
6133 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6139 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6134 6140
6135 <trans-unit id="819067926858619041" datatype="html"> 6141 <trans-unit id="819067926858619041" datatype="html">
6136 <source>Account videos</source> 6142 <source>Account videos</source>
@@ -6173,12 +6179,12 @@ Account aanmaken</target>
6173 <source><x id="PH"/> direct account followers </source> 6179 <source><x id="PH"/> direct account followers </source>
6174 <target state="translated"><x id="PH"/> directe accountvolgers </target> 6180 <target state="translated"><x id="PH"/> directe accountvolgers </target>
6175 6181
6176 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit> 6182 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6177 <trans-unit id="6250999352462648289" datatype="html"> 6183 <trans-unit id="6250999352462648289" datatype="html">
6178 <source>Report this account</source> 6184 <source>Report this account</source>
6179 <target state="translated">Deze account melden</target> 6185 <target state="translated">Deze account melden</target>
6180 6186
6181 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6187 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6182 6188
6183 <trans-unit id="1504521795586863905" datatype="html"> 6189 <trans-unit id="1504521795586863905" datatype="html">
6184 <source>VIDEOS</source> 6190 <source>VIDEOS</source>
@@ -6192,27 +6198,19 @@ Account aanmaken</target>
6192 <target>Gebruikersnaam gekopieerd</target> 6198 <target>Gebruikersnaam gekopieerd</target>
6193 6199
6194 6200
6195 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit> 6201 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6196 <trans-unit id="9221735175659318025" datatype="html"> 6202 <trans-unit id="9221735175659318025" datatype="html">
6197 <source>1 subscriber</source> 6203 <source>1 subscriber</source>
6198 <target state="translated">1 abonnee</target> 6204 <target state="translated">1 abonnee</target>
6199 6205
6200 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit> 6206 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6201 <trans-unit id="4097331874769079975" datatype="html"> 6207 <trans-unit id="4097331874769079975" datatype="html">
6202 <source><x id="PH"/> subscribers</source> 6208 <source><x id="PH"/> subscribers</source>
6203 <target state="translated"><x id="PH"/> abonnees</target> 6209 <target state="translated"><x id="PH"/> abonnees</target>
6204 6210
6205 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6211 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6206 <trans-unit id="4682675125751819107" datatype="html"> 6212
6207 <source>Instances you follow</source> 6213
6208 <target state="translated">Exemplaren van PeerTube die je volgt</target>
6209
6210 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6211 <trans-unit id="8899833753704589712" datatype="html">
6212 <source>Instances following you</source>
6213 <target state="translated">Exemplaren van PeerTube die je volgen</target>
6214
6215 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6216 <trans-unit id="1035838766454786107" datatype="html"> 6214 <trans-unit id="1035838766454786107" datatype="html">
6217 <source>Audio-only</source> 6215 <source>Audio-only</source>
6218 <target state="translated">Enkel audio</target> 6216 <target state="translated">Enkel audio</target>
@@ -6260,7 +6258,13 @@ Account aanmaken</target>
6260 <source>Auto (via ffmpeg)</source> 6258 <source>Auto (via ffmpeg)</source>
6261 <target>Automatisch (via ffmpeg)</target> 6259 <target>Automatisch (via ffmpeg)</target>
6262 6260
6263 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit> 6261 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="3642770981085338761" datatype="html">
6262 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6263 <context-group purpose="location">
6264 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6265 <context context-type="linenumber">3</context>
6266 </context-group>
6267 </trans-unit>
6264 <trans-unit id="931255636742351800" datatype="html"> 6268 <trans-unit id="931255636742351800" datatype="html">
6265 <source>No limit</source> 6269 <source>No limit</source>
6266 <target state="translated">Onbeperkt</target> 6270 <target state="translated">Onbeperkt</target>
@@ -6390,18 +6394,34 @@ Account aanmaken</target>
6390 <trans-unit id="2127446333083057097" datatype="html"> 6394 <trans-unit id="2127446333083057097" datatype="html">
6391 <source>Domain is required.</source> 6395 <source>Domain is required.</source>
6392 <target state="translated">Domein is vereist.</target> 6396 <target state="translated">Domein is vereist.</target>
6393 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6397
6394 </trans-unit> 6398 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6395 <trans-unit id="6780793142903080663" datatype="html"> 6399 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6396 <source>Domains entered are invalid.</source> 6400 <context-group purpose="location">
6397 <target state="translated">De ingevoerde domeinen zijn ongeldig.</target> 6401 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6398 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6402 <context context-type="linenumber">93</context>
6399 </trans-unit> 6403 </context-group>
6400 <trans-unit id="5886492514458202177" datatype="html"> 6404 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6401 <source>Domains entered contain duplicates.</source> 6405 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6402 <target state="translated">De ingevoerde domeinen bevatten dubbels.</target> 6406 <context-group purpose="location">
6403 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6407 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6408 <context context-type="linenumber">94</context>
6409 </context-group>
6410 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6411 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6412 <context-group purpose="location">
6413 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6414 <context context-type="linenumber">102</context>
6415 </context-group>
6416 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6417 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6418 <context-group purpose="location">
6419 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6420 <context context-type="linenumber">103</context>
6421 </context-group>
6404 </trans-unit> 6422 </trans-unit>
6423
6424
6405 <trans-unit id="240806681889331244"> 6425 <trans-unit id="240806681889331244">
6406 <source>Unlimited</source> 6426 <source>Unlimited</source>
6407 <target>Oneindig</target> 6427 <target>Oneindig</target>
@@ -6555,24 +6575,50 @@ Account aanmaken</target>
6555 <source><x id="PH"/> removed from instance followers </source> 6575 <source><x id="PH"/> removed from instance followers </source>
6556 <target><x id="PH"/> verwijderd uit volgers van dit exemplaar van PeerTube </target> 6576 <target><x id="PH"/> verwijderd uit volgers van dit exemplaar van PeerTube </target>
6557 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6577 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6578 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6579 <source>Follow</source><target state="new">Follow</target>
6580 <context-group purpose="location">
6581 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6582 <context context-type="linenumber">3</context>
6583 </context-group>
6584 <context-group purpose="location">
6585 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6586 <context context-type="linenumber">37</context>
6587 </context-group>
6588 <context-group purpose="location">
6589 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6590 <context context-type="linenumber">18</context>
6591 </context-group>
6592 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6593 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6594 <context-group purpose="location">
6595 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6596 <context context-type="linenumber">11</context>
6597 </context-group>
6558 </trans-unit> 6598 </trans-unit>
6559 <trans-unit id="2740793005745065895"> 6599 <trans-unit id="2740793005745065895">
6560 <source><x id="PH"/> is not valid </source> 6600 <source><x id="PH"/> is not valid </source>
6561 <target> 6601 <target>
6562 <x id="PH"/> is niet valide 6602 <x id="PH"/> is niet valide
6563 </target> 6603 </target>
6564 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6604
6565 </trans-unit> 6605 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6566 <trans-unit id="2355066641781598196"> 6606 <trans-unit id="2355066641781598196">
6567 <source>Follow request(s) sent!</source> 6607 <source>Follow request(s) sent!</source>
6568 <target>Volgverzoek(en) verstuurd!</target> 6608 <target>Volgverzoek(en) verstuurd!</target>
6569 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6609
6610 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6611 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6612 <context-group purpose="location">
6613 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6614 <context context-type="linenumber">3</context>
6615 </context-group>
6570 </trans-unit> 6616 </trans-unit>
6571 <trans-unit id="4245720728052819482"> 6617 <trans-unit id="4245720728052819482">
6572 <source>Do you really want to unfollow <x id="PH"/>?</source> 6618 <source>Do you really want to unfollow <x id="PH"/>?</source>
6573 <target>Wil je echt <x id="PH"/> niet meer volgen?</target> 6619 <target>Wil je echt <x id="PH"/> niet meer volgen?</target>
6574 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6620
6575 </trans-unit> 6621 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6576 <trans-unit id="9160510009013134726"> 6622 <trans-unit id="9160510009013134726">
6577 <source>Unfollow</source> 6623 <source>Unfollow</source>
6578 <target>Onvolgen</target> 6624 <target>Onvolgen</target>
@@ -6581,8 +6627,8 @@ Account aanmaken</target>
6581 <trans-unit id="3935234189109112926"> 6627 <trans-unit id="3935234189109112926">
6582 <source>You are not following <x id="PH"/> anymore.</source> 6628 <source>You are not following <x id="PH"/> anymore.</source>
6583 <target>Je volgt <x id="PH"/> niet meer.</target> 6629 <target>Je volgt <x id="PH"/> niet meer.</target>
6584 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6630
6585 </trans-unit> 6631 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6586 <trans-unit id="2593763089859685916"> 6632 <trans-unit id="2593763089859685916">
6587 <source>enabled</source> 6633 <source>enabled</source>
6588 <target>ingeschakeld</target> 6634 <target>ingeschakeld</target>
@@ -7030,9 +7076,9 @@ Account aanmaken</target>
7030 <trans-unit id="1519954996184640001"> 7076 <trans-unit id="1519954996184640001">
7031 <source>Error</source> 7077 <source>Error</source>
7032 <target>Fout</target> 7078 <target>Fout</target>
7033 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7079
7034 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7080
7035 </trans-unit> 7081 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7036 <trans-unit id="5076187961693950167" datatype="html"> 7082 <trans-unit id="5076187961693950167" datatype="html">
7037 <source>Standard logs</source> 7083 <source>Standard logs</source>
7038 <target state="translated">Standaardlogboeken</target> 7084 <target state="translated">Standaardlogboeken</target>
@@ -7073,16 +7119,8 @@ Account aanmaken</target>
7073 <target>Update gebruikerswachtwoord</target> 7119 <target>Update gebruikerswachtwoord</target>
7074 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7120 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7075 </trans-unit> 7121 </trans-unit>
7076 <trans-unit id="177544274549739411" datatype="html"> 7122
7077 <source>Following list</source> 7123
7078 <target state="translated">Lijst van gevolgden</target>
7079 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7080 </trans-unit>
7081 <trans-unit id="8092429110007204784" datatype="html">
7082 <source>Followers list</source>
7083 <target state="translated">Lijst van volgers</target>
7084 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7085 </trans-unit>
7086 <trans-unit id="780323526182667308" datatype="html"> 7124 <trans-unit id="780323526182667308" datatype="html">
7087 <source>User <x id="PH"/> updated.</source> 7125 <source>User <x id="PH"/> updated.</source>
7088 <target state="translated">Gebruiker <x id="PH"/> bijgewerkt.</target> 7126 <target state="translated">Gebruiker <x id="PH"/> bijgewerkt.</target>
@@ -7118,16 +7156,8 @@ Account aanmaken</target>
7118 <target state="translated">Federatie</target> 7156 <target state="translated">Federatie</target>
7119 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7157 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7120 </trans-unit> 7158 </trans-unit>
7121 <trans-unit id="4682675125751819107" datatype="html"> 7159
7122 <source>Instances you follow</source> 7160
7123 <target state="translated">Exemplaren van PeerTube die je volgt</target>
7124 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7125 </trans-unit>
7126 <trans-unit id="8899833753704589712" datatype="html">
7127 <source>Instances following you</source>
7128 <target state="translated">Exemplaren van PeerTube die jou volgen</target>
7129 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7130 </trans-unit>
7131 <trans-unit id="3767259920053407667" datatype="html"> 7161 <trans-unit id="3767259920053407667" datatype="html">
7132 <source>Videos will be deleted, comments will be tombstoned.</source> 7162 <source>Videos will be deleted, comments will be tombstoned.</source>
7133 <target state="translated">Videos zullen worden verwijderd, reacties als verwijderd gemarkeerd.</target> 7163 <target state="translated">Videos zullen worden verwijderd, reacties als verwijderd gemarkeerd.</target>
@@ -7158,7 +7188,25 @@ Account aanmaken</target>
7158 <target>Zet E-mail als Geverifieerd</target> 7188 <target>Zet E-mail als Geverifieerd</target>
7159 7189
7160 7190
7161 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7191 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7192 <source>Created</source><target state="new">Created</target>
7193 <context-group purpose="location">
7194 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7195 <context context-type="linenumber">115</context>
7196 </context-group>
7197 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7198 <source>Daily quota</source><target state="new">Daily quota</target>
7199 <context-group purpose="location">
7200 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7201 <context context-type="linenumber">120</context>
7202 </context-group>
7203 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7204 <source>Last login</source><target state="new">Last login</target>
7205 <context-group purpose="location">
7206 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7207 <context context-type="linenumber">122</context>
7208 </context-group>
7209 </trans-unit>
7162 <trans-unit id="3403978719736970622"> 7210 <trans-unit id="3403978719736970622">
7163 <source>You cannot ban root.</source> 7211 <source>You cannot ban root.</source>
7164 <target>Je kan root niet verbannen.</target> 7212 <target>Je kan root niet verbannen.</target>
@@ -7450,12 +7498,12 @@ Account aanmaken</target>
7450 <source>Video channel <x id="PH"/> created.</source> 7498 <source>Video channel <x id="PH"/> created.</source>
7451 <target>Videokanaal <x id="PH"/> gemaakt.</target> 7499 <target>Videokanaal <x id="PH"/> gemaakt.</target>
7452 7500
7453 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7501 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7454 <trans-unit id="8723777130353305761"> 7502 <trans-unit id="8723777130353305761">
7455 <source>This name already exists on this instance.</source> 7503 <source>This name already exists on this instance.</source>
7456 <target>Deze naam bestaat al op dit exemplaar van PeerTube.</target> 7504 <target>Deze naam bestaat al op dit exemplaar van PeerTube.</target>
7457 7505
7458 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7506 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7459 <trans-unit id="7589345916094713536"> 7507 <trans-unit id="7589345916094713536">
7460 <source>Video channel <x id="PH"/> updated.</source> 7508 <source>Video channel <x id="PH"/> updated.</source>
7461 <target>Videokanaal <x id="PH"/> bijgewerkt.</target> 7509 <target>Videokanaal <x id="PH"/> bijgewerkt.</target>
@@ -7470,11 +7518,7 @@ Account aanmaken</target>
7470 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 7518 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7471 7519
7472 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7520 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7473 <trans-unit id="2575302837003821736"> 7521
7474 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7475 <target>Typ alsjeblieft de weergavenaam van het videokanaal ( <x id="PH"/>) om te bevestigen</target>
7476
7477 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7478 <trans-unit id="624066830180032195"> 7522 <trans-unit id="624066830180032195">
7479 <source>Video channel <x id="PH"/> deleted.</source> 7523 <source>Video channel <x id="PH"/> deleted.</source>
7480 <target>Videokanaal <x id="PH"/> verwijderd.</target> 7524 <target>Videokanaal <x id="PH"/> verwijderd.</target>
@@ -7613,6 +7657,12 @@ Account aanmaken</target>
7613 <source>Ownership change request sent.</source> 7657 <source>Ownership change request sent.</source>
7614 <target>Eigenaarsveranderingsaanvrag gestuurd.</target> 7658 <target>Eigenaarsveranderingsaanvrag gestuurd.</target>
7615 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 7659 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
7660 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
7661 <source>Sort by</source><target state="new">Sort by</target>
7662 <context-group purpose="location">
7663 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7664 <context context-type="linenumber">26</context>
7665 </context-group>
7616 </trans-unit> 7666 </trans-unit>
7617 <trans-unit id="3245220240937722814"> 7667 <trans-unit id="3245220240937722814">
7618 <source>My channels</source> 7668 <source>My channels</source>
@@ -7713,7 +7763,7 @@ Account aanmaken</target>
7713 <target>Abonneren op account</target> 7763 <target>Abonneren op account</target>
7714 7764
7715 7765
7716 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 7766 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
7717 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 7767 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
7718 <context-group purpose="location"> 7768 <context-group purpose="location">
7719 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 7769 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -7759,34 +7809,34 @@ Account aanmaken</target>
7759 <trans-unit id="3779524668013120370"> 7809 <trans-unit id="3779524668013120370">
7760 <source>Go to my subscriptions</source> 7810 <source>Go to my subscriptions</source>
7761 <target>Naar mijn abonnementen gaan</target> 7811 <target>Naar mijn abonnementen gaan</target>
7762 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 7812
7763 </trans-unit> 7813 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
7764 <trans-unit id="1136469849928650779"> 7814 <trans-unit id="1136469849928650779">
7765 <source>Go to my videos</source> 7815 <source>Go to my videos</source>
7766 <target>Ga naar mijn videos</target> 7816 <target>Ga naar mijn videos</target>
7767 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 7817
7768 </trans-unit> 7818 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
7769 <trans-unit id="7836683738999600376"> 7819 <trans-unit id="7836683738999600376">
7770 <source>Go to my imports</source> 7820 <source>Go to my imports</source>
7771 <target>Ga naar mijn imports</target> 7821 <target>Ga naar mijn imports</target>
7772 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 7822
7773 </trans-unit> 7823 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
7774 <trans-unit id="7511292153332773503"> 7824 <trans-unit id="7511292153332773503">
7775 <source>Go to my channels</source> 7825 <source>Go to my channels</source>
7776 <target>Ga naar mijn kanalen</target> 7826 <target>Ga naar mijn kanalen</target>
7777 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 7827
7778 </trans-unit> 7828 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
7779 <trans-unit id="2013324644839511073" datatype="html"> 7829 <trans-unit id="2013324644839511073" datatype="html">
7780 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 7830 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
7781Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 7831Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
7782 <target state="translated">Kan OAuth Client-aanmeldinformatie niet ophalen: <x id="PH"/>. Vergewis je ervan dat je PeerTube (config/ map) juist hebt geconfigureerd, in het bijzonder het onderdeel "webserver".</target> 7832 <target state="translated">Kan OAuth Client-aanmeldinformatie niet ophalen: <x id="PH"/>. Vergewis je ervan dat je PeerTube (config/ map) juist hebt geconfigureerd, in het bijzonder het onderdeel "webserver".</target>
7783 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 7833
7784 </trans-unit> 7834 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
7785 <trans-unit id="375263728166936544"> 7835 <trans-unit id="375263728166936544">
7786 <source>You need to reconnect.</source> 7836 <source>You need to reconnect.</source>
7787 <target>Je moet opnieuw verbinden.</target> 7837 <target>Je moet opnieuw verbinden.</target>
7788 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 7838
7789 </trans-unit> 7839 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
7790 <trans-unit id="2206638022166154361"> 7840 <trans-unit id="2206638022166154361">
7791 <source>Keyboard Shortcuts:</source> 7841 <source>Keyboard Shortcuts:</source>
7792 <target>Sneltoetsen:</target> 7842 <target>Sneltoetsen:</target>
@@ -7797,6 +7847,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7797 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 7847 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7798 <context context-type="linenumber">98</context> 7848 <context context-type="linenumber">98</context>
7799 </context-group> 7849 </context-group>
7850 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
7851 <source>In my library</source><target state="new">In my library</target>
7852 <context-group purpose="location">
7853 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7854 <context context-type="linenumber">104</context>
7855 </context-group>
7800 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 7856 </trans-unit><trans-unit id="232050922346936574" datatype="html">
7801 <source>Trending</source><target state="new">Trending</target> 7857 <source>Trending</source><target state="new">Trending</target>
7802 7858
@@ -7820,38 +7876,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7820 <source>Incorrect username or password.</source> 7876 <source>Incorrect username or password.</source>
7821 <target>Incorrecte gebruikersnaam of wachtwoord.</target> 7877 <target>Incorrecte gebruikersnaam of wachtwoord.</target>
7822 7878
7823 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 7879 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
7824 <trans-unit id="6974874606619467663" datatype="html"> 7880 <trans-unit id="6974874606619467663" datatype="html">
7825 <source>Your account is blocked.</source> 7881 <source>Your account is blocked.</source>
7826 <target state="translated">Je account is geblokkeerd.</target> 7882 <target state="translated">Je account is geblokkeerd.</target>
7827 7883
7828 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 7884 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
7829 <trans-unit id="7939914198003891823" datatype="html"> 7885 <trans-unit id="7939914198003891823" datatype="html">
7830 <source>any language</source> 7886 <source>any language</source>
7831 <target state="translated">gelijk welke taal</target> 7887 <target state="translated">gelijk welke taal</target>
7832 7888
7833 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 7889 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
7834 7890
7835 <trans-unit id="5633144232269377096" datatype="html"> 7891 <trans-unit id="5633144232269377096" datatype="html">
7836 <source>hide</source> 7892 <source>hide</source>
7837 <target state="translated">verbergen</target> 7893 <target state="translated">verbergen</target>
7838 7894
7839 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 7895 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
7840 <trans-unit id="8603861867909474404" datatype="html"> 7896 <trans-unit id="8603861867909474404" datatype="html">
7841 <source>blur</source> 7897 <source>blur</source>
7842 <target state="translated">vervagen</target> 7898 <target state="translated">vervagen</target>
7843 7899
7844 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 7900 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
7845 <trans-unit id="4534458451100881847" datatype="html"> 7901 <trans-unit id="4534458451100881847" datatype="html">
7846 <source>display</source> 7902 <source>display</source>
7847 <target state="translated">weergeven</target> 7903 <target state="translated">weergeven</target>
7848 7904
7849 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 7905 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
7850 <trans-unit id="4467323362722952678" datatype="html"> 7906 <trans-unit id="4467323362722952678" datatype="html">
7851 <source>Unknown</source> 7907 <source>Unknown</source>
7852 <target state="translated">Niet gekend</target> 7908 <target state="translated">Niet gekend</target>
7853 7909
7854 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 7910 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
7855 <trans-unit id="8781423666414310853"> 7911 <trans-unit id="8781423666414310853">
7856 <source>Your password has been successfully reset!</source> 7912 <source>Your password has been successfully reset!</source>
7857 <target>Jouw wachtwoord is succesvol gereset!</target> 7913 <target>Jouw wachtwoord is succesvol gereset!</target>
@@ -9379,18 +9435,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9379 <trans-unit id="968295009933361070"> 9435 <trans-unit id="968295009933361070">
9380 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9436 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9381 <target>Te veel pogingen. Probeer alstublieft opnieuw na <x id="PH"/> minuten.</target> 9437 <target>Te veel pogingen. Probeer alstublieft opnieuw na <x id="PH"/> minuten.</target>
9382 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9438
9383 </trans-unit> 9439 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9384 <trans-unit id="4965472196059235310"> 9440 <trans-unit id="4965472196059235310">
9385 <source>Too many attempts, please try again later.</source> 9441 <source>Too many attempts, please try again later.</source>
9386 <target>Te vaak geprobeerd, probeer alstublieft later.</target> 9442 <target>Te vaak geprobeerd, probeer alstublieft later.</target>
9387 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9443
9388 </trans-unit> 9444 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9389 <trans-unit id="1693549688987384699"> 9445 <trans-unit id="1693549688987384699">
9390 <source>Server error. Please retry later.</source> 9446 <source>Server error. Please retry later.</source>
9391 <target>Serverfout. Probeer later alstublieft weer.</target> 9447 <target>Serverfout. Probeer later alstublieft weer.</target>
9392 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9448
9393 </trans-unit> 9449 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9394 <trans-unit id="5927402622550505067" datatype="html"> 9450 <trans-unit id="5927402622550505067" datatype="html">
9395 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9451 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9396 <target state="translated">Geabonneerd op alle huidige kanalen van <x id="PH"/>. U krijgt meldingen van al zijn of haar nieuwe video's.</target> 9452 <target state="translated">Geabonneerd op alle huidige kanalen van <x id="PH"/>. U krijgt meldingen van al zijn of haar nieuwe video's.</target>
@@ -9963,35 +10019,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9963 <target>Jouw video is geupload naar jouw account en is privé. 10019 <target>Jouw video is geupload naar jouw account en is privé.
9964 </target> 10020 </target>
9965 10021
9966 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10022 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
9967 <trans-unit id="5699822024600815733"> 10023 <trans-unit id="5699822024600815733">
9968 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10024 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
9969 <target>Maar geassocieerde data(tags, beschrijving...) zullen verloren raken, weet je zeker dat je deze pagina wilt verlaten?</target> 10025 <target>Maar geassocieerde data(tags, beschrijving...) zullen verloren raken, weet je zeker dat je deze pagina wilt verlaten?</target>
9970 10026
9971 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10027 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
9972 <trans-unit id="1219739004043110649"> 10028 <trans-unit id="1219739004043110649">
9973 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10029 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
9974 <target>Jouw video is nog niet geupload, weet je zeker dat je deze pagina wilt verlaten?</target> 10030 <target>Jouw video is nog niet geupload, weet je zeker dat je deze pagina wilt verlaten?</target>
9975 10031
9976 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10032 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
9977 <trans-unit id="6932865105766151309" datatype="html"> 10033 <trans-unit id="6932865105766151309" datatype="html">
9978 <source>Upload</source> 10034 <source>Upload</source>
9979 <target state="translated">Uploaden</target> 10035 <target state="translated">Uploaden</target>
9980 10036
9981 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10037 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
9982 <trans-unit id="8278735427925094503"> 10038 <trans-unit id="8278735427925094503">
9983 <source>Upload <x id="PH"/> </source> 10039 <source>Upload <x id="PH"/> </source>
9984 <target> 10040 <target>
9985 <x id="PH"/> uploaden 10041 <x id="PH"/> uploaden
9986 </target> 10042 </target>
9987 10043
9988 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10044 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
9989 10045
9990 <trans-unit id="5981816353437801748"> 10046 <trans-unit id="5981816353437801748">
9991 <source>Video published.</source> 10047 <source>Video published.</source>
9992 <target>Video gepubliceerd.</target> 10048 <target>Video gepubliceerd.</target>
9993 10049
9994 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10050 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
9995 10051
9996 10052
9997 <trans-unit id="764164089183618119"> 10053 <trans-unit id="764164089183618119">
@@ -10039,27 +10095,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10039 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10095 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10040 <target state="translated">Deze video is niet beschikbaar op dit exemplaar van PeerTube. Wil je doorverwezen worden naar het originele exemplaar &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10096 <target state="translated">Deze video is niet beschikbaar op dit exemplaar van PeerTube. Wil je doorverwezen worden naar het originele exemplaar &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10041 10097
10042 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10098 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10043 <trans-unit id="5761611056224181752" datatype="html"> 10099 <trans-unit id="5761611056224181752" datatype="html">
10044 <source>Redirection</source> 10100 <source>Redirection</source>
10045 <target state="translated">Doorverwijzing</target> 10101 <target state="translated">Doorverwijzing</target>
10046 10102
10047 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10103 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10048 <trans-unit id="8858527736400081688"> 10104 <trans-unit id="8858527736400081688">
10049 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10105 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10050 <target>Deze video bevat volwassen of expliciete inhoud. Weet je zeker dat je hem wilt kijken?</target> 10106 <target>Deze video bevat volwassen of expliciete inhoud. Weet je zeker dat je hem wilt kijken?</target>
10051 10107
10052 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10108 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10053 <trans-unit id="3937119019020041049"> 10109 <trans-unit id="3937119019020041049">
10054 <source>Mature or explicit content</source> 10110 <source>Mature or explicit content</source>
10055 <target>Volwassen of expliciete content</target> 10111 <target>Volwassen of expliciete content</target>
10056 10112
10057 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10113 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10058 <trans-unit id="1755474755114288376" datatype="html"> 10114 <trans-unit id="1755474755114288376" datatype="html">
10059 <source>Up Next</source> 10115 <source>Up Next</source>
10060 <target state="translated">Volgende</target> 10116 <target state="translated">Volgende</target>
10061 10117
10062 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10118 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10063 <trans-unit id="2159130950882492111" datatype="html"> 10119 <trans-unit id="2159130950882492111" datatype="html">
10064 <source>Cancel</source> 10120 <source>Cancel</source>
10065 <target state="translated">Annuleren</target> 10121 <target state="translated">Annuleren</target>
@@ -10069,62 +10125,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10069 <source>Autoplay is suspended</source> 10125 <source>Autoplay is suspended</source>
10070 <target state="translated">Automatisch afspelen is opgeschort</target> 10126 <target state="translated">Automatisch afspelen is opgeschort</target>
10071 10127
10072 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10128 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10073 <trans-unit id="7895294730547405228" datatype="html"> 10129 <trans-unit id="7895294730547405228" datatype="html">
10074 <source>Enter/exit fullscreen (requires player focus)</source> 10130 <source>Enter/exit fullscreen (requires player focus)</source>
10075 <target state="translated">Volledig scherm ingaan/uitgaan (vereist focus op afspeler)</target> 10131 <target state="translated">Volledig scherm ingaan/uitgaan (vereist focus op afspeler)</target>
10076 10132
10077 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10133 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10078 <trans-unit id="7618388257165864759" datatype="html"> 10134 <trans-unit id="7618388257165864759" datatype="html">
10079 <source>Play/Pause the video (requires player focus)</source> 10135 <source>Play/Pause the video (requires player focus)</source>
10080 <target state="translated">De video afspelen/pauseren (vereist focus op de afspeler)</target> 10136 <target state="translated">De video afspelen/pauseren (vereist focus op de afspeler)</target>
10081 10137
10082 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10138 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10083 <trans-unit id="7761890399634216630" datatype="html"> 10139 <trans-unit id="7761890399634216630" datatype="html">
10084 <source>Mute/unmute the video (requires player focus)</source> 10140 <source>Mute/unmute the video (requires player focus)</source>
10085 <target state="translated">De video dempen of niet meer dempen (vereist focus op de speler)</target> 10141 <target state="translated">De video dempen of niet meer dempen (vereist focus op de speler)</target>
10086 10142
10087 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10143 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10088 <trans-unit id="5996585232248234904" datatype="html"> 10144 <trans-unit id="5996585232248234904" datatype="html">
10089 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10145 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10090 <target state="translated">Naar een percentage van de video springen: 0 is 0% and 9 is 90% (vereist focus op de speler)</target> 10146 <target state="translated">Naar een percentage van de video springen: 0 is 0% and 9 is 90% (vereist focus op de speler)</target>
10091 10147
10092 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10148 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10093 <trans-unit id="3748765405903319998" datatype="html"> 10149 <trans-unit id="3748765405903319998" datatype="html">
10094 <source>Increase the volume (requires player focus)</source> 10150 <source>Increase the volume (requires player focus)</source>
10095 <target state="translated">Het volume verhogen (vereist focus op de speler)</target> 10151 <target state="translated">Het volume verhogen (vereist focus op de speler)</target>
10096 10152
10097 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10153 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10098 <trans-unit id="5810704036407159982" datatype="html"> 10154 <trans-unit id="5810704036407159982" datatype="html">
10099 <source>Decrease the volume (requires player focus)</source> 10155 <source>Decrease the volume (requires player focus)</source>
10100 <target state="translated">Het volume verlagen (vereist focus op de speler)</target> 10156 <target state="translated">Het volume verlagen (vereist focus op de speler)</target>
10101 10157
10102 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10158 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10103 <trans-unit id="2622048822548065691" datatype="html"> 10159 <trans-unit id="2622048822548065691" datatype="html">
10104 <source>Seek the video forward (requires player focus)</source> 10160 <source>Seek the video forward (requires player focus)</source>
10105 <target state="translated">Vooruitspringen in de video (vereist focus op de speler)</target> 10161 <target state="translated">Vooruitspringen in de video (vereist focus op de speler)</target>
10106 10162
10107 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10163 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10108 <trans-unit id="6540078205109221153" datatype="html"> 10164 <trans-unit id="6540078205109221153" datatype="html">
10109 <source>Seek the video backward (requires player focus)</source> 10165 <source>Seek the video backward (requires player focus)</source>
10110 <target state="translated">Achteruit springen in de video (vereist focus op de speler)</target> 10166 <target state="translated">Achteruit springen in de video (vereist focus op de speler)</target>
10111 10167
10112 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10168 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10113 <trans-unit id="1956491957766210808" datatype="html"> 10169 <trans-unit id="1956491957766210808" datatype="html">
10114 <source>Increase playback rate (requires player focus)</source> 10170 <source>Increase playback rate (requires player focus)</source>
10115 <target state="translated">Afspelen versnellen (vereist focus op de speler)</target> 10171 <target state="translated">Afspelen versnellen (vereist focus op de speler)</target>
10116 10172
10117 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10173 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10118 <trans-unit id="5495529997674803186" datatype="html"> 10174 <trans-unit id="5495529997674803186" datatype="html">
10119 <source>Decrease playback rate (requires player focus)</source> 10175 <source>Decrease playback rate (requires player focus)</source>
10120 <target state="translated">Afspelen vertragen (vereist focus op de speler)</target> 10176 <target state="translated">Afspelen vertragen (vereist focus op de speler)</target>
10121 10177
10122 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10178 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10123 <trans-unit id="3178343147230721210" datatype="html"> 10179 <trans-unit id="3178343147230721210" datatype="html">
10124 <source>Navigate in the video frame by frame (requires player focus)</source> 10180 <source>Navigate in the video frame by frame (requires player focus)</source>
10125 <target state="translated">Frame per frame door de video navigeren (vereist focus op de speler)</target> 10181 <target state="translated">Frame per frame door de video navigeren (vereist focus op de speler)</target>
10126 10182
10127 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10183 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10128 <trans-unit id="8025996572234182184"> 10184 <trans-unit id="8025996572234182184">
10129 <source>Like the video</source> 10185 <source>Like the video</source>
10130 <target>Like de video</target> 10186 <target>Like de video</target>
diff --git a/client/src/locale/angular.oc.xlf b/client/src/locale/angular.oc.xlf
index f1dc9a13d..d3cc91167 100644
--- a/client/src/locale/angular.oc.xlf
+++ b/client/src/locale/angular.oc.xlf
@@ -231,7 +231,7 @@
231 231
232 232
233 233
234 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 234 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
235 <trans-unit id="1486537403020619891" datatype="html"> 235 <trans-unit id="1486537403020619891" datatype="html">
236 <source>My watch history</source> 236 <source>My watch history</source>
237 <target state="new">My watch history</target> 237 <target state="new">My watch history</target>
@@ -307,23 +307,23 @@
307 <trans-unit id="5674286808255988565"> 307 <trans-unit id="5674286808255988565">
308 <source>Create</source> 308 <source>Create</source>
309 <target>Crear</target> 309 <target>Crear</target>
310 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 310
311 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 311
312 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 312
313 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 313
314 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 314
315 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 315
316 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 316
317 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 317
318 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 318
319 </trans-unit> 319 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
320 <trans-unit id="1006562256968398209" datatype="html"> 320 <trans-unit id="1006562256968398209" datatype="html">
321 <source>video</source> 321 <source>video</source>
322 <target state="translated">vidèo</target> 322 <target state="translated">vidèo</target>
323 323
324 324
325 325
326 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 326 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
327 <trans-unit id="6438815964972582865" datatype="html"> 327 <trans-unit id="6438815964972582865" datatype="html">
328 <source>The following link contains a private token and should not be shared with anyone.</source> 328 <source>The following link contains a private token and should not be shared with anyone.</source>
329 <target state="new"> The following link contains a private token and should not be shared with anyone. </target> 329 <target state="new"> The following link contains a private token and should not be shared with anyone. </target>
@@ -397,12 +397,12 @@
397 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 397 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
398 <target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 398 <target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
399 399
400 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit> 400 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
401 <trans-unit id="7873395933409147217" datatype="html"> 401 <trans-unit id="7873395933409147217" datatype="html">
402 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 402 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
403 <target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 403 <target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
404 404
405 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 405 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
406 <trans-unit id="5235042777215655908" datatype="html"> 406 <trans-unit id="5235042777215655908" datatype="html">
407 <source>subtitles</source> 407 <source>subtitles</source>
408 <target state="new">subtitles</target> 408 <target state="new">subtitles</target>
@@ -859,10 +859,10 @@
859 <trans-unit id="2602586221576511475"> 859 <trans-unit id="2602586221576511475">
860 <source>Video quota</source> 860 <source>Video quota</source>
861 <target>Quòta vidèo</target> 861 <target>Quòta vidèo</target>
862 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 862
863 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 863
864 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 864
865 </trans-unit> 865 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
866 <trans-unit id="1502595455339510144"> 866 <trans-unit id="1502595455339510144">
867 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 867 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
868 <target> 868 <target>
@@ -947,6 +947,30 @@
947 <target>Federacion</target> 947 <target>Federacion</target>
948 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 948 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
949 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 949 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
950 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
951 <source>Following</source><target state="new">Following</target>
952 <context-group purpose="location">
953 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
954 <context context-type="linenumber">29</context>
955 </context-group>
956 <context-group purpose="location">
957 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
958 <context context-type="linenumber">31</context>
959 </context-group>
960 <context-group purpose="location">
961 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
962 <context context-type="linenumber">28</context>
963 </context-group>
964 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
965 <source>Followers</source><target state="new">Followers</target>
966 <context-group purpose="location">
967 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
968 <context context-type="linenumber">34</context>
969 </context-group>
970 <context-group purpose="location">
971 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
972 <context context-type="linenumber">37</context>
973 </context-group>
950 </trans-unit> 974 </trans-unit>
951 <trans-unit id="3541687134897970106"> 975 <trans-unit id="3541687134897970106">
952 <source>followers</source> 976 <source>followers</source>
@@ -1028,7 +1052,7 @@
1028 1052
1029 1053
1030 1054
1031 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 1055 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
1032 <trans-unit id="3616223838716839702"> 1056 <trans-unit id="3616223838716839702">
1033 <source>Ban this user</source> 1057 <source>Ban this user</source>
1034 <target>Fòrabandir aqueste utilizaire</target> 1058 <target>Fòrabandir aqueste utilizaire</target>
@@ -1101,19 +1125,13 @@
1101 <trans-unit id="7252854992688790751" datatype="html"> 1125 <trans-unit id="7252854992688790751" datatype="html">
1102 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1126 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1103 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1127 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1104 <context-group purpose="location"> 1128
1105 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1129 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1106 <context context-type="linenumber">60,62</context>
1107 </context-group>
1108 </trans-unit>
1109 <trans-unit id="7215649348148521605" datatype="html"> 1130 <trans-unit id="7215649348148521605" datatype="html">
1110 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1131 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1111 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1132 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1112 <context-group purpose="location"> 1133
1113 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1134 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1114 <context context-type="linenumber">65,67</context>
1115 </context-group>
1116 </trans-unit>
1117 <trans-unit id="2392488717875840729"> 1135 <trans-unit id="2392488717875840729">
1118 <source>User</source> 1136 <source>User</source>
1119 <target>Utilizaire</target> 1137 <target>Utilizaire</target>
@@ -1124,67 +1142,67 @@
1124 <source>Username or email address</source> 1142 <source>Username or email address</source>
1125 <target>Nom d’utilizaire o adreça electronica</target> 1143 <target>Nom d’utilizaire o adreça electronica</target>
1126 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1144 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1145 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1146 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1147 <context-group purpose="location">
1148 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1149 <context context-type="linenumber">33,34</context>
1150 </context-group>
1127 </trans-unit> 1151 </trans-unit>
1128 <trans-unit id="1431416938026210429"> 1152 <trans-unit id="1431416938026210429">
1129 <source>Password</source> 1153 <source>Password</source>
1130 <target>Senhal</target> 1154 <target>Senhal</target>
1131 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1155
1132 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1156
1133 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1157
1134 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1158
1135 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1159
1136 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1160
1137 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1161
1138 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1162
1139 </trans-unit> 1163 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1140 <trans-unit id="8715156686857791956" datatype="html"> 1164 <trans-unit id="8715156686857791956" datatype="html">
1141 <source>Click here to reset your password</source> 1165 <source>Click here to reset your password</source>
1142 <target state="translated">Clicatz aquí per reïnicializar vòstre senhal</target> 1166 <target state="translated">Clicatz aquí per reïnicializar vòstre senhal</target>
1143 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1167
1144 </trans-unit> 1168 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1145 <trans-unit id="892063502898494584" datatype="html"> 1169 <trans-unit id="892063502898494584" datatype="html">
1146 <source>I forgot my password</source> 1170 <source>I forgot my password</source>
1147 <target state="new">I forgot my password</target> 1171 <target state="new">I forgot my password</target>
1148 <context-group purpose="location"> 1172
1149 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1173 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1150 <context context-type="linenumber">47</context>
1151 </context-group>
1152 </trans-unit>
1153 <trans-unit id="2101170466365500913" datatype="html"> 1174 <trans-unit id="2101170466365500913" datatype="html">
1154 <source>Logging into an account lets you publish content</source> 1175 <source>Logging into an account lets you publish content</source>
1155 <target state="new"> Logging into an account lets you publish content </target> 1176 <target state="new"> Logging into an account lets you publish content </target>
1156 <context-group purpose="location"> 1177
1157 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1178 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1158 <context context-type="linenumber">56,57</context>
1159 </context-group>
1160 </trans-unit>
1161 <trans-unit id="2454050363478003966"> 1179 <trans-unit id="2454050363478003966">
1162 <source>Login</source> 1180 <source>Login</source>
1163 <target>Connexion</target> 1181 <target>Connexion</target>
1164 1182
1165 1183
1166 1184
1167 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1185 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1168 <trans-unit id="3183213940445113677" datatype="html"> 1186 <trans-unit id="3183213940445113677" datatype="html">
1169 <source>Or sign in with</source> 1187 <source>Or sign in with</source>
1170 <target state="new">Or sign in with</target> 1188 <target state="new">Or sign in with</target>
1171 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1189
1172 </trans-unit> 1190 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1173 <trans-unit id="3238209155172574367"> 1191 <trans-unit id="3238209155172574367">
1174 <source>Forgot your password</source> 1192 <source>Forgot your password</source>
1175 <target>Senhal oblidat</target> 1193 <target>Senhal oblidat</target>
1176 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1194
1177 </trans-unit> 1195 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1178 <trans-unit id="87327320394367488" datatype="html"> 1196 <trans-unit id="87327320394367488" datatype="html">
1179 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1197 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1180 <target state="translated">O planhèm, podètz pas restaurar lo senhal perque l'administrator de l'instància configurèt pas lo sistèm de corrièl de PeerTube.</target> 1198 <target state="translated">O planhèm, podètz pas restaurar lo senhal perque l'administrator de l'instància configurèt pas lo sistèm de corrièl de PeerTube.</target>
1181 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1199
1182 </trans-unit> 1200 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1183 <trans-unit id="3188014010833256853" datatype="html"> 1201 <trans-unit id="3188014010833256853" datatype="html">
1184 <source>Enter your email address and we will send you a link to reset your password.</source> 1202 <source>Enter your email address and we will send you a link to reset your password.</source>
1185 <target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1203 <target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1186 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1204
1187 </trans-unit> 1205 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1188 <trans-unit id="1190256911880544559" datatype="html"> 1206 <trans-unit id="1190256911880544559" datatype="html">
1189 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1207 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1190The link will expire within 1 hour.</source> 1208The link will expire within 1 hour.</source>
@@ -1195,26 +1213,26 @@ The link will expire within 1 hour.</target>
1195 <trans-unit id="4768749765465246664"> 1213 <trans-unit id="4768749765465246664">
1196 <source>Email</source> 1214 <source>Email</source>
1197 <target>Corrièl</target> 1215 <target>Corrièl</target>
1198 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1216
1199 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1217
1200 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1218
1201 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1219
1202 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1220
1203 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1221
1204 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1222
1205 </trans-unit> 1223 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1206 <trans-unit id="3967269098753656610"> 1224 <trans-unit id="3967269098753656610">
1207 <source>Email address</source> 1225 <source>Email address</source>
1208 <target>Adreça de corrièl</target> 1226 <target>Adreça de corrièl</target>
1209 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1227
1210 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1228
1211 </trans-unit> 1229 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1212 <trans-unit id="7808756054397155068" datatype="html"> 1230 <trans-unit id="7808756054397155068" datatype="html">
1213 <source>Reset</source> 1231 <source>Reset</source>
1214 <target state="new">Reset</target> 1232 <target state="new">Reset</target>
1215 <note priority="1" from="description">Password reset button</note> 1233 <note priority="1" from="description">Password reset button</note>
1216 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1234
1217 </trans-unit> 1235 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1218 <trans-unit id="4319634264526091601" datatype="html"> 1236 <trans-unit id="4319634264526091601" datatype="html">
1219 <source>on this instance</source> 1237 <source>on this instance</source>
1220 <target state="new">on this instance</target> 1238 <target state="new">on this instance</target>
@@ -1581,7 +1599,7 @@ The link will expire within 1 hour.</target>
1581 <target>Crear un compte</target> 1599 <target>Crear un compte</target>
1582 1600
1583 1601
1584 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1602 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1585 1603
1586 <trans-unit id="3058024914967508975" datatype="html"> 1604 <trans-unit id="3058024914967508975" datatype="html">
1587 <source>My videos</source> 1605 <source>My videos</source>
@@ -1649,7 +1667,7 @@ The link will expire within 1 hour.</target>
1649 1667
1650 1668
1651 1669
1652 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 1670 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1653 <trans-unit id="667372110624203230" datatype="html"> 1671 <trans-unit id="667372110624203230" datatype="html">
1654 <source>Import jobs concurrency</source> 1672 <source>Import jobs concurrency</source>
1655 <target state="new">Import jobs concurrency</target> 1673 <target state="new">Import jobs concurrency</target>
@@ -1727,8 +1745,8 @@ The link will expire within 1 hour.</target>
1727 <trans-unit id="4424964105331349857" datatype="html"> 1745 <trans-unit id="4424964105331349857" datatype="html">
1728 <source>I'm a teapot</source> 1746 <source>I'm a teapot</source>
1729 <target state="new">I'm a teapot</target> 1747 <target state="new">I'm a teapot</target>
1730 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1748
1731 </trans-unit> 1749 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1732 <trans-unit id="1597262876035959248" datatype="html"> 1750 <trans-unit id="1597262876035959248" datatype="html">
1733 <source>That's an error.</source> 1751 <source>That's an error.</source>
1734 <target state="new">That's an error.</target> 1752 <target state="new">That's an error.</target>
@@ -1821,8 +1839,8 @@ The link will expire within 1 hour.</target>
1821 <trans-unit id="2971365540217107489" datatype="html"> 1839 <trans-unit id="2971365540217107489" datatype="html">
1822 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1840 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1823 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1841 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1824 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1842
1825 </trans-unit> 1843 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1826 1844
1827 <trans-unit id="5131854469652959713" datatype="html"> 1845 <trans-unit id="5131854469652959713" datatype="html">
1828 <source>GLOBAL SEARCH</source> 1846 <source>GLOBAL SEARCH</source>
@@ -2588,7 +2606,7 @@ The link will expire within 1 hour.</target>
2588 <source>Upload on hold</source> 2606 <source>Upload on hold</source>
2589 <target state="new">Upload on hold</target> 2607 <target state="new">Upload on hold</target>
2590 2608
2591 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2609 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2592 <trans-unit id="285180972645018518" datatype="html"> 2610 <trans-unit id="285180972645018518" datatype="html">
2593 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2611 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2594 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2612 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3314,11 +3332,7 @@ The link will expire within 1 hour.</target>
3314 <target>ID</target> 3332 <target>ID</target>
3315 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3333 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3316 </trans-unit> 3334 </trans-unit>
3317 <trans-unit id="2265605798180116441"> 3335
3318 <source>Follower handle</source>
3319 <target>Gestion dels seguidors</target>
3320 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3321 </trans-unit>
3322 <trans-unit id="5911214550882917183"> 3336 <trans-unit id="5911214550882917183">
3323 <source>State</source> 3337 <source>State</source>
3324 <target>Estatisticas</target> 3338 <target>Estatisticas</target>
@@ -3393,11 +3407,7 @@ The link will expire within 1 hour.</target>
3393 </target> 3407 </target>
3394 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3408 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3395 </trans-unit> 3409 </trans-unit>
3396 <trans-unit id="6641024648411549335"> 3410
3397 <source>Host</source>
3398 <target>Òst</target>
3399 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3400 </trans-unit>
3401 <trans-unit id="6571718060636962350" datatype="html"> 3411 <trans-unit id="6571718060636962350" datatype="html">
3402 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3412 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3403 <target state="new">Redundancy allowed 3413 <target state="new">Redundancy allowed
@@ -3409,9 +3419,9 @@ The link will expire within 1 hour.</target>
3409 <trans-unit id="9160510009013134726" datatype="html"> 3419 <trans-unit id="9160510009013134726" datatype="html">
3410 <source>Unfollow</source> 3420 <source>Unfollow</source>
3411 <target state="new">Unfollow</target> 3421 <target state="new">Unfollow</target>
3412 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3422
3413 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3423
3414 </trans-unit> 3424 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3415 <trans-unit id="8246779176913476983" datatype="html"> 3425 <trans-unit id="8246779176913476983" datatype="html">
3416 <source>Open instance in a new tab</source> 3426 <source>Open instance in a new tab</source>
3417 <target state="new">Open instance in a new tab</target> 3427 <target state="new">Open instance in a new tab</target>
@@ -3422,13 +3432,13 @@ The link will expire within 1 hour.</target>
3422 <trans-unit id="9132918641931433659" datatype="html"> 3432 <trans-unit id="9132918641931433659" datatype="html">
3423 <source>No host found matching current filters.</source> 3433 <source>No host found matching current filters.</source>
3424 <target state="new">No host found matching current filters.</target> 3434 <target state="new">No host found matching current filters.</target>
3425 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3435
3426 </trans-unit> 3436 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3427 <trans-unit id="7274241885665071790" datatype="html"> 3437 <trans-unit id="7274241885665071790" datatype="html">
3428 <source>Your instance is not following anyone.</source> 3438 <source>Your instance is not following anyone.</source>
3429 <target state="new">Your instance is not following anyone.</target> 3439 <target state="new">Your instance is not following anyone.</target>
3430 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3440
3431 </trans-unit> 3441 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3432 <trans-unit id="4774348799569692380" datatype="html"> 3442 <trans-unit id="4774348799569692380" datatype="html">
3433 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3443 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3434 <target state="new">Showing 3444 <target state="new">Showing
@@ -3438,16 +3448,8 @@ The link will expire within 1 hour.</target>
3438 </target> 3448 </target>
3439 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3449 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3440 </trans-unit> 3450 </trans-unit>
3441 <trans-unit id="6275803119759621687" datatype="html"> 3451
3442 <source>Follow domains</source> 3452
3443 <target state="new">Follow domains</target>
3444 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3445 </trans-unit>
3446 <trans-unit id="1268699198448750610" datatype="html">
3447 <source>Follow instances</source>
3448 <target state="new">Follow instances</target>
3449 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3450 </trans-unit>
3451 <trans-unit id="9216117865911519658" datatype="html"> 3453 <trans-unit id="9216117865911519658" datatype="html">
3452 <source>Action</source> 3454 <source>Action</source>
3453 <target state="new">Action</target> 3455 <target state="new">Action</target>
@@ -3497,11 +3499,11 @@ The link will expire within 1 hour.</target>
3497 <trans-unit id="5248717555542428023"> 3499 <trans-unit id="5248717555542428023">
3498 <source>Username</source> 3500 <source>Username</source>
3499 <target>Nom d’utilizaire</target> 3501 <target>Nom d’utilizaire</target>
3500 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3502
3501 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3503
3502 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3504
3503 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3505
3504 </trans-unit> 3506 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3505 <trans-unit id="5428411040014095392" datatype="html"> 3507 <trans-unit id="5428411040014095392" datatype="html">
3506 <source>e.g. jane_doe</source> 3508 <source>e.g. jane_doe</source>
3507 <target state="new">e.g. jane_doe</target> 3509 <target state="new">e.g. jane_doe</target>
@@ -3531,9 +3533,9 @@ The link will expire within 1 hour.</target>
3531 <trans-unit id="4145496584631696119"> 3533 <trans-unit id="4145496584631696119">
3532 <source>Role</source> 3534 <source>Role</source>
3533 <target>Ròtle</target> 3535 <target>Ròtle</target>
3534 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3536
3535 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3537
3536 </trans-unit> 3538 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3537 <trans-unit id="7046347992315328430" datatype="html"> 3539 <trans-unit id="7046347992315328430" datatype="html">
3538 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3540 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3539 <target state="new"> 3541 <target state="new">
@@ -3558,15 +3560,9 @@ The link will expire within 1 hour.</target>
3558 <trans-unit id="2622255144026150901" datatype="html"> 3560 <trans-unit id="2622255144026150901" datatype="html">
3559 <source>Auth plugin</source> 3561 <source>Auth plugin</source>
3560 <target state="new">Auth plugin</target> 3562 <target state="new">Auth plugin</target>
3561 <context-group purpose="location"> 3563
3562 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3564
3563 <context context-type="linenumber">188</context> 3565 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3564 </context-group>
3565 <context-group purpose="location">
3566 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3567 <context context-type="linenumber">188</context>
3568 </context-group>
3569 </trans-unit>
3570 <trans-unit id="588099657508661970" datatype="html"> 3566 <trans-unit id="588099657508661970" datatype="html">
3571 <source>None (local authentication)</source> 3567 <source>None (local authentication)</source>
3572 <target state="new">None (local authentication)</target> 3568 <target state="new">None (local authentication)</target>
@@ -3831,6 +3827,12 @@ The link will expire within 1 hour.</target>
3831 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3827 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3832 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3828 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3833 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3829 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3830 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3831 <source>Follower</source><target state="new">Follower</target>
3832 <context-group purpose="location">
3833 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3834 <context context-type="linenumber">24</context>
3835 </context-group>
3834 </trans-unit> 3836 </trans-unit>
3835 <trans-unit id="4691552465058437520" datatype="html"> 3837 <trans-unit id="4691552465058437520" datatype="html">
3836 <source>Commented video</source> 3838 <source>Commented video</source>
@@ -4171,8 +4173,8 @@ The link will expire within 1 hour.</target>
4171 <target state="new"> 4173 <target state="new">
4172 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 4174 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
4173 </target> 4175 </target>
4174 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 4176
4175 </trans-unit> 4177 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
4176 <trans-unit id="4058814854824495833" datatype="html"> 4178 <trans-unit id="4058814854824495833" datatype="html">
4177 <source>Mute domains</source> 4179 <source>Mute domains</source>
4178 <target state="new">Mute domains</target> 4180 <target state="new">Mute domains</target>
@@ -6176,11 +6178,8 @@ color: red;
6176 <trans-unit id="5512878593724620692" datatype="html"> 6178 <trans-unit id="5512878593724620692" datatype="html">
6177 <source>CHANNELS</source> 6179 <source>CHANNELS</source>
6178 <target state="new">CHANNELS</target> 6180 <target state="new">CHANNELS</target>
6179 <context-group purpose="location"> 6181
6180 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 6182 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
6181 <context context-type="linenumber">82</context>
6182 </context-group>
6183 </trans-unit>
6184 <trans-unit id="3666829335406793239"> 6183 <trans-unit id="3666829335406793239">
6185 <source>This account does not have channels.</source> 6184 <source>This account does not have channels.</source>
6186 <target>Aqueste compte a pas cap de cadena.</target> 6185 <target>Aqueste compte a pas cap de cadena.</target>
@@ -6225,6 +6224,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6225It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 6224It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
6226channel with the same name (<x id="PH_2"/>)!</target> 6225channel with the same name (<x id="PH_2"/>)!</target>
6227 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 6226 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
6227 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
6228 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
6229 <context-group purpose="location">
6230 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
6231 <context context-type="linenumber">48</context>
6232 </context-group>
6228 </trans-unit> 6233 </trans-unit>
6229 <trans-unit id="5387007581996837469" datatype="html"> 6234 <trans-unit id="5387007581996837469" datatype="html">
6230 <source>My Channels</source> 6235 <source>My Channels</source>
@@ -6817,12 +6822,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6817 <source>Your message has been sent.</source> 6822 <source>Your message has been sent.</source>
6818 <target>Messatge enviat.</target> 6823 <target>Messatge enviat.</target>
6819 6824
6820 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6825 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6821 <trans-unit id="2072135752262464360"> 6826 <trans-unit id="2072135752262464360">
6822 <source>You already sent this form recently</source> 6827 <source>You already sent this form recently</source>
6823 <target>Avètz ja enviat aqueste formulari fa pas gaire</target> 6828 <target>Avètz ja enviat aqueste formulari fa pas gaire</target>
6824 6829
6825 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6830 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6826 <trans-unit id="819067926858619041" datatype="html"> 6831 <trans-unit id="819067926858619041" datatype="html">
6827 <source>Account videos</source> 6832 <source>Account videos</source>
6828 <target state="new">Account videos</target> 6833 <target state="new">Account videos</target>
@@ -6868,13 +6873,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6868 <target state="new"> 6873 <target state="new">
6869 <x id="PH"/> direct account followers 6874 <x id="PH"/> direct account followers
6870 </target> 6875 </target>
6871 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6876
6872 </trans-unit> 6877 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6873 <trans-unit id="6250999352462648289" datatype="html"> 6878 <trans-unit id="6250999352462648289" datatype="html">
6874 <source>Report this account</source> 6879 <source>Report this account</source>
6875 <target state="new">Report this account</target> 6880 <target state="new">Report this account</target>
6876 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6881
6877 </trans-unit> 6882 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6878 <trans-unit id="1504521795586863905" datatype="html"> 6883 <trans-unit id="1504521795586863905" datatype="html">
6879 <source>VIDEOS</source> 6884 <source>VIDEOS</source>
6880 <target state="new">VIDEOS</target> 6885 <target state="new">VIDEOS</target>
@@ -6884,31 +6889,21 @@ channel with the same name (<x id="PH_2"/>)!</target>
6884 <trans-unit id="25349740244798533"> 6889 <trans-unit id="25349740244798533">
6885 <source>Username copied</source> 6890 <source>Username copied</source>
6886 <target>Nom d’utilizaire copiat</target> 6891 <target>Nom d’utilizaire copiat</target>
6887 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6892
6888 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6893
6889 </trans-unit> 6894 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6890 <trans-unit id="9221735175659318025" datatype="html"> 6895 <trans-unit id="9221735175659318025" datatype="html">
6891 <source>1 subscriber</source> 6896 <source>1 subscriber</source>
6892 <target state="new">1 subscriber</target> 6897 <target state="new">1 subscriber</target>
6893 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6898
6894 </trans-unit> 6899 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6895 <trans-unit id="4097331874769079975" datatype="html"> 6900 <trans-unit id="4097331874769079975" datatype="html">
6896 <source><x id="PH"/> subscribers</source> 6901 <source><x id="PH"/> subscribers</source>
6897 <target state="new"><x id="PH"/> subscribers</target> 6902 <target state="new"><x id="PH"/> subscribers</target>
6898 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6903
6899 </trans-unit> 6904 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6900 <trans-unit id="4682675125751819107" datatype="html"> 6905
6901 <source>Instances you follow</source> 6906
6902 <target state="translated">Las instàncias que seguissètz</target>
6903 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6904 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6905 </trans-unit>
6906 <trans-unit id="8899833753704589712" datatype="html">
6907 <source>Instances following you</source>
6908 <target state="translated">Las instàncias que vos seguisson</target>
6909 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6910 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6911 </trans-unit>
6912 <trans-unit id="1035838766454786107" datatype="html"> 6907 <trans-unit id="1035838766454786107" datatype="html">
6913 <source>Audio-only</source> 6908 <source>Audio-only</source>
6914 <target state="translated">Àudio solament</target> 6909 <target state="translated">Àudio solament</target>
@@ -6958,6 +6953,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6958 <source>Auto (via ffmpeg)</source> 6953 <source>Auto (via ffmpeg)</source>
6959 <target>Auto (via ffmpeg)</target> 6954 <target>Auto (via ffmpeg)</target>
6960 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6955 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6956 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6957 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6958 <context-group purpose="location">
6959 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6960 <context context-type="linenumber">3</context>
6961 </context-group>
6961 </trans-unit> 6962 </trans-unit>
6962 <trans-unit id="931255636742351800" datatype="html"> 6963 <trans-unit id="931255636742351800" datatype="html">
6963 <source>No limit</source> 6964 <source>No limit</source>
@@ -7108,18 +7109,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
7108 <trans-unit id="2127446333083057097" datatype="html"> 7109 <trans-unit id="2127446333083057097" datatype="html">
7109 <source>Domain is required.</source> 7110 <source>Domain is required.</source>
7110 <target state="translated">Un domeni es requerit.</target> 7111 <target state="translated">Un domeni es requerit.</target>
7111 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 7112
7112 </trans-unit> 7113 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
7113 <trans-unit id="6780793142903080663" datatype="html"> 7114 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
7114 <source>Domains entered are invalid.</source> 7115 <context-group purpose="location">
7115 <target state="translated">Los domenis picats son pas valids.</target> 7116 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7116 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 7117 <context context-type="linenumber">93</context>
7117 </trans-unit> 7118 </context-group>
7118 <trans-unit id="5886492514458202177" datatype="html"> 7119 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
7119 <source>Domains entered contain duplicates.</source> 7120 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
7120 <target state="new">Domains entered contain duplicates.</target> 7121 <context-group purpose="location">
7121 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 7122 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7123 <context context-type="linenumber">94</context>
7124 </context-group>
7125 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
7126 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
7127 <context-group purpose="location">
7128 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7129 <context context-type="linenumber">102</context>
7130 </context-group>
7131 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
7132 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
7133 <context-group purpose="location">
7134 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7135 <context context-type="linenumber">103</context>
7136 </context-group>
7122 </trans-unit> 7137 </trans-unit>
7138
7139
7123 <trans-unit id="240806681889331244"> 7140 <trans-unit id="240806681889331244">
7124 <source>Unlimited</source> 7141 <source>Unlimited</source>
7125 <target>Cap de limit</target> 7142 <target>Cap de limit</target>
@@ -7279,26 +7296,52 @@ channel with the same name (<x id="PH_2"/>)!</target>
7279 <x id="PH"/> tirat dels seguidors de l’instància 7296 <x id="PH"/> tirat dels seguidors de l’instància
7280 </target> 7297 </target>
7281 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 7298 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
7299 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
7300 <source>Follow</source><target state="new">Follow</target>
7301 <context-group purpose="location">
7302 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7303 <context context-type="linenumber">3</context>
7304 </context-group>
7305 <context-group purpose="location">
7306 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7307 <context context-type="linenumber">37</context>
7308 </context-group>
7309 <context-group purpose="location">
7310 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7311 <context context-type="linenumber">18</context>
7312 </context-group>
7313 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
7314 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
7315 <context-group purpose="location">
7316 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7317 <context context-type="linenumber">11</context>
7318 </context-group>
7282 </trans-unit> 7319 </trans-unit>
7283 <trans-unit id="2740793005745065895"> 7320 <trans-unit id="2740793005745065895">
7284 <source><x id="PH"/> is not valid </source> 7321 <source><x id="PH"/> is not valid </source>
7285 <target> 7322 <target>
7286 <x id="PH"/> es pas valid 7323 <x id="PH"/> es pas valid
7287 </target> 7324 </target>
7288 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 7325
7289 </trans-unit> 7326 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
7290 <trans-unit id="2355066641781598196"> 7327 <trans-unit id="2355066641781598196">
7291 <source>Follow request(s) sent!</source> 7328 <source>Follow request(s) sent!</source>
7292 <target>Demanda(s) de seguiment enviada !</target> 7329 <target>Demanda(s) de seguiment enviada !</target>
7293 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 7330
7331 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
7332 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
7333 <context-group purpose="location">
7334 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7335 <context context-type="linenumber">3</context>
7336 </context-group>
7294 </trans-unit> 7337 </trans-unit>
7295 <trans-unit id="4245720728052819482"> 7338 <trans-unit id="4245720728052819482">
7296 <source>Do you really want to unfollow <x id="PH"/>?</source> 7339 <source>Do you really want to unfollow <x id="PH"/>?</source>
7297 <target>Volètz vertadièrament quitar de seguir 7340 <target>Volètz vertadièrament quitar de seguir
7298 <x id="PH"/> ? 7341 <x id="PH"/> ?
7299 </target> 7342 </target>
7300 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 7343
7301 </trans-unit> 7344 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
7302 <trans-unit id="9160510009013134726"> 7345 <trans-unit id="9160510009013134726">
7303 <source>Unfollow</source> 7346 <source>Unfollow</source>
7304 <target>Quitar de seguir</target> 7347 <target>Quitar de seguir</target>
@@ -7309,8 +7352,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7309 <target>Seguèt pas mai 7352 <target>Seguèt pas mai
7310 <x id="PH"/>. 7353 <x id="PH"/>.
7311 </target> 7354 </target>
7312 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 7355
7313 </trans-unit> 7356 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
7314 <trans-unit id="2593763089859685916"> 7357 <trans-unit id="2593763089859685916">
7315 <source>enabled</source> 7358 <source>enabled</source>
7316 <target>activada</target> 7359 <target>activada</target>
@@ -7801,9 +7844,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7801 <trans-unit id="1519954996184640001"> 7844 <trans-unit id="1519954996184640001">
7802 <source>Error</source> 7845 <source>Error</source>
7803 <target>Error</target> 7846 <target>Error</target>
7804 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7847
7805 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7848
7806 </trans-unit> 7849 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7807 <trans-unit id="5076187961693950167" datatype="html"> 7850 <trans-unit id="5076187961693950167" datatype="html">
7808 <source>Standard logs</source> 7851 <source>Standard logs</source>
7809 <target state="new">Standard logs</target> 7852 <target state="new">Standard logs</target>
@@ -7848,16 +7891,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7848 <target>Actualizar lo senhal de l’utilizaire</target> 7891 <target>Actualizar lo senhal de l’utilizaire</target>
7849 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7892 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7850 </trans-unit> 7893 </trans-unit>
7851 <trans-unit id="177544274549739411" datatype="html"> 7894
7852 <source>Following list</source> 7895
7853 <target state="new">Following list</target>
7854 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7855 </trans-unit>
7856 <trans-unit id="8092429110007204784" datatype="html">
7857 <source>Followers list</source>
7858 <target state="new">Followers list</target>
7859 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7860 </trans-unit>
7861 <trans-unit id="780323526182667308" datatype="html"> 7896 <trans-unit id="780323526182667308" datatype="html">
7862 <source>User <x id="PH"/> updated.</source> 7897 <source>User <x id="PH"/> updated.</source>
7863 <target state="new">User 7898 <target state="new">User
@@ -7897,16 +7932,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7897 <target state="new">Federation</target> 7932 <target state="new">Federation</target>
7898 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7933 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7899 </trans-unit> 7934 </trans-unit>
7900 <trans-unit id="4682675125751819107" datatype="html"> 7935
7901 <source>Instances you follow</source> 7936
7902 <target state="new">Instances you follow</target>
7903 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7904 </trans-unit>
7905 <trans-unit id="8899833753704589712" datatype="html">
7906 <source>Instances following you</source>
7907 <target state="new">Instances following you</target>
7908 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7909 </trans-unit>
7910 <trans-unit id="3767259920053407667" datatype="html"> 7937 <trans-unit id="3767259920053407667" datatype="html">
7911 <source>Videos will be deleted, comments will be tombstoned.</source> 7938 <source>Videos will be deleted, comments will be tombstoned.</source>
7912 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7939 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7937,7 +7964,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7937 <target>Passar l’adreça coma verificada</target> 7964 <target>Passar l’adreça coma verificada</target>
7938 7965
7939 7966
7940 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7967 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7968 <source>Created</source><target state="new">Created</target>
7969 <context-group purpose="location">
7970 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7971 <context context-type="linenumber">115</context>
7972 </context-group>
7973 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7974 <source>Daily quota</source><target state="new">Daily quota</target>
7975 <context-group purpose="location">
7976 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7977 <context context-type="linenumber">120</context>
7978 </context-group>
7979 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7980 <source>Last login</source><target state="new">Last login</target>
7981 <context-group purpose="location">
7982 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7983 <context context-type="linenumber">122</context>
7984 </context-group>
7985 </trans-unit>
7941 <trans-unit id="3403978719736970622"> 7986 <trans-unit id="3403978719736970622">
7942 <source>You cannot ban root.</source> 7987 <source>You cannot ban root.</source>
7943 <target>Podètz pas fòrabandir lo root.</target> 7988 <target>Podètz pas fòrabandir lo root.</target>
@@ -8250,13 +8295,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
8250 <target>Cadena vidèo 8295 <target>Cadena vidèo
8251 <x id="PH"/> creada. 8296 <x id="PH"/> creada.
8252 </target> 8297 </target>
8253 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 8298
8254 </trans-unit> 8299 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
8255 <trans-unit id="8723777130353305761"> 8300 <trans-unit id="8723777130353305761">
8256 <source>This name already exists on this instance.</source> 8301 <source>This name already exists on this instance.</source>
8257 <target>Aqueste nom existís ja sus aquesta instància.</target> 8302 <target>Aqueste nom existís ja sus aquesta instància.</target>
8258 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 8303
8259 </trans-unit> 8304 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
8260 <trans-unit id="7589345916094713536"> 8305 <trans-unit id="7589345916094713536">
8261 <source>Video channel <x id="PH"/> updated.</source> 8306 <source>Video channel <x id="PH"/> updated.</source>
8262 <target>Cadena vidèo 8307 <target>Cadena vidèo
@@ -8279,13 +8324,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8279 <target state="new">Banner deleted.</target> 8324 <target state="new">Banner deleted.</target>
8280 8325
8281 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 8326 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
8282 <trans-unit id="2575302837003821736"> 8327
8283 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
8284 <target>Volgatz ben picar lo nom public de la cadena vidèo (
8285 <x id="PH"/>) per dire de confirmar
8286 </target>
8287 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
8288 </trans-unit>
8289 <trans-unit id="624066830180032195"> 8328 <trans-unit id="624066830180032195">
8290 <source>Video channel <x id="PH"/> deleted.</source> 8329 <source>Video channel <x id="PH"/> deleted.</source>
8291 <target>Cadena vidèo 8330 <target>Cadena vidèo
@@ -8451,6 +8490,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8451 <source>Ownership change request sent.</source> 8490 <source>Ownership change request sent.</source>
8452 <target>Demanda de cambiament de proprietat enviada.</target> 8491 <target>Demanda de cambiament de proprietat enviada.</target>
8453 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8492 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8493 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8494 <source>Sort by</source><target state="new">Sort by</target>
8495 <context-group purpose="location">
8496 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8497 <context context-type="linenumber">26</context>
8498 </context-group>
8454 </trans-unit> 8499 </trans-unit>
8455 <trans-unit id="3245220240937722814"> 8500 <trans-unit id="3245220240937722814">
8456 <source>My channels</source> 8501 <source>My channels</source>
@@ -8553,7 +8598,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8553 <target>S’abonar al compte</target> 8598 <target>S’abonar al compte</target>
8554 8599
8555 8600
8556 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8601 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8557 <trans-unit id="3131904093925601441" datatype="html"> 8602 <trans-unit id="3131904093925601441" datatype="html">
8558 <source>PLAYLISTS</source> 8603 <source>PLAYLISTS</source>
8559 <target state="new">PLAYLISTS</target> 8604 <target state="new">PLAYLISTS</target>
@@ -8600,35 +8645,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8600 <trans-unit id="3779524668013120370"> 8645 <trans-unit id="3779524668013120370">
8601 <source>Go to my subscriptions</source> 8646 <source>Go to my subscriptions</source>
8602 <target>Anar a mos abonaments</target> 8647 <target>Anar a mos abonaments</target>
8603 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8648
8604 </trans-unit> 8649 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8605 <trans-unit id="1136469849928650779"> 8650 <trans-unit id="1136469849928650779">
8606 <source>Go to my videos</source> 8651 <source>Go to my videos</source>
8607 <target>Anar a mas vidèos</target> 8652 <target>Anar a mas vidèos</target>
8608 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8653
8609 </trans-unit> 8654 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8610 <trans-unit id="7836683738999600376"> 8655 <trans-unit id="7836683738999600376">
8611 <source>Go to my imports</source> 8656 <source>Go to my imports</source>
8612 <target>Anar a mos imports</target> 8657 <target>Anar a mos imports</target>
8613 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8658
8614 </trans-unit> 8659 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8615 <trans-unit id="7511292153332773503"> 8660 <trans-unit id="7511292153332773503">
8616 <source>Go to my channels</source> 8661 <source>Go to my channels</source>
8617 <target>Anar a ma cadena</target> 8662 <target>Anar a ma cadena</target>
8618 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8663
8619 </trans-unit> 8664 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8620 <trans-unit id="2013324644839511073" datatype="html"> 8665 <trans-unit id="2013324644839511073" datatype="html">
8621 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8666 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8622Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8667Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8623 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8668 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8624Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8669Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8625 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8670
8626 </trans-unit> 8671 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8627 <trans-unit id="375263728166936544"> 8672 <trans-unit id="375263728166936544">
8628 <source>You need to reconnect.</source> 8673 <source>You need to reconnect.</source>
8629 <target>Vos cal vos reconnectar.</target> 8674 <target>Vos cal vos reconnectar.</target>
8630 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8675
8631 </trans-unit> 8676 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8632 <trans-unit id="2206638022166154361"> 8677 <trans-unit id="2206638022166154361">
8633 <source>Keyboard Shortcuts:</source> 8678 <source>Keyboard Shortcuts:</source>
8634 <target>Acorchis clavièr :</target> 8679 <target>Acorchis clavièr :</target>
@@ -8639,6 +8684,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8639 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8684 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8640 <context context-type="linenumber">98</context> 8685 <context context-type="linenumber">98</context>
8641 </context-group> 8686 </context-group>
8687 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8688 <source>In my library</source><target state="new">In my library</target>
8689 <context-group purpose="location">
8690 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8691 <context context-type="linenumber">104</context>
8692 </context-group>
8642 </trans-unit> 8693 </trans-unit>
8643 <trans-unit id="232050922346936574" datatype="html"> 8694 <trans-unit id="232050922346936574" datatype="html">
8644 <source>Trending</source> 8695 <source>Trending</source>
@@ -8665,39 +8716,39 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8665 <trans-unit id="1266887509445371246"> 8716 <trans-unit id="1266887509445371246">
8666 <source>Incorrect username or password.</source> 8717 <source>Incorrect username or password.</source>
8667 <target>Nom d’utilizaire o senhal incorrècte.</target> 8718 <target>Nom d’utilizaire o senhal incorrècte.</target>
8668 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8719
8669 </trans-unit> 8720 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8670 <trans-unit id="6974874606619467663" datatype="html"> 8721 <trans-unit id="6974874606619467663" datatype="html">
8671 <source>Your account is blocked.</source> 8722 <source>Your account is blocked.</source>
8672 <target state="new">Your account is blocked.</target> 8723 <target state="new">Your account is blocked.</target>
8673 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8724
8674 </trans-unit> 8725 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8675 <trans-unit id="7939914198003891823" datatype="html"> 8726 <trans-unit id="7939914198003891823" datatype="html">
8676 <source>any language</source> 8727 <source>any language</source>
8677 <target state="translated">tota lenga</target> 8728 <target state="translated">tota lenga</target>
8678 8729
8679 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8730 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8680 8731
8681 <trans-unit id="5633144232269377096" datatype="html"> 8732 <trans-unit id="5633144232269377096" datatype="html">
8682 <source>hide</source> 8733 <source>hide</source>
8683 <target state="translated">amagar</target> 8734 <target state="translated">amagar</target>
8684 8735
8685 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8736 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8686 <trans-unit id="8603861867909474404" datatype="html"> 8737 <trans-unit id="8603861867909474404" datatype="html">
8687 <source>blur</source> 8738 <source>blur</source>
8688 <target state="new">blur</target> 8739 <target state="new">blur</target>
8689 8740
8690 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8741 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8691 <trans-unit id="4534458451100881847" datatype="html"> 8742 <trans-unit id="4534458451100881847" datatype="html">
8692 <source>display</source> 8743 <source>display</source>
8693 <target state="translated">mostrar</target> 8744 <target state="translated">mostrar</target>
8694 8745
8695 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8746 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8696 <trans-unit id="4467323362722952678" datatype="html"> 8747 <trans-unit id="4467323362722952678" datatype="html">
8697 <source>Unknown</source> 8748 <source>Unknown</source>
8698 <target state="new">Unknown</target> 8749 <target state="new">Unknown</target>
8699 8750
8700 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8751 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8701 <trans-unit id="8781423666414310853"> 8752 <trans-unit id="8781423666414310853">
8702 <source>Your password has been successfully reset!</source> 8753 <source>Your password has been successfully reset!</source>
8703 <target>Vòstre senhal es estat corrèctament reïnicializat !</target> 8754 <target>Vòstre senhal es estat corrèctament reïnicializat !</target>
@@ -10302,18 +10353,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10302 <target>Tròp d’ensages, mercés de tornar ensajar dins 10353 <target>Tròp d’ensages, mercés de tornar ensajar dins
10303 <x id="PH"/> minutas. 10354 <x id="PH"/> minutas.
10304 </target> 10355 </target>
10305 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 10356
10306 </trans-unit> 10357 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
10307 <trans-unit id="4965472196059235310"> 10358 <trans-unit id="4965472196059235310">
10308 <source>Too many attempts, please try again later.</source> 10359 <source>Too many attempts, please try again later.</source>
10309 <target>Tròp d’ensages, mercés de tornar ensajar mai tard.</target> 10360 <target>Tròp d’ensages, mercés de tornar ensajar mai tard.</target>
10310 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 10361
10311 </trans-unit> 10362 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
10312 <trans-unit id="1693549688987384699"> 10363 <trans-unit id="1693549688987384699">
10313 <source>Server error. Please retry later.</source> 10364 <source>Server error. Please retry later.</source>
10314 <target>Error servidor. Mercés de tornar ensajar mai tard.</target> 10365 <target>Error servidor. Mercés de tornar ensajar mai tard.</target>
10315 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 10366
10316 </trans-unit> 10367 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
10317 <trans-unit id="5927402622550505067" datatype="html"> 10368 <trans-unit id="5927402622550505067" datatype="html">
10318 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 10369 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
10319 <target state="new">Subscribed to all current channels of 10370 <target state="new">Subscribed to all current channels of
@@ -10912,34 +10963,34 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10912 <source>Your video was uploaded to your account and is private.</source> 10963 <source>Your video was uploaded to your account and is private.</source>
10913 <target>La vidèo es estada enviada a vòstre compte e es privada.</target> 10964 <target>La vidèo es estada enviada a vòstre compte e es privada.</target>
10914 10965
10915 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10966 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10916 <trans-unit id="5699822024600815733"> 10967 <trans-unit id="5699822024600815733">
10917 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10968 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10918 <target>Mas las donadas associadas (etiquetas, descripcion...) seràn perdudas, volètz vertadièrament quitar la pagina ?</target> 10969 <target>Mas las donadas associadas (etiquetas, descripcion...) seràn perdudas, volètz vertadièrament quitar la pagina ?</target>
10919 10970
10920 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10971 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10921 <trans-unit id="1219739004043110649"> 10972 <trans-unit id="1219739004043110649">
10922 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10973 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10923 <target>La vidèo es pas encara complètament enviada, volètz vertadièrament quitar la pagina ?</target> 10974 <target>La vidèo es pas encara complètament enviada, volètz vertadièrament quitar la pagina ?</target>
10924 10975
10925 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10976 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10926 <trans-unit id="6932865105766151309" datatype="html"> 10977 <trans-unit id="6932865105766151309" datatype="html">
10927 <source>Upload</source> 10978 <source>Upload</source>
10928 <target state="new">Upload</target> 10979 <target state="new">Upload</target>
10929 10980
10930 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10981 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10931 <trans-unit id="8278735427925094503"> 10982 <trans-unit id="8278735427925094503">
10932 <source>Upload <x id="PH"/> </source> 10983 <source>Upload <x id="PH"/> </source>
10933 <target>Enviar 10984 <target>Enviar
10934 <x id="PH"/> 10985 <x id="PH"/>
10935 </target> 10986 </target>
10936 10987
10937 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10988 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10938 <trans-unit id="5981816353437801748"> 10989 <trans-unit id="5981816353437801748">
10939 <source>Video published.</source> 10990 <source>Video published.</source>
10940 <target>Vidèo publicada.</target> 10991 <target>Vidèo publicada.</target>
10941 10992
10942 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10993 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10943 <trans-unit id="764164089183618119"> 10994 <trans-unit id="764164089183618119">
10944 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10995 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10945 <target>Avètz de modificacions pas enregistradas. Se partissètz vòstras modificacions seràn perdudas. </target> 10996 <target>Avètz de modificacions pas enregistradas. Se partissètz vòstras modificacions seràn perdudas. </target>
@@ -10987,27 +11038,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10987 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 11038 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10988 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 11039 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10989 11040
10990 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 11041 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10991 <trans-unit id="5761611056224181752" datatype="html"> 11042 <trans-unit id="5761611056224181752" datatype="html">
10992 <source>Redirection</source> 11043 <source>Redirection</source>
10993 <target state="new">Redirection</target> 11044 <target state="new">Redirection</target>
10994 11045
10995 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 11046 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10996 <trans-unit id="8858527736400081688"> 11047 <trans-unit id="8858527736400081688">
10997 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 11048 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10998 <target>Aquesta vidèo conten un contengut per adult o explicite. Volètz vertadièrament la veire ?</target> 11049 <target>Aquesta vidèo conten un contengut per adult o explicite. Volètz vertadièrament la veire ?</target>
10999 11050
11000 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 11051 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
11001 <trans-unit id="3937119019020041049"> 11052 <trans-unit id="3937119019020041049">
11002 <source>Mature or explicit content</source> 11053 <source>Mature or explicit content</source>
11003 <target>Contengut per adult o explicite</target> 11054 <target>Contengut per adult o explicite</target>
11004 11055
11005 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 11056 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
11006 <trans-unit id="1755474755114288376" datatype="html"> 11057 <trans-unit id="1755474755114288376" datatype="html">
11007 <source>Up Next</source> 11058 <source>Up Next</source>
11008 <target state="translated">Seguent</target> 11059 <target state="translated">Seguent</target>
11009 11060
11010 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 11061 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
11011 <trans-unit id="2159130950882492111" datatype="html"> 11062 <trans-unit id="2159130950882492111" datatype="html">
11012 <source>Cancel</source> 11063 <source>Cancel</source>
11013 <target state="new">Cancel</target> 11064 <target state="new">Cancel</target>
@@ -11017,62 +11068,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
11017 <source>Autoplay is suspended</source> 11068 <source>Autoplay is suspended</source>
11018 <target state="translated">La lectura automatica es suspenduda</target> 11069 <target state="translated">La lectura automatica es suspenduda</target>
11019 11070
11020 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 11071 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
11021 <trans-unit id="7895294730547405228" datatype="html"> 11072 <trans-unit id="7895294730547405228" datatype="html">
11022 <source>Enter/exit fullscreen (requires player focus)</source> 11073 <source>Enter/exit fullscreen (requires player focus)</source>
11023 <target state="new">Enter/exit fullscreen (requires player focus)</target> 11074 <target state="new">Enter/exit fullscreen (requires player focus)</target>
11024 11075
11025 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 11076 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
11026 <trans-unit id="7618388257165864759" datatype="html"> 11077 <trans-unit id="7618388257165864759" datatype="html">
11027 <source>Play/Pause the video (requires player focus)</source> 11078 <source>Play/Pause the video (requires player focus)</source>
11028 <target state="new">Play/Pause the video (requires player focus)</target> 11079 <target state="new">Play/Pause the video (requires player focus)</target>
11029 11080
11030 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 11081 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
11031 <trans-unit id="7761890399634216630" datatype="html"> 11082 <trans-unit id="7761890399634216630" datatype="html">
11032 <source>Mute/unmute the video (requires player focus)</source> 11083 <source>Mute/unmute the video (requires player focus)</source>
11033 <target state="new">Mute/unmute the video (requires player focus)</target> 11084 <target state="new">Mute/unmute the video (requires player focus)</target>
11034 11085
11035 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 11086 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
11036 <trans-unit id="5996585232248234904" datatype="html"> 11087 <trans-unit id="5996585232248234904" datatype="html">
11037 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 11088 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
11038 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 11089 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
11039 11090
11040 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 11091 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
11041 <trans-unit id="3748765405903319998" datatype="html"> 11092 <trans-unit id="3748765405903319998" datatype="html">
11042 <source>Increase the volume (requires player focus)</source> 11093 <source>Increase the volume (requires player focus)</source>
11043 <target state="new">Increase the volume (requires player focus)</target> 11094 <target state="new">Increase the volume (requires player focus)</target>
11044 11095
11045 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 11096 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
11046 <trans-unit id="5810704036407159982" datatype="html"> 11097 <trans-unit id="5810704036407159982" datatype="html">
11047 <source>Decrease the volume (requires player focus)</source> 11098 <source>Decrease the volume (requires player focus)</source>
11048 <target state="new">Decrease the volume (requires player focus)</target> 11099 <target state="new">Decrease the volume (requires player focus)</target>
11049 11100
11050 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 11101 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
11051 <trans-unit id="2622048822548065691" datatype="html"> 11102 <trans-unit id="2622048822548065691" datatype="html">
11052 <source>Seek the video forward (requires player focus)</source> 11103 <source>Seek the video forward (requires player focus)</source>
11053 <target state="new">Seek the video forward (requires player focus)</target> 11104 <target state="new">Seek the video forward (requires player focus)</target>
11054 11105
11055 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 11106 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
11056 <trans-unit id="6540078205109221153" datatype="html"> 11107 <trans-unit id="6540078205109221153" datatype="html">
11057 <source>Seek the video backward (requires player focus)</source> 11108 <source>Seek the video backward (requires player focus)</source>
11058 <target state="new">Seek the video backward (requires player focus)</target> 11109 <target state="new">Seek the video backward (requires player focus)</target>
11059 11110
11060 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 11111 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
11061 <trans-unit id="1956491957766210808" datatype="html"> 11112 <trans-unit id="1956491957766210808" datatype="html">
11062 <source>Increase playback rate (requires player focus)</source> 11113 <source>Increase playback rate (requires player focus)</source>
11063 <target state="new">Increase playback rate (requires player focus)</target> 11114 <target state="new">Increase playback rate (requires player focus)</target>
11064 11115
11065 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 11116 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
11066 <trans-unit id="5495529997674803186" datatype="html"> 11117 <trans-unit id="5495529997674803186" datatype="html">
11067 <source>Decrease playback rate (requires player focus)</source> 11118 <source>Decrease playback rate (requires player focus)</source>
11068 <target state="new">Decrease playback rate (requires player focus)</target> 11119 <target state="new">Decrease playback rate (requires player focus)</target>
11069 11120
11070 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 11121 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
11071 <trans-unit id="3178343147230721210" datatype="html"> 11122 <trans-unit id="3178343147230721210" datatype="html">
11072 <source>Navigate in the video frame by frame (requires player focus)</source> 11123 <source>Navigate in the video frame by frame (requires player focus)</source>
11073 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 11124 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
11074 11125
11075 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 11126 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
11076 <trans-unit id="8025996572234182184"> 11127 <trans-unit id="8025996572234182184">
11077 <source>Like the video</source> 11128 <source>Like the video</source>
11078 <target>Aimar la vidèo</target> 11129 <target>Aimar la vidèo</target>
diff --git a/client/src/locale/angular.pl-PL.xlf b/client/src/locale/angular.pl-PL.xlf
index e5421bec8..8b02e562c 100644
--- a/client/src/locale/angular.pl-PL.xlf
+++ b/client/src/locale/angular.pl-PL.xlf
@@ -209,19 +209,19 @@
209 <target state="translated"> 209 <target state="translated">
210 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 210 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
211 </target> 211 </target>
212 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group> 212
213 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group> 213
214 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group> 214
215 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group> 215
216 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group> 216
217 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 217
218 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group> 218
219 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group> 219
220 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group> 220
221 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group> 221
222 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group> 222
223 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group> 223
224 </trans-unit> 224 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
225 <trans-unit id="1486537403020619891" datatype="html"> 225 <trans-unit id="1486537403020619891" datatype="html">
226 <source>My watch history</source> 226 <source>My watch history</source>
227 <target state="translated">Moja historia oglądania</target> 227 <target state="translated">Moja historia oglądania</target>
@@ -290,22 +290,22 @@
290 <trans-unit id="5674286808255988565"> 290 <trans-unit id="5674286808255988565">
291 <source>Create</source> 291 <source>Create</source>
292 <target>Utwórz</target> 292 <target>Utwórz</target>
293 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 293
294 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 294
295 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 295
296 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 296
297 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 297
298 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 298
299 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 299
300 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 300
301 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 301
302 </trans-unit> 302 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
303 <trans-unit id="1006562256968398209" datatype="html"> 303 <trans-unit id="1006562256968398209" datatype="html">
304 <source>video</source> 304 <source>video</source>
305 <target state="translated">film</target> 305 <target state="translated">film</target>
306 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group> 306
307 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group> 307
308 </trans-unit> 308 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
309 <trans-unit id="6438815964972582865" datatype="html"> 309 <trans-unit id="6438815964972582865" datatype="html">
310 <source>The following link contains a private token and should not be shared with anyone.</source> 310 <source>The following link contains a private token and should not be shared with anyone.</source>
311 <target state="translated">Następujący odnośnik zawiera prywatny token i nie należy się nim z nikim dzielić.</target> 311 <target state="translated">Następujący odnośnik zawiera prywatny token i nie należy się nim z nikim dzielić.</target>
@@ -375,13 +375,13 @@
375 <trans-unit id="6995024616159044376" datatype="html"> 375 <trans-unit id="6995024616159044376" datatype="html">
376 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 376 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
377 <target state="translated">Twoja powierzchnia na filmy została przekroczona przez ten film (rozmiar filmu: <x id="PH" equiv-text="videoSizeBytes"/>, wykorzystano: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, powierzchnia: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 377 <target state="translated">Twoja powierzchnia na filmy została przekroczona przez ten film (rozmiar filmu: <x id="PH" equiv-text="videoSizeBytes"/>, wykorzystano: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, powierzchnia: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
378 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 378
379 </trans-unit> 379 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
380 <trans-unit id="7873395933409147217" datatype="html"> 380 <trans-unit id="7873395933409147217" datatype="html">
381 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 381 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
382 <target state="translated">Twoja dzienna powierzchnia na filmy została przekroczona przez ten film (rozmiar filmu: <x id="PH" equiv-text="videoSizeBytes"/>, wykorzystano: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, powierzchnia <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 382 <target state="translated">Twoja dzienna powierzchnia na filmy została przekroczona przez ten film (rozmiar filmu: <x id="PH" equiv-text="videoSizeBytes"/>, wykorzystano: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, powierzchnia <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
383 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 383
384 </trans-unit> 384 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
385 <trans-unit id="5235042777215655908" datatype="html"> 385 <trans-unit id="5235042777215655908" datatype="html">
386 <source>subtitles</source> 386 <source>subtitles</source>
387 <target state="translated">napisy</target> 387 <target state="translated">napisy</target>
@@ -837,10 +837,10 @@
837 <trans-unit id="2602586221576511475"> 837 <trans-unit id="2602586221576511475">
838 <source>Video quota</source> 838 <source>Video quota</source>
839 <target>Przestrzeń na filmy</target> 839 <target>Przestrzeń na filmy</target>
840 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 840
841 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 841
842 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 842
843 </trans-unit> 843 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
844 <trans-unit id="1502595455339510144"> 844 <trans-unit id="1502595455339510144">
845 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 845 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
846 <target>Nieograniczony 846 <target>Nieograniczony
@@ -924,6 +924,30 @@
924 <target state="translated">Federacja</target> 924 <target state="translated">Federacja</target>
925 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 925 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
926 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 926 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
927 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
928 <source>Following</source><target state="new">Following</target>
929 <context-group purpose="location">
930 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
931 <context context-type="linenumber">29</context>
932 </context-group>
933 <context-group purpose="location">
934 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
935 <context context-type="linenumber">31</context>
936 </context-group>
937 <context-group purpose="location">
938 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
939 <context context-type="linenumber">28</context>
940 </context-group>
941 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
942 <source>Followers</source><target state="new">Followers</target>
943 <context-group purpose="location">
944 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
945 <context context-type="linenumber">34</context>
946 </context-group>
947 <context-group purpose="location">
948 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
949 <context context-type="linenumber">37</context>
950 </context-group>
927 </trans-unit> 951 </trans-unit>
928 <trans-unit id="3541687134897970106" datatype="html"> 952 <trans-unit id="3541687134897970106" datatype="html">
929 <source>followers</source> 953 <source>followers</source>
@@ -985,25 +1009,25 @@
985 <trans-unit id="2159130950882492111"> 1009 <trans-unit id="2159130950882492111">
986 <source>Cancel</source> 1010 <source>Cancel</source>
987 <target>Anuluj</target> 1011 <target>Anuluj</target>
988 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group> 1012
989 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group> 1013
990 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group> 1014
991 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group> 1015
992 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group> 1016
993 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group> 1017
994 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group> 1018
995 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group> 1019
996 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group> 1020
997 <context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group> 1021
998 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group> 1022
999 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group> 1023
1000 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 1024
1001 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 1025
1002 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group> 1026
1003 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group> 1027
1004 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group> 1028
1005 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group> 1029
1006 </trans-unit> 1030 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
1007 <trans-unit id="3616223838716839702" datatype="html"> 1031 <trans-unit id="3616223838716839702" datatype="html">
1008 <source>Ban this user</source> 1032 <source>Ban this user</source>
1009 <target state="translated">Zbanuj tego użytkownika</target> 1033 <target state="translated">Zbanuj tego użytkownika</target>
@@ -1069,19 +1093,13 @@
1069 <trans-unit id="7252854992688790751" datatype="html"> 1093 <trans-unit id="7252854992688790751" datatype="html">
1070 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1094 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1071 <target state="translated">Ta knstancja pozwala na rejestrację. Pamiętaj jednak sprawdzić <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Zasady<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Zasady<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> przed utworzeniem konta. Możesz też odnaleźć inną instancję spełniającą Twoje oczekiwania na: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/pl/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/pl/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1095 <target state="translated">Ta knstancja pozwala na rejestrację. Pamiętaj jednak sprawdzić <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Zasady<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Zasady<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> przed utworzeniem konta. Możesz też odnaleźć inną instancję spełniającą Twoje oczekiwania na: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/pl/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/pl/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1072 <context-group purpose="location"> 1096
1073 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1097 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1074 <context context-type="linenumber">60,62</context>
1075 </context-group>
1076 </trans-unit>
1077 <trans-unit id="7215649348148521605" datatype="html"> 1098 <trans-unit id="7215649348148521605" datatype="html">
1078 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1099 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1079 <target state="translated">Obecnie ta instancja nie pozwala na rejestrację użytkowników, możesz sprawdzić <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Zasady<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>, aby znaleźć więcej szczegółów lub znaleźć instancję, która pozwoli Ci na rejestrację konta i wysyłanie własnych filmów. Znajdź swoją spośród wielu instancji na: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/pl/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/pl/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1100 <target state="translated">Obecnie ta instancja nie pozwala na rejestrację użytkowników, możesz sprawdzić <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Zasady<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>, aby znaleźć więcej szczegółów lub znaleźć instancję, która pozwoli Ci na rejestrację konta i wysyłanie własnych filmów. Znajdź swoją spośród wielu instancji na: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/pl/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/pl/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1080 <context-group purpose="location"> 1101
1081 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1102 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1082 <context context-type="linenumber">65,67</context>
1083 </context-group>
1084 </trans-unit>
1085 <trans-unit id="2392488717875840729"> 1103 <trans-unit id="2392488717875840729">
1086 <source>User</source> 1104 <source>User</source>
1087 <target>Użytkownik</target> 1105 <target>Użytkownik</target>
@@ -1092,67 +1110,67 @@
1092 <source>Username or email address</source> 1110 <source>Username or email address</source>
1093 <target>Nazwa użytkownika lub adres e-mail</target> 1111 <target>Nazwa użytkownika lub adres e-mail</target>
1094 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1112 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1113 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1114 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1115 <context-group purpose="location">
1116 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1117 <context context-type="linenumber">33,34</context>
1118 </context-group>
1095 </trans-unit> 1119 </trans-unit>
1096 <trans-unit id="1431416938026210429"> 1120 <trans-unit id="1431416938026210429">
1097 <source>Password</source> 1121 <source>Password</source>
1098 <target>Hasło</target> 1122 <target>Hasło</target>
1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1123
1100 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1124
1101 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1125
1102 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1126
1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1127
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1128
1105 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1129
1106 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1130
1107 </trans-unit> 1131 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1108 <trans-unit id="8715156686857791956" datatype="html"> 1132 <trans-unit id="8715156686857791956" datatype="html">
1109 <source>Click here to reset your password</source> 1133 <source>Click here to reset your password</source>
1110 <target state="translated">Kliknij tutaj aby zresetować swoje hasło</target> 1134 <target state="translated">Kliknij tutaj aby zresetować swoje hasło</target>
1111 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1135
1112 </trans-unit> 1136 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1113 <trans-unit id="892063502898494584" datatype="html"> 1137 <trans-unit id="892063502898494584" datatype="html">
1114 <source>I forgot my password</source> 1138 <source>I forgot my password</source>
1115 <target state="translated">Nie pamiętam hasła</target> 1139 <target state="translated">Nie pamiętam hasła</target>
1116 <context-group purpose="location"> 1140
1117 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1141 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1118 <context context-type="linenumber">47</context>
1119 </context-group>
1120 </trans-unit>
1121 <trans-unit id="2101170466365500913" datatype="html"> 1142 <trans-unit id="2101170466365500913" datatype="html">
1122 <source>Logging into an account lets you publish content</source> 1143 <source>Logging into an account lets you publish content</source>
1123 <target state="translated">Logowanie na konto pozwala na publikację treści</target> 1144 <target state="translated">Logowanie na konto pozwala na publikację treści</target>
1124 <context-group purpose="location"> 1145
1125 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1146 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1126 <context context-type="linenumber">56,57</context>
1127 </context-group>
1128 </trans-unit>
1129 <trans-unit id="2454050363478003966"> 1147 <trans-unit id="2454050363478003966">
1130 <source>Login</source> 1148 <source>Login</source>
1131 <target>Zaloguj się</target> 1149 <target>Zaloguj się</target>
1132 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1150
1133 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1151
1134 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1152
1135 </trans-unit> 1153 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1136 <trans-unit id="3183213940445113677" datatype="html"> 1154 <trans-unit id="3183213940445113677" datatype="html">
1137 <source>Or sign in with</source> 1155 <source>Or sign in with</source>
1138 <target state="translated">Lub zaloguj się z</target> 1156 <target state="translated">Lub zaloguj się z</target>
1139 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1157
1140 </trans-unit> 1158 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1141 <trans-unit id="3238209155172574367"> 1159 <trans-unit id="3238209155172574367">
1142 <source>Forgot your password</source> 1160 <source>Forgot your password</source>
1143 <target>Zapomniałem hasła</target> 1161 <target>Zapomniałem hasła</target>
1144 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1162
1145 </trans-unit> 1163 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1146 <trans-unit id="87327320394367488" datatype="html"> 1164 <trans-unit id="87327320394367488" datatype="html">
1147 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1165 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1148 <target state="translated">Przepraszamy, nie możesz odzyskać hasła ponieważ administrator twojej instancji nie skonfigurował systemu e-mail.</target> 1166 <target state="translated">Przepraszamy, nie możesz odzyskać hasła ponieważ administrator twojej instancji nie skonfigurował systemu e-mail.</target>
1149 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1167
1150 </trans-unit> 1168 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1151 <trans-unit id="3188014010833256853" datatype="html"> 1169 <trans-unit id="3188014010833256853" datatype="html">
1152 <source>Enter your email address and we will send you a link to reset your password.</source> 1170 <source>Enter your email address and we will send you a link to reset your password.</source>
1153 <target state="translated">Wprowadź swój adres e-mail, a my wyślemy link pozwalający na zresetowanie hasła.</target> 1171 <target state="translated">Wprowadź swój adres e-mail, a my wyślemy link pozwalający na zresetowanie hasła.</target>
1154 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1172
1155 </trans-unit> 1173 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1156 <trans-unit id="1190256911880544559" datatype="html"> 1174 <trans-unit id="1190256911880544559" datatype="html">
1157 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1175 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1158The link will expire within 1 hour.</source> 1176The link will expire within 1 hour.</source>
@@ -1162,26 +1180,26 @@ The link will expire within 1 hour.</source>
1162 <trans-unit id="4768749765465246664"> 1180 <trans-unit id="4768749765465246664">
1163 <source>Email</source> 1181 <source>Email</source>
1164 <target>E-mail</target> 1182 <target>E-mail</target>
1165 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1183
1166 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1184
1167 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1185
1168 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1186
1169 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1187
1170 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1188
1171 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1189
1172 </trans-unit> 1190 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1173 <trans-unit id="3967269098753656610"> 1191 <trans-unit id="3967269098753656610">
1174 <source>Email address</source> 1192 <source>Email address</source>
1175 <target>Adres e-mail</target> 1193 <target>Adres e-mail</target>
1176 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1194
1177 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1195
1178 </trans-unit> 1196 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1179 <trans-unit id="7808756054397155068" datatype="html"> 1197 <trans-unit id="7808756054397155068" datatype="html">
1180 <source>Reset</source> 1198 <source>Reset</source>
1181 <target state="translated">Resetuj</target> 1199 <target state="translated">Resetuj</target>
1182 <note priority="1" from="description">Password reset button</note> 1200 <note priority="1" from="description">Password reset button</note>
1183 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1201
1184 </trans-unit> 1202 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1185 <trans-unit id="4319634264526091601" datatype="html"> 1203 <trans-unit id="4319634264526091601" datatype="html">
1186 <source>on this instance</source> 1204 <source>on this instance</source>
1187 <target state="translated">na tej instancji</target> 1205 <target state="translated">na tej instancji</target>
@@ -1532,9 +1550,9 @@ The link will expire within 1 hour.</source>
1532 <trans-unit id="2308975396733519902"> 1550 <trans-unit id="2308975396733519902">
1533 <source>Create an account</source> 1551 <source>Create an account</source>
1534 <target>Utwórz konto</target> 1552 <target>Utwórz konto</target>
1535 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1553
1536 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1554
1537 </trans-unit> 1555 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1538 <trans-unit id="3058024914967508975" datatype="html"> 1556 <trans-unit id="3058024914967508975" datatype="html">
1539 <source>My videos</source> 1557 <source>My videos</source>
1540 <target state="translated">Moje filmy</target> 1558 <target state="translated">Moje filmy</target>
@@ -1601,10 +1619,10 @@ The link will expire within 1 hour.</source>
1601 <trans-unit id="1504521795586863905" datatype="html"> 1619 <trans-unit id="1504521795586863905" datatype="html">
1602 <source>VIDEOS</source> 1620 <source>VIDEOS</source>
1603 <target state="translated">FILMY</target> 1621 <target state="translated">FILMY</target>
1604 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1622
1605 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1623
1606 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1624
1607 </trans-unit> 1625 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1608 <trans-unit id="667372110624203230" datatype="html"> 1626 <trans-unit id="667372110624203230" datatype="html">
1609 <source>Import jobs concurrency</source> 1627 <source>Import jobs concurrency</source>
1610 <target state="translated">Równoczesne zadania importu</target> 1628 <target state="translated">Równoczesne zadania importu</target>
@@ -1683,8 +1701,8 @@ The link will expire within 1 hour.</source>
1683 <trans-unit id="4424964105331349857" datatype="html"> 1701 <trans-unit id="4424964105331349857" datatype="html">
1684 <source>I'm a teapot</source> 1702 <source>I'm a teapot</source>
1685 <target state="translated">Jestem czajniczkiem</target> 1703 <target state="translated">Jestem czajniczkiem</target>
1686 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1704
1687 </trans-unit> 1705 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1688 <trans-unit id="1597262876035959248" datatype="html"> 1706 <trans-unit id="1597262876035959248" datatype="html">
1689 <source>That's an error.</source> 1707 <source>That's an error.</source>
1690 <target state="translated">To błąd.</target> 1708 <target state="translated">To błąd.</target>
@@ -1777,8 +1795,8 @@ The link will expire within 1 hour.</source>
1777 <trans-unit id="2971365540217107489" datatype="html"> 1795 <trans-unit id="2971365540217107489" datatype="html">
1778 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1796 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1779 <target state="translated">Zawartość multimedialna jest zbyt wielka dla tego serwerami skontaktuj się z administratorem, jeżeli chcesz aby zwiększył limit rozmiaru.</target> 1797 <target state="translated">Zawartość multimedialna jest zbyt wielka dla tego serwerami skontaktuj się z administratorem, jeżeli chcesz aby zwiększył limit rozmiaru.</target>
1780 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1798
1781 </trans-unit> 1799 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1782 <trans-unit id="5131854469652959713" datatype="html"> 1800 <trans-unit id="5131854469652959713" datatype="html">
1783 <source>GLOBAL SEARCH</source> 1801 <source>GLOBAL SEARCH</source>
1784 <target state="translated">WYSZUKIWANIE OGÓLNE</target> 1802 <target state="translated">WYSZUKIWANIE OGÓLNE</target>
@@ -2520,8 +2538,8 @@ The link will expire within 1 hour.</source>
2520 <trans-unit id="6161604372916832458" datatype="html"> 2538 <trans-unit id="6161604372916832458" datatype="html">
2521 <source>Upload on hold</source> 2539 <source>Upload on hold</source>
2522 <target state="new">Upload on hold</target> 2540 <target state="new">Upload on hold</target>
2523 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2541
2524 </trans-unit> 2542 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2525 <trans-unit id="285180972645018518" datatype="html"> 2543 <trans-unit id="285180972645018518" datatype="html">
2526 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2544 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2527 <target state="translated">Niestety, możliwość wysyłania jest wyłączona dla Twojego konta. Jeżeli chcesz dodać filmy, administrator musi odblokować Twój przydział powierzchni.</target> 2545 <target state="translated">Niestety, możliwość wysyłania jest wyłączona dla Twojego konta. Jeżeli chcesz dodać filmy, administrator musi odblokować Twój przydział powierzchni.</target>
@@ -3203,11 +3221,7 @@ The link will expire within 1 hour.</source>
3203 <target>ID</target> 3221 <target>ID</target>
3204 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3222 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3205 </trans-unit> 3223 </trans-unit>
3206 <trans-unit id="2265605798180116441" datatype="html"> 3224
3207 <source>Follower handle</source>
3208 <target state="translated">Nazwa obserwującego użytkownika</target>
3209 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3210 </trans-unit>
3211 <trans-unit id="5911214550882917183"> 3225 <trans-unit id="5911214550882917183">
3212 <source>State</source> 3226 <source>State</source>
3213 <target state="translated">Stan</target> 3227 <target state="translated">Stan</target>
@@ -3276,11 +3290,7 @@ The link will expire within 1 hour.</source>
3276 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target> 3290 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target>
3277 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3291 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3278 </trans-unit> 3292 </trans-unit>
3279 <trans-unit id="6641024648411549335"> 3293
3280 <source>Host</source>
3281 <target>Host</target>
3282 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3283 </trans-unit>
3284 <trans-unit id="6571718060636962350" datatype="html"> 3294 <trans-unit id="6571718060636962350" datatype="html">
3285 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3295 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3286 <target state="translated">Redundancja zezwolona 3296 <target state="translated">Redundancja zezwolona
@@ -3292,9 +3302,9 @@ The link will expire within 1 hour.</source>
3292 <trans-unit id="9160510009013134726" datatype="html"> 3302 <trans-unit id="9160510009013134726" datatype="html">
3293 <source>Unfollow</source> 3303 <source>Unfollow</source>
3294 <target state="translated">Przestań obserwować</target> 3304 <target state="translated">Przestań obserwować</target>
3295 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3305
3296 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3306
3297 </trans-unit> 3307 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3298 <trans-unit id="8246779176913476983" datatype="html"> 3308 <trans-unit id="8246779176913476983" datatype="html">
3299 <source>Open instance in a new tab</source> 3309 <source>Open instance in a new tab</source>
3300 <target state="translated">Otwórz instancję w nowej karcie</target> 3310 <target state="translated">Otwórz instancję w nowej karcie</target>
@@ -3305,28 +3315,20 @@ The link will expire within 1 hour.</source>
3305 <trans-unit id="9132918641931433659" datatype="html"> 3315 <trans-unit id="9132918641931433659" datatype="html">
3306 <source>No host found matching current filters.</source> 3316 <source>No host found matching current filters.</source>
3307 <target state="translated">Brak hostów spełniających obecne kryteria.</target> 3317 <target state="translated">Brak hostów spełniających obecne kryteria.</target>
3308 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3318
3309 </trans-unit> 3319 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3310 <trans-unit id="7274241885665071790" datatype="html"> 3320 <trans-unit id="7274241885665071790" datatype="html">
3311 <source>Your instance is not following anyone.</source> 3321 <source>Your instance is not following anyone.</source>
3312 <target state="translated">Twoja instancja nie obserwuje nikogo.</target> 3322 <target state="translated">Twoja instancja nie obserwuje nikogo.</target>
3313 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3323
3314 </trans-unit> 3324 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3315 <trans-unit id="4774348799569692380" datatype="html"> 3325 <trans-unit id="4774348799569692380" datatype="html">
3316 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3326 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3317 <target state="translated">Wyświetlanie <x id="INTERPOLATION"/> do <x id="INTERPOLATION_1"/> z <x id="INTERPOLATION_2"/> hostów</target> 3327 <target state="translated">Wyświetlanie <x id="INTERPOLATION"/> do <x id="INTERPOLATION_1"/> z <x id="INTERPOLATION_2"/> hostów</target>
3318 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3328 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3319 </trans-unit> 3329 </trans-unit>
3320 <trans-unit id="6275803119759621687" datatype="html"> 3330
3321 <source>Follow domains</source> 3331
3322 <target state="translated">Obserwuj domeny</target>
3323 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3324 </trans-unit>
3325 <trans-unit id="1268699198448750610" datatype="html">
3326 <source>Follow instances</source>
3327 <target state="translated">Zaobserwuj instancje</target>
3328 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3329 </trans-unit>
3330 <trans-unit id="9216117865911519658" datatype="html"> 3332 <trans-unit id="9216117865911519658" datatype="html">
3331 <source>Action</source> 3333 <source>Action</source>
3332 <target state="translated">Działanie</target> 3334 <target state="translated">Działanie</target>
@@ -3376,11 +3378,11 @@ The link will expire within 1 hour.</source>
3376 <trans-unit id="5248717555542428023"> 3378 <trans-unit id="5248717555542428023">
3377 <source>Username</source> 3379 <source>Username</source>
3378 <target>Nazwa użytkownika</target> 3380 <target>Nazwa użytkownika</target>
3379 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3381
3380 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3382
3381 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3383
3382 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3384
3383 </trans-unit> 3385 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3384 <trans-unit id="5428411040014095392" datatype="html"> 3386 <trans-unit id="5428411040014095392" datatype="html">
3385 <source>e.g. jane_doe</source> 3387 <source>e.g. jane_doe</source>
3386 <target state="translated">np. jane_doe</target> 3388 <target state="translated">np. jane_doe</target>
@@ -3408,9 +3410,9 @@ The link will expire within 1 hour.</source>
3408 <trans-unit id="4145496584631696119"> 3410 <trans-unit id="4145496584631696119">
3409 <source>Role</source> 3411 <source>Role</source>
3410 <target>Rola</target> 3412 <target>Rola</target>
3411 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3413
3412 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3414
3413 </trans-unit> 3415 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3414 <trans-unit id="7046347992315328430" datatype="html"> 3416 <trans-unit id="7046347992315328430" datatype="html">
3415 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3417 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3416 <target state="translated">Transkodowanie jest włączone. Limit użytej powierzchni bierze pod uwagę tylko <x id="START_TAG_STRONG"/>oryginalny<x id="CLOSE_TAG_STRONG"/> rozmiar filmów. <x id="LINE_BREAK"/> Maksymalnie, użytkownik może wysłać~ <x id="INTERPOLATION"/>. </target> 3418 <target state="translated">Transkodowanie jest włączone. Limit użytej powierzchni bierze pod uwagę tylko <x id="START_TAG_STRONG"/>oryginalny<x id="CLOSE_TAG_STRONG"/> rozmiar filmów. <x id="LINE_BREAK"/> Maksymalnie, użytkownik może wysłać~ <x id="INTERPOLATION"/>. </target>
@@ -3427,15 +3429,9 @@ The link will expire within 1 hour.</source>
3427 <trans-unit id="2622255144026150901" datatype="html"> 3429 <trans-unit id="2622255144026150901" datatype="html">
3428 <source>Auth plugin</source> 3430 <source>Auth plugin</source>
3429 <target state="translated">Wtyczka uwierzytelniania</target> 3431 <target state="translated">Wtyczka uwierzytelniania</target>
3430 <context-group purpose="location"> 3432
3431 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3433
3432 <context context-type="linenumber">188</context> 3434 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3433 </context-group>
3434 <context-group purpose="location">
3435 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3436 <context context-type="linenumber">188</context>
3437 </context-group>
3438 </trans-unit>
3439 <trans-unit id="588099657508661970" datatype="html"> 3435 <trans-unit id="588099657508661970" datatype="html">
3440 <source>None (local authentication)</source> 3436 <source>None (local authentication)</source>
3441 <target state="translated">Brak (lokalne uwierzytelnianie)</target> 3437 <target state="translated">Brak (lokalne uwierzytelnianie)</target>
@@ -3689,6 +3685,12 @@ The link will expire within 1 hour.</source>
3689 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3685 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3690 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3686 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3691 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3687 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3688 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3689 <source>Follower</source><target state="new">Follower</target>
3690 <context-group purpose="location">
3691 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3692 <context context-type="linenumber">24</context>
3693 </context-group>
3692 </trans-unit> 3694 </trans-unit>
3693 <trans-unit id="4691552465058437520" datatype="html"> 3695 <trans-unit id="4691552465058437520" datatype="html">
3694 <source>Commented video</source> 3696 <source>Commented video</source>
@@ -3997,8 +3999,8 @@ The link will expire within 1 hour.</source>
3997 <trans-unit id="4917252294930256268" datatype="html"> 3999 <trans-unit id="4917252294930256268" datatype="html">
3998 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 4000 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3999 <target state="translated">Wygląda na to, że nie jesteś na serwerze HTTPS. Twój serwer musi mieć aktywne TLS, aby obserwować inne serwery.</target> 4001 <target state="translated">Wygląda na to, że nie jesteś na serwerze HTTPS. Twój serwer musi mieć aktywne TLS, aby obserwować inne serwery.</target>
4000 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 4002
4001 </trans-unit> 4003 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
4002 <trans-unit id="4058814854824495833" datatype="html"> 4004 <trans-unit id="4058814854824495833" datatype="html">
4003 <source>Mute domains</source> 4005 <source>Mute domains</source>
4004 <target state="translated">Wycisz domeny</target> 4006 <target state="translated">Wycisz domeny</target>
@@ -5959,11 +5961,8 @@ color: red;
5959 <trans-unit id="5512878593724620692" datatype="html"> 5961 <trans-unit id="5512878593724620692" datatype="html">
5960 <source>CHANNELS</source> 5962 <source>CHANNELS</source>
5961 <target state="translated">KANAŁY</target> 5963 <target state="translated">KANAŁY</target>
5962 <context-group purpose="location"> 5964
5963 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5965 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5964 <context context-type="linenumber">82</context>
5965 </context-group>
5966 </trans-unit>
5967 <trans-unit id="3666829335406793239" datatype="html"> 5966 <trans-unit id="3666829335406793239" datatype="html">
5968 <source>This account does not have channels.</source> 5967 <source>This account does not have channels.</source>
5969 <target state="translated">To konto nie ma kanałów.</target> 5968 <target state="translated">To konto nie ma kanałów.</target>
@@ -6002,6 +6001,12 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
6002channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 6001channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
6003 <target state="translated">Czy na pewno cchesz usunąć <x id="PH" equiv-text="videoChannel.displayName"/>? Usuniesz w ten sposób <x id="PH_1" equiv-text="videoChannel.videosCount"/> filmów wysłanych na ten kanał i nie będziesz miał(a) możliwości założenia kanału o tej samej nazwie (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target> 6002 <target state="translated">Czy na pewno cchesz usunąć <x id="PH" equiv-text="videoChannel.displayName"/>? Usuniesz w ten sposób <x id="PH_1" equiv-text="videoChannel.videosCount"/> filmów wysłanych na ten kanał i nie będziesz miał(a) możliwości założenia kanału o tej samej nazwie (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target>
6004 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 6003 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
6004 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
6005 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
6006 <context-group purpose="location">
6007 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
6008 <context context-type="linenumber">48</context>
6009 </context-group>
6005 </trans-unit> 6010 </trans-unit>
6006 <trans-unit id="5387007581996837469" datatype="html"> 6011 <trans-unit id="5387007581996837469" datatype="html">
6007 <source>My Channels</source> 6012 <source>My Channels</source>
@@ -6535,13 +6540,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6535 <trans-unit id="6979021199788941693"> 6540 <trans-unit id="6979021199788941693">
6536 <source>Your message has been sent.</source> 6541 <source>Your message has been sent.</source>
6537 <target>Twoja wiadomość została wysłana.</target> 6542 <target>Twoja wiadomość została wysłana.</target>
6538 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group> 6543
6539 </trans-unit> 6544 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6540 <trans-unit id="2072135752262464360" datatype="html"> 6545 <trans-unit id="2072135752262464360" datatype="html">
6541 <source>You already sent this form recently</source> 6546 <source>You already sent this form recently</source>
6542 <target state="translated">Niedawno wypełniłeś(-aś) ten formularz</target> 6547 <target state="translated">Niedawno wypełniłeś(-aś) ten formularz</target>
6543 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group> 6548
6544 </trans-unit> 6549 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6545 <trans-unit id="819067926858619041" datatype="html"> 6550 <trans-unit id="819067926858619041" datatype="html">
6546 <source>Account videos</source> 6551 <source>Account videos</source>
6547 <target state="translated">Filmy konta</target> 6552 <target state="translated">Filmy konta</target>
@@ -6588,13 +6593,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6588 <target state="translated"> 6593 <target state="translated">
6589 <x id="PH"/> bezpośrednio obserwujących konto 6594 <x id="PH"/> bezpośrednio obserwujących konto
6590 </target> 6595 </target>
6591 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6596
6592 </trans-unit> 6597 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6593 <trans-unit id="6250999352462648289" datatype="html"> 6598 <trans-unit id="6250999352462648289" datatype="html">
6594 <source>Report this account</source> 6599 <source>Report this account</source>
6595 <target state="translated">Zgłoś to konto</target> 6600 <target state="translated">Zgłoś to konto</target>
6596 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6601
6597 </trans-unit> 6602 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6598 <trans-unit id="1504521795586863905" datatype="html"> 6603 <trans-unit id="1504521795586863905" datatype="html">
6599 <source>VIDEOS</source> 6604 <source>VIDEOS</source>
6600 <target state="translated">FILMY</target> 6605 <target state="translated">FILMY</target>
@@ -6604,31 +6609,21 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6604 <trans-unit id="25349740244798533" datatype="html"> 6609 <trans-unit id="25349740244798533" datatype="html">
6605 <source>Username copied</source> 6610 <source>Username copied</source>
6606 <target state="translated">Nazwa użytkownika skopiowana</target> 6611 <target state="translated">Nazwa użytkownika skopiowana</target>
6607 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6612
6608 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6613
6609 </trans-unit> 6614 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6610 <trans-unit id="9221735175659318025" datatype="html"> 6615 <trans-unit id="9221735175659318025" datatype="html">
6611 <source>1 subscriber</source> 6616 <source>1 subscriber</source>
6612 <target state="translated">1 subskrybujący</target> 6617 <target state="translated">1 subskrybujący</target>
6613 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6618
6614 </trans-unit> 6619 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6615 <trans-unit id="4097331874769079975" datatype="html"> 6620 <trans-unit id="4097331874769079975" datatype="html">
6616 <source><x id="PH"/> subscribers</source> 6621 <source><x id="PH"/> subscribers</source>
6617 <target state="translated"><x id="PH"/> subskrybujący</target> 6622 <target state="translated"><x id="PH"/> subskrybujący</target>
6618 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6623
6619 </trans-unit> 6624 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6620 <trans-unit id="4682675125751819107" datatype="html"> 6625
6621 <source>Instances you follow</source> 6626
6622 <target state="translated">Instancje, które obserwujesz</target>
6623 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6624 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6625 </trans-unit>
6626 <trans-unit id="8899833753704589712" datatype="html">
6627 <source>Instances following you</source>
6628 <target state="translated">Instancje, które Cię obserwują</target>
6629 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6630 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6631 </trans-unit>
6632 <trans-unit id="1035838766454786107" datatype="html"> 6627 <trans-unit id="1035838766454786107" datatype="html">
6633 <source>Audio-only</source> 6628 <source>Audio-only</source>
6634 <target state="translated">Tylko dźwięk</target> 6629 <target state="translated">Tylko dźwięk</target>
@@ -6678,6 +6673,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6678 <source>Auto (via ffmpeg)</source> 6673 <source>Auto (via ffmpeg)</source>
6679 <target state="translated">Automatycznie (poprzez ffmpeg)</target> 6674 <target state="translated">Automatycznie (poprzez ffmpeg)</target>
6680 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6675 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6676 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6677 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6678 <context-group purpose="location">
6679 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6680 <context context-type="linenumber">3</context>
6681 </context-group>
6681 </trans-unit> 6682 </trans-unit>
6682 <trans-unit id="931255636742351800" datatype="html"> 6683 <trans-unit id="931255636742351800" datatype="html">
6683 <source>No limit</source> 6684 <source>No limit</source>
@@ -6826,18 +6827,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6826 <trans-unit id="2127446333083057097" datatype="html"> 6827 <trans-unit id="2127446333083057097" datatype="html">
6827 <source>Domain is required.</source> 6828 <source>Domain is required.</source>
6828 <target state="translated">Domena jest wymagana.</target> 6829 <target state="translated">Domena jest wymagana.</target>
6829 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6830
6830 </trans-unit> 6831 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6831 <trans-unit id="6780793142903080663" datatype="html"> 6832 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6832 <source>Domains entered are invalid.</source> 6833 <context-group purpose="location">
6833 <target state="translated">Wprowadzone domeny są nieprawidłowe.</target> 6834 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6834 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6835 <context context-type="linenumber">93</context>
6835 </trans-unit> 6836 </context-group>
6836 <trans-unit id="5886492514458202177" datatype="html"> 6837 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6837 <source>Domains entered contain duplicates.</source> 6838 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6838 <target state="translated">Wprowadzone domeny zawierają duplikaty.</target> 6839 <context-group purpose="location">
6839 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6840 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6841 <context context-type="linenumber">94</context>
6842 </context-group>
6843 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6844 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6845 <context-group purpose="location">
6846 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6847 <context context-type="linenumber">102</context>
6848 </context-group>
6849 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6850 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6851 <context-group purpose="location">
6852 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6853 <context context-type="linenumber">103</context>
6854 </context-group>
6840 </trans-unit> 6855 </trans-unit>
6856
6857
6841 <trans-unit id="240806681889331244"> 6858 <trans-unit id="240806681889331244">
6842 <source>Unlimited</source> 6859 <source>Unlimited</source>
6843 <target>Nieograniczona</target> 6860 <target>Nieograniczona</target>
@@ -6997,26 +7014,52 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6997 <x id="PH"/> usunięty z obserwujących instancję 7014 <x id="PH"/> usunięty z obserwujących instancję
6998 </target> 7015 </target>
6999 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 7016 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
7017 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
7018 <source>Follow</source><target state="new">Follow</target>
7019 <context-group purpose="location">
7020 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7021 <context context-type="linenumber">3</context>
7022 </context-group>
7023 <context-group purpose="location">
7024 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7025 <context context-type="linenumber">37</context>
7026 </context-group>
7027 <context-group purpose="location">
7028 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7029 <context context-type="linenumber">18</context>
7030 </context-group>
7031 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
7032 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
7033 <context-group purpose="location">
7034 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7035 <context context-type="linenumber">11</context>
7036 </context-group>
7000 </trans-unit> 7037 </trans-unit>
7001 <trans-unit id="2740793005745065895"> 7038 <trans-unit id="2740793005745065895">
7002 <source><x id="PH"/> is not valid </source> 7039 <source><x id="PH"/> is not valid </source>
7003 <target> 7040 <target>
7004 <x id="PH"/> nie jest prawidowy 7041 <x id="PH"/> nie jest prawidowy
7005 </target> 7042 </target>
7006 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 7043
7007 </trans-unit> 7044 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
7008 <trans-unit id="2355066641781598196"> 7045 <trans-unit id="2355066641781598196">
7009 <source>Follow request(s) sent!</source> 7046 <source>Follow request(s) sent!</source>
7010 <target>Wysłano prośby o możliwość śledzenia!</target> 7047 <target>Wysłano prośby o możliwość śledzenia!</target>
7011 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 7048
7049 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
7050 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
7051 <context-group purpose="location">
7052 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7053 <context context-type="linenumber">3</context>
7054 </context-group>
7012 </trans-unit> 7055 </trans-unit>
7013 <trans-unit id="4245720728052819482"> 7056 <trans-unit id="4245720728052819482">
7014 <source>Do you really want to unfollow <x id="PH"/>?</source> 7057 <source>Do you really want to unfollow <x id="PH"/>?</source>
7015 <target>Czy na pewno chcesz przestać śledzić 7058 <target>Czy na pewno chcesz przestać śledzić
7016 <x id="PH"/>? 7059 <x id="PH"/>?
7017 </target> 7060 </target>
7018 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 7061
7019 </trans-unit> 7062 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
7020 <trans-unit id="9160510009013134726"> 7063 <trans-unit id="9160510009013134726">
7021 <source>Unfollow</source> 7064 <source>Unfollow</source>
7022 <target>Przestań śledzić</target> 7065 <target>Przestań śledzić</target>
@@ -7027,8 +7070,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7027 <target>Już nie śledzisz 7070 <target>Już nie śledzisz
7028 <x id="PH"/>. 7071 <x id="PH"/>.
7029 </target> 7072 </target>
7030 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 7073
7031 </trans-unit> 7074 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
7032 <trans-unit id="2593763089859685916" datatype="html"> 7075 <trans-unit id="2593763089859685916" datatype="html">
7033 <source>enabled</source> 7076 <source>enabled</source>
7034 <target state="translated">włączona</target> 7077 <target state="translated">włączona</target>
@@ -7513,9 +7556,9 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7513 <trans-unit id="1519954996184640001"> 7556 <trans-unit id="1519954996184640001">
7514 <source>Error</source> 7557 <source>Error</source>
7515 <target>Błąd</target> 7558 <target>Błąd</target>
7516 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7559
7517 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7560
7518 </trans-unit> 7561 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7519 <trans-unit id="5076187961693950167" datatype="html"> 7562 <trans-unit id="5076187961693950167" datatype="html">
7520 <source>Standard logs</source> 7563 <source>Standard logs</source>
7521 <target state="translated">Standardowe raporty</target> 7564 <target state="translated">Standardowe raporty</target>
@@ -7560,16 +7603,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7560 <target state="translated">Zaktualizuj hasło użytkownika</target> 7603 <target state="translated">Zaktualizuj hasło użytkownika</target>
7561 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7604 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7562 </trans-unit> 7605 </trans-unit>
7563 <trans-unit id="177544274549739411" datatype="html"> 7606
7564 <source>Following list</source> 7607
7565 <target state="translated">Lista obserwujących</target>
7566 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7567 </trans-unit>
7568 <trans-unit id="8092429110007204784" datatype="html">
7569 <source>Followers list</source>
7570 <target state="translated">Lista obserwowanych</target>
7571 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7572 </trans-unit>
7573 <trans-unit id="780323526182667308" datatype="html"> 7608 <trans-unit id="780323526182667308" datatype="html">
7574 <source>User <x id="PH"/> updated.</source> 7609 <source>User <x id="PH"/> updated.</source>
7575 <target state="translated">Aktualizowano użytkownika <x id="PH"/>.</target> 7610 <target state="translated">Aktualizowano użytkownika <x id="PH"/>.</target>
@@ -7607,16 +7642,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7607 <target state="translated">Federacja</target> 7642 <target state="translated">Federacja</target>
7608 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7643 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7609 </trans-unit> 7644 </trans-unit>
7610 <trans-unit id="4682675125751819107" datatype="html"> 7645
7611 <source>Instances you follow</source> 7646
7612 <target state="translated">Instancje które obserwujesz</target>
7613 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7614 </trans-unit>
7615 <trans-unit id="8899833753704589712" datatype="html">
7616 <source>Instances following you</source>
7617 <target state="translated">Instancje które Cię obserwują</target>
7618 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7619 </trans-unit>
7620 <trans-unit id="3767259920053407667" datatype="html"> 7647 <trans-unit id="3767259920053407667" datatype="html">
7621 <source>Videos will be deleted, comments will be tombstoned.</source> 7648 <source>Videos will be deleted, comments will be tombstoned.</source>
7622 <target state="translated">Filmy zostaną usunięte, komentarze zostaną porzucone.</target> 7649 <target state="translated">Filmy zostaną usunięte, komentarze zostaną porzucone.</target>
@@ -7647,6 +7674,24 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7647 <target state="translated">Ustaw e-mail jako zwerifykowany</target> 7674 <target state="translated">Ustaw e-mail jako zwerifykowany</target>
7648 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7675 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7649 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7676 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7677 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7678 <source>Created</source><target state="new">Created</target>
7679 <context-group purpose="location">
7680 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7681 <context context-type="linenumber">115</context>
7682 </context-group>
7683 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7684 <source>Daily quota</source><target state="new">Daily quota</target>
7685 <context-group purpose="location">
7686 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7687 <context context-type="linenumber">120</context>
7688 </context-group>
7689 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7690 <source>Last login</source><target state="new">Last login</target>
7691 <context-group purpose="location">
7692 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7693 <context context-type="linenumber">122</context>
7694 </context-group>
7650 </trans-unit> 7695 </trans-unit>
7651 <trans-unit id="3403978719736970622" datatype="html"> 7696 <trans-unit id="3403978719736970622" datatype="html">
7652 <source>You cannot ban root.</source> 7697 <source>You cannot ban root.</source>
@@ -7959,13 +8004,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7959 <target>Utworzono kanał wideo 8004 <target>Utworzono kanał wideo
7960 <x id="PH"/>. 8005 <x id="PH"/>.
7961 </target> 8006 </target>
7962 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 8007
7963 </trans-unit> 8008 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7964 <trans-unit id="8723777130353305761" datatype="html"> 8009 <trans-unit id="8723777130353305761" datatype="html">
7965 <source>This name already exists on this instance.</source> 8010 <source>This name already exists on this instance.</source>
7966 <target state="translated">Ta nazwa już istnieje na tej instancji.</target> 8011 <target state="translated">Ta nazwa już istnieje na tej instancji.</target>
7967 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 8012
7968 </trans-unit> 8013 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7969 <trans-unit id="7589345916094713536"> 8014 <trans-unit id="7589345916094713536">
7970 <source>Video channel <x id="PH"/> updated.</source> 8015 <source>Video channel <x id="PH"/> updated.</source>
7971 <target>Zaktualizowano kanał wideo 8016 <target>Zaktualizowano kanał wideo
@@ -7988,13 +8033,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7988 <target state="translated">Usunięto baner.</target> 8033 <target state="translated">Usunięto baner.</target>
7989 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 8034 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
7990 </trans-unit> 8035 </trans-unit>
7991 <trans-unit id="2575302837003821736" datatype="html"> 8036
7992 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7993 <target state="translated">Wpisz nazwę kanału (
7994 <x id="PH"/>) aby potwierdzić
7995 </target>
7996 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
7997 </trans-unit>
7998 <trans-unit id="624066830180032195"> 8037 <trans-unit id="624066830180032195">
7999 <source>Video channel <x id="PH"/> deleted.</source> 8038 <source>Video channel <x id="PH"/> deleted.</source>
8000 <target>Usunięto kanał wideo 8039 <target>Usunięto kanał wideo
@@ -8160,6 +8199,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8160 <source>Ownership change request sent.</source> 8199 <source>Ownership change request sent.</source>
8161 <target state="translated">Wysłano prośbę o zmianę właściciela.</target> 8200 <target state="translated">Wysłano prośbę o zmianę właściciela.</target>
8162 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8201 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8202 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8203 <source>Sort by</source><target state="new">Sort by</target>
8204 <context-group purpose="location">
8205 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8206 <context context-type="linenumber">26</context>
8207 </context-group>
8163 </trans-unit> 8208 </trans-unit>
8164 <trans-unit id="3245220240937722814"> 8209 <trans-unit id="3245220240937722814">
8165 <source>My channels</source> 8210 <source>My channels</source>
@@ -8262,7 +8307,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8262 <target state="translated">Subskrybuj to konto</target> 8307 <target state="translated">Subskrybuj to konto</target>
8263 8308
8264 8309
8265 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8310 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8266 <trans-unit id="3131904093925601441" datatype="html"> 8311 <trans-unit id="3131904093925601441" datatype="html">
8267 <source>PLAYLISTS</source> 8312 <source>PLAYLISTS</source>
8268 <target state="translated">PLAYLISTY</target> 8313 <target state="translated">PLAYLISTY</target>
@@ -8309,34 +8354,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8309 <trans-unit id="3779524668013120370" datatype="html"> 8354 <trans-unit id="3779524668013120370" datatype="html">
8310 <source>Go to my subscriptions</source> 8355 <source>Go to my subscriptions</source>
8311 <target state="translated">Przejdź do moich subskrypcji</target> 8356 <target state="translated">Przejdź do moich subskrypcji</target>
8312 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8357
8313 </trans-unit> 8358 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8314 <trans-unit id="1136469849928650779" datatype="html"> 8359 <trans-unit id="1136469849928650779" datatype="html">
8315 <source>Go to my videos</source> 8360 <source>Go to my videos</source>
8316 <target state="translated">Przejdź do moich filmów</target> 8361 <target state="translated">Przejdź do moich filmów</target>
8317 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8362
8318 </trans-unit> 8363 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8319 <trans-unit id="7836683738999600376" datatype="html"> 8364 <trans-unit id="7836683738999600376" datatype="html">
8320 <source>Go to my imports</source> 8365 <source>Go to my imports</source>
8321 <target state="translated">Przejdź do moich importów</target> 8366 <target state="translated">Przejdź do moich importów</target>
8322 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8367
8323 </trans-unit> 8368 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8324 <trans-unit id="7511292153332773503" datatype="html"> 8369 <trans-unit id="7511292153332773503" datatype="html">
8325 <source>Go to my channels</source> 8370 <source>Go to my channels</source>
8326 <target state="translated">Przejdź do moich kanałów</target> 8371 <target state="translated">Przejdź do moich kanałów</target>
8327 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8372
8328 </trans-unit> 8373 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8329 <trans-unit id="2013324644839511073" datatype="html"> 8374 <trans-unit id="2013324644839511073" datatype="html">
8330 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8375 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8331Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8376Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8332 <target state="translated">Nie można uzyskać danych uwierzytelniających klienta OAuth: <x id="PH" equiv-text="error.text"/>. Upewnij się, że PeerTube jest prawidłowo skonfigurowane (katalog config/), szczególnie w sekcji „webserver”.</target> 8377 <target state="translated">Nie można uzyskać danych uwierzytelniających klienta OAuth: <x id="PH" equiv-text="error.text"/>. Upewnij się, że PeerTube jest prawidłowo skonfigurowane (katalog config/), szczególnie w sekcji „webserver”.</target>
8333 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8378
8334 </trans-unit> 8379 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8335 <trans-unit id="375263728166936544"> 8380 <trans-unit id="375263728166936544">
8336 <source>You need to reconnect.</source> 8381 <source>You need to reconnect.</source>
8337 <target>Musisz połączyć się ponownie.</target> 8382 <target>Musisz połączyć się ponownie.</target>
8338 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8383
8339 </trans-unit> 8384 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8340 <trans-unit id="2206638022166154361" datatype="html"> 8385 <trans-unit id="2206638022166154361" datatype="html">
8341 <source>Keyboard Shortcuts:</source> 8386 <source>Keyboard Shortcuts:</source>
8342 <target state="translated">Skróty klawiaturowe:</target> 8387 <target state="translated">Skróty klawiaturowe:</target>
@@ -8349,6 +8394,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8349 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8394 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8350 <context context-type="linenumber">98</context> 8395 <context context-type="linenumber">98</context>
8351 </context-group> 8396 </context-group>
8397 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8398 <source>In my library</source><target state="new">In my library</target>
8399 <context-group purpose="location">
8400 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8401 <context context-type="linenumber">104</context>
8402 </context-group>
8352 </trans-unit> 8403 </trans-unit>
8353 <trans-unit id="232050922346936574" datatype="html"> 8404 <trans-unit id="232050922346936574" datatype="html">
8354 <source>Trending</source> 8405 <source>Trending</source>
@@ -8377,38 +8428,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8377 <trans-unit id="1266887509445371246" datatype="html"> 8428 <trans-unit id="1266887509445371246" datatype="html">
8378 <source>Incorrect username or password.</source> 8429 <source>Incorrect username or password.</source>
8379 <target state="translated">Nieprawidłowa nazwa użytkownika lub hasło.</target> 8430 <target state="translated">Nieprawidłowa nazwa użytkownika lub hasło.</target>
8380 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8431
8381 </trans-unit> 8432 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8382 <trans-unit id="6974874606619467663" datatype="html"> 8433 <trans-unit id="6974874606619467663" datatype="html">
8383 <source>Your account is blocked.</source> 8434 <source>Your account is blocked.</source>
8384 <target state="translated">Twoje konto jest zablokowane</target> 8435 <target state="translated">Twoje konto jest zablokowane</target>
8385 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8436
8386 </trans-unit> 8437 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8387 <trans-unit id="7939914198003891823" datatype="html"> 8438 <trans-unit id="7939914198003891823" datatype="html">
8388 <source>any language</source> 8439 <source>any language</source>
8389 <target state="translated">jakikolwiek język</target> 8440 <target state="translated">jakikolwiek język</target>
8390 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8441
8391 </trans-unit> 8442 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8392 <trans-unit id="5633144232269377096" datatype="html"> 8443 <trans-unit id="5633144232269377096" datatype="html">
8393 <source>hide</source> 8444 <source>hide</source>
8394 <target state="translated">ukryj</target> 8445 <target state="translated">ukryj</target>
8395 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8446
8396 </trans-unit> 8447 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8397 <trans-unit id="8603861867909474404" datatype="html"> 8448 <trans-unit id="8603861867909474404" datatype="html">
8398 <source>blur</source> 8449 <source>blur</source>
8399 <target state="translated">rozmazanie</target> 8450 <target state="translated">rozmazanie</target>
8400 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8451
8401 </trans-unit> 8452 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8402 <trans-unit id="4534458451100881847" datatype="html"> 8453 <trans-unit id="4534458451100881847" datatype="html">
8403 <source>display</source> 8454 <source>display</source>
8404 <target state="translated">wyświetl</target> 8455 <target state="translated">wyświetl</target>
8405 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8456
8406 </trans-unit> 8457 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8407 <trans-unit id="4467323362722952678" datatype="html"> 8458 <trans-unit id="4467323362722952678" datatype="html">
8408 <source>Unknown</source> 8459 <source>Unknown</source>
8409 <target state="translated">Nieznane</target> 8460 <target state="translated">Nieznane</target>
8410 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8461
8411 </trans-unit> 8462 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8412 <trans-unit id="8781423666414310853"> 8463 <trans-unit id="8781423666414310853">
8413 <source>Your password has been successfully reset!</source> 8464 <source>Your password has been successfully reset!</source>
8414 <target>Pomyślnie zresetowano hasło!</target> 8465 <target>Pomyślnie zresetowano hasło!</target>
@@ -10010,18 +10061,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10010 <target>Zbyt wiele prób, spróbuj ponownie za 10061 <target>Zbyt wiele prób, spróbuj ponownie za
10011 <x id="PH"/> minut. 10062 <x id="PH"/> minut.
10012 </target> 10063 </target>
10013 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 10064
10014 </trans-unit> 10065 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
10015 <trans-unit id="4965472196059235310"> 10066 <trans-unit id="4965472196059235310">
10016 <source>Too many attempts, please try again later.</source> 10067 <source>Too many attempts, please try again later.</source>
10017 <target>Zbyt wiele prób, spróbuj ponownie później.</target> 10068 <target>Zbyt wiele prób, spróbuj ponownie później.</target>
10018 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 10069
10019 </trans-unit> 10070 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
10020 <trans-unit id="1693549688987384699"> 10071 <trans-unit id="1693549688987384699">
10021 <source>Server error. Please retry later.</source> 10072 <source>Server error. Please retry later.</source>
10022 <target>Błąd serwera. Spróbuj ponownie później.</target> 10073 <target>Błąd serwera. Spróbuj ponownie później.</target>
10023 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 10074
10024 </trans-unit> 10075 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
10025 <trans-unit id="5927402622550505067" datatype="html"> 10076 <trans-unit id="5927402622550505067" datatype="html">
10026 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 10077 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
10027 <target state="translated">Zasubskrybowano wszystkie kanały użytkownika 10078 <target state="translated">Zasubskrybowano wszystkie kanały użytkownika
@@ -10621,35 +10672,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10621 <trans-unit id="3284171506518522275" datatype="html"> 10672 <trans-unit id="3284171506518522275" datatype="html">
10622 <source>Your video was uploaded to your account and is private.</source> 10673 <source>Your video was uploaded to your account and is private.</source>
10623 <target state="translated">Film został wrzucony na twoje konto i jest prywatny.</target> 10674 <target state="translated">Film został wrzucony na twoje konto i jest prywatny.</target>
10624 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10675
10625 </trans-unit> 10676 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10626 <trans-unit id="5699822024600815733"> 10677 <trans-unit id="5699822024600815733">
10627 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10678 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10628 <target>Powiązane dane (tagi, opis…) zostaną utracone, czy na pewno chcesz opuścić tą stronę?</target> 10679 <target>Powiązane dane (tagi, opis…) zostaną utracone, czy na pewno chcesz opuścić tą stronę?</target>
10629 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10680
10630 </trans-unit> 10681 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10631 <trans-unit id="1219739004043110649"> 10682 <trans-unit id="1219739004043110649">
10632 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10683 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10633 <target>Twój film nie został jeszcze wysłany, czy na pewno chcesz opuścić tą stronę?</target> 10684 <target>Twój film nie został jeszcze wysłany, czy na pewno chcesz opuścić tą stronę?</target>
10634 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10685
10635 </trans-unit> 10686 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10636 <trans-unit id="6932865105766151309" datatype="html"> 10687 <trans-unit id="6932865105766151309" datatype="html">
10637 <source>Upload</source> 10688 <source>Upload</source>
10638 <target state="translated">Wyślij</target> 10689 <target state="translated">Wyślij</target>
10639 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10690
10640 </trans-unit> 10691 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10641 <trans-unit id="8278735427925094503" datatype="html"> 10692 <trans-unit id="8278735427925094503" datatype="html">
10642 <source>Upload <x id="PH"/> </source> 10693 <source>Upload <x id="PH"/> </source>
10643 <target state="translated">Wrzuć 10694 <target state="translated">Wrzuć
10644 <x id="PH"/> 10695 <x id="PH"/>
10645 </target> 10696 </target>
10646 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10697
10647 </trans-unit> 10698 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10648 <trans-unit id="5981816353437801748"> 10699 <trans-unit id="5981816353437801748">
10649 <source>Video published.</source> 10700 <source>Video published.</source>
10650 <target>Opublikowano film.</target> 10701 <target>Opublikowano film.</target>
10651 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10702
10652 </trans-unit> 10703 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10653 <trans-unit id="764164089183618119" datatype="html"> 10704 <trans-unit id="764164089183618119" datatype="html">
10654 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10705 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10655 <target state="translated">Masz niezapisane zmiany! Jeżeli zamkniesz to okno, twoje zmiany zostaną stracone.</target> 10706 <target state="translated">Masz niezapisane zmiany! Jeżeli zamkniesz to okno, twoje zmiany zostaną stracone.</target>
@@ -10716,28 +10767,28 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10716 <trans-unit id="961774488937452220" datatype="html"> 10767 <trans-unit id="961774488937452220" datatype="html">
10717 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10768 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10718 <target state="translated">Ten film nie jest dostępny na tej instancji. Czy chcesz zostać przekierowany(-a) na instancję źródłową: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10769 <target state="translated">Ten film nie jest dostępny na tej instancji. Czy chcesz zostać przekierowany(-a) na instancję źródłową: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10719 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group> 10770
10720 </trans-unit> 10771 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10721 <trans-unit id="5761611056224181752" datatype="html"> 10772 <trans-unit id="5761611056224181752" datatype="html">
10722 <source>Redirection</source> 10773 <source>Redirection</source>
10723 <target state="translated">Przekierowanie</target> 10774 <target state="translated">Przekierowanie</target>
10724 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group> 10775
10725 </trans-unit> 10776 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10726 <trans-unit id="8858527736400081688"> 10777 <trans-unit id="8858527736400081688">
10727 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10778 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10728 <target>Ten film zawiera treści wulgarne lub przeznaczone dla dorosłych. Czy na pewno chcesz go obejrzeć?</target> 10779 <target>Ten film zawiera treści wulgarne lub przeznaczone dla dorosłych. Czy na pewno chcesz go obejrzeć?</target>
10729 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group> 10780
10730 </trans-unit> 10781 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10731 <trans-unit id="3937119019020041049"> 10782 <trans-unit id="3937119019020041049">
10732 <source>Mature or explicit content</source> 10783 <source>Mature or explicit content</source>
10733 <target>Zawartość wulgarna lub dla dorosłych</target> 10784 <target>Zawartość wulgarna lub dla dorosłych</target>
10734 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group> 10785
10735 </trans-unit> 10786 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10736 <trans-unit id="1755474755114288376" datatype="html"> 10787 <trans-unit id="1755474755114288376" datatype="html">
10737 <source>Up Next</source> 10788 <source>Up Next</source>
10738 <target state="translated">Następnie</target> 10789 <target state="translated">Następnie</target>
10739 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group> 10790
10740 </trans-unit> 10791 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10741 <trans-unit id="2159130950882492111" datatype="html"> 10792 <trans-unit id="2159130950882492111" datatype="html">
10742 <source>Cancel</source> 10793 <source>Cancel</source>
10743 <target state="translated">Anuluj</target> 10794 <target state="translated">Anuluj</target>
@@ -10746,63 +10797,63 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10746 <trans-unit id="3354816756665089864" datatype="html"> 10797 <trans-unit id="3354816756665089864" datatype="html">
10747 <source>Autoplay is suspended</source> 10798 <source>Autoplay is suspended</source>
10748 <target state="translated">Automatyczne odtwarzanie jest zatrzymane</target> 10799 <target state="translated">Automatyczne odtwarzanie jest zatrzymane</target>
10749 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group> 10800
10750 </trans-unit> 10801 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10751 <trans-unit id="7895294730547405228" datatype="html"> 10802 <trans-unit id="7895294730547405228" datatype="html">
10752 <source>Enter/exit fullscreen (requires player focus)</source> 10803 <source>Enter/exit fullscreen (requires player focus)</source>
10753 <target state="translated">Włącz/wyłącz tryb pełnoekranowy (kiedy zaznaczony jest odtwarzacz)</target> 10804 <target state="translated">Włącz/wyłącz tryb pełnoekranowy (kiedy zaznaczony jest odtwarzacz)</target>
10754 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group> 10805
10755 </trans-unit> 10806 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10756 <trans-unit id="7618388257165864759" datatype="html"> 10807 <trans-unit id="7618388257165864759" datatype="html">
10757 <source>Play/Pause the video (requires player focus)</source> 10808 <source>Play/Pause the video (requires player focus)</source>
10758 <target state="translated">Odtwarzaj/spauzuj film (kiedy zaznaczony jest odtwarzacz)</target> 10809 <target state="translated">Odtwarzaj/spauzuj film (kiedy zaznaczony jest odtwarzacz)</target>
10759 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group> 10810
10760 </trans-unit> 10811 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10761 <trans-unit id="7761890399634216630" datatype="html"> 10812 <trans-unit id="7761890399634216630" datatype="html">
10762 <source>Mute/unmute the video (requires player focus)</source> 10813 <source>Mute/unmute the video (requires player focus)</source>
10763 <target state="translated">Wycisz/wyłącz wyciszenie filmu (kiedy zaznaczony jest odtwarzacz)</target> 10814 <target state="translated">Wycisz/wyłącz wyciszenie filmu (kiedy zaznaczony jest odtwarzacz)</target>
10764 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group> 10815
10765 </trans-unit> 10816 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10766 <trans-unit id="5996585232248234904" datatype="html"> 10817 <trans-unit id="5996585232248234904" datatype="html">
10767 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10818 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10768 <target state="translated">Przeskocz do procentowej części filmu: 0 to 0% i 9 to 90% (kiedy zaznaczony jest odtwarzacz)</target> 10819 <target state="translated">Przeskocz do procentowej części filmu: 0 to 0% i 9 to 90% (kiedy zaznaczony jest odtwarzacz)</target>
10769 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group> 10820
10770 </trans-unit> 10821 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10771 <trans-unit id="3748765405903319998" datatype="html"> 10822 <trans-unit id="3748765405903319998" datatype="html">
10772 <source>Increase the volume (requires player focus)</source> 10823 <source>Increase the volume (requires player focus)</source>
10773 <target state="translated">Zwiększ głośność (kiedy zaznaczony jest odtwarzacz)</target> 10824 <target state="translated">Zwiększ głośność (kiedy zaznaczony jest odtwarzacz)</target>
10774 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group> 10825
10775 </trans-unit> 10826 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10776 <trans-unit id="5810704036407159982" datatype="html"> 10827 <trans-unit id="5810704036407159982" datatype="html">
10777 <source>Decrease the volume (requires player focus)</source> 10828 <source>Decrease the volume (requires player focus)</source>
10778 <target state="translated">Zmniejsz głośność (kiedy zaznaczony jest odtwarzacz)</target> 10829 <target state="translated">Zmniejsz głośność (kiedy zaznaczony jest odtwarzacz)</target>
10779 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group> 10830
10780 </trans-unit> 10831 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10781 <trans-unit id="2622048822548065691" datatype="html"> 10832 <trans-unit id="2622048822548065691" datatype="html">
10782 <source>Seek the video forward (requires player focus)</source> 10833 <source>Seek the video forward (requires player focus)</source>
10783 <target state="translated">Przeskocz dalej w filmie (kiedy zaznaczony jest odtwarzacz)</target> 10834 <target state="translated">Przeskocz dalej w filmie (kiedy zaznaczony jest odtwarzacz)</target>
10784 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group> 10835
10785 </trans-unit> 10836 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10786 <trans-unit id="6540078205109221153" datatype="html"> 10837 <trans-unit id="6540078205109221153" datatype="html">
10787 <source>Seek the video backward (requires player focus)</source> 10838 <source>Seek the video backward (requires player focus)</source>
10788 <target state="translated">Przeskocz do tyłu w filmie (kiedy zaznaczony jest odtwarzacz)</target> 10839 <target state="translated">Przeskocz do tyłu w filmie (kiedy zaznaczony jest odtwarzacz)</target>
10789 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group> 10840
10790 </trans-unit> 10841 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10791 <trans-unit id="1956491957766210808" datatype="html"> 10842 <trans-unit id="1956491957766210808" datatype="html">
10792 <source>Increase playback rate (requires player focus)</source> 10843 <source>Increase playback rate (requires player focus)</source>
10793 <target state="translated">Przyspiesz odtwarzanie (kiedy zaznaczony jest odtwarzacz)</target> 10844 <target state="translated">Przyspiesz odtwarzanie (kiedy zaznaczony jest odtwarzacz)</target>
10794 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group> 10845
10795 </trans-unit> 10846 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10796 <trans-unit id="5495529997674803186" datatype="html"> 10847 <trans-unit id="5495529997674803186" datatype="html">
10797 <source>Decrease playback rate (requires player focus)</source> 10848 <source>Decrease playback rate (requires player focus)</source>
10798 <target state="translated">Zwolnij odtwarzanie (kiedy zaznaczony jest odtwarzacz)</target> 10849 <target state="translated">Zwolnij odtwarzanie (kiedy zaznaczony jest odtwarzacz)</target>
10799 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group> 10850
10800 </trans-unit> 10851 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10801 <trans-unit id="3178343147230721210" datatype="html"> 10852 <trans-unit id="3178343147230721210" datatype="html">
10802 <source>Navigate in the video frame by frame (requires player focus)</source> 10853 <source>Navigate in the video frame by frame (requires player focus)</source>
10803 <target state="translated">Przeglądaj film klatka po klatce (kiedy zaznaczony jest odtwarzacz)</target> 10854 <target state="translated">Przeglądaj film klatka po klatce (kiedy zaznaczony jest odtwarzacz)</target>
10804 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group> 10855
10805 </trans-unit> 10856 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10806 <trans-unit id="8025996572234182184" datatype="html"> 10857 <trans-unit id="8025996572234182184" datatype="html">
10807 <source>Like the video</source> 10858 <source>Like the video</source>
10808 <target state="translated">Zaznacz "Lubię to"</target> 10859 <target state="translated">Zaznacz "Lubię to"</target>
diff --git a/client/src/locale/angular.pt-BR.xlf b/client/src/locale/angular.pt-BR.xlf
index da2526596..3ee4bd99f 100644
--- a/client/src/locale/angular.pt-BR.xlf
+++ b/client/src/locale/angular.pt-BR.xlf
@@ -244,7 +244,7 @@
244 244
245 245
246 246
247 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 247 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
248 <trans-unit id="1486537403020619891" datatype="html"> 248 <trans-unit id="1486537403020619891" datatype="html">
249 <source>My watch history</source> 249 <source>My watch history</source>
250 <target state="new">My watch history</target> 250 <target state="new">My watch history</target>
@@ -313,23 +313,23 @@
313 <trans-unit id="5674286808255988565"> 313 <trans-unit id="5674286808255988565">
314 <source>Create</source> 314 <source>Create</source>
315 <target>Criar</target> 315 <target>Criar</target>
316 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 316
317 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 317
318 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 318
319 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 319
320 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 320
321 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 321
322 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 322
323 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 323
324 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 324
325 </trans-unit> 325 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
326 <trans-unit id="1006562256968398209" datatype="html"> 326 <trans-unit id="1006562256968398209" datatype="html">
327 <source>video</source> 327 <source>video</source>
328 <target state="translated">vídeo</target> 328 <target state="translated">vídeo</target>
329 329
330 330
331 331
332 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 332 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
333 <trans-unit id="6438815964972582865" datatype="html"> 333 <trans-unit id="6438815964972582865" datatype="html">
334 <source>The following link contains a private token and should not be shared with anyone.</source> 334 <source>The following link contains a private token and should not be shared with anyone.</source>
335 <target state="new"> The following link contains a private token and should not be shared with anyone. </target> 335 <target state="new"> The following link contains a private token and should not be shared with anyone. </target>
@@ -403,12 +403,12 @@
403 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 403 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
404 <target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 404 <target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
405 405
406 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit> 406 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
407 <trans-unit id="7873395933409147217" datatype="html"> 407 <trans-unit id="7873395933409147217" datatype="html">
408 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 408 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
409 <target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 409 <target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
410 410
411 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 411 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
412 <trans-unit id="5235042777215655908" datatype="html"> 412 <trans-unit id="5235042777215655908" datatype="html">
413 <source>subtitles</source> 413 <source>subtitles</source>
414 <target state="translated">subtítulos</target> 414 <target state="translated">subtítulos</target>
@@ -861,10 +861,10 @@
861 <trans-unit id="2602586221576511475"> 861 <trans-unit id="2602586221576511475">
862 <source>Video quota</source> 862 <source>Video quota</source>
863 <target>Quota de vídeo</target> 863 <target>Quota de vídeo</target>
864 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 864
865 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 865
866 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 866
867 </trans-unit> 867 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
868 <trans-unit id="1502595455339510144"> 868 <trans-unit id="1502595455339510144">
869 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 869 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
870 <target> 870 <target>
@@ -949,6 +949,30 @@
949 <target state="translated">Federação</target> 949 <target state="translated">Federação</target>
950 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 950 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
951 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 951 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
952 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
953 <source>Following</source><target state="new">Following</target>
954 <context-group purpose="location">
955 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
956 <context context-type="linenumber">29</context>
957 </context-group>
958 <context-group purpose="location">
959 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
960 <context context-type="linenumber">31</context>
961 </context-group>
962 <context-group purpose="location">
963 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
964 <context context-type="linenumber">28</context>
965 </context-group>
966 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
967 <source>Followers</source><target state="new">Followers</target>
968 <context-group purpose="location">
969 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
970 <context context-type="linenumber">34</context>
971 </context-group>
972 <context-group purpose="location">
973 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
974 <context context-type="linenumber">37</context>
975 </context-group>
952 </trans-unit> 976 </trans-unit>
953 <trans-unit id="3541687134897970106" datatype="html"> 977 <trans-unit id="3541687134897970106" datatype="html">
954 <source>followers</source> 978 <source>followers</source>
@@ -1030,7 +1054,7 @@
1030 1054
1031 1055
1032 1056
1033 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 1057 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
1034 <trans-unit id="3616223838716839702"> 1058 <trans-unit id="3616223838716839702">
1035 <source>Ban this user</source> 1059 <source>Ban this user</source>
1036 <target>Banir este usuário</target> 1060 <target>Banir este usuário</target>
@@ -1099,19 +1123,13 @@
1099 <trans-unit id="7252854992688790751" datatype="html"> 1123 <trans-unit id="7252854992688790751" datatype="html">
1100 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1124 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1101 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1125 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1102 <context-group purpose="location"> 1126
1103 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1127 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1104 <context context-type="linenumber">60,62</context>
1105 </context-group>
1106 </trans-unit>
1107 <trans-unit id="7215649348148521605" datatype="html"> 1128 <trans-unit id="7215649348148521605" datatype="html">
1108 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1129 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1109 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1130 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1110 <context-group purpose="location"> 1131
1111 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1132 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1112 <context context-type="linenumber">65,67</context>
1113 </context-group>
1114 </trans-unit>
1115 <trans-unit id="2392488717875840729"> 1133 <trans-unit id="2392488717875840729">
1116 <source>User</source> 1134 <source>User</source>
1117 <target>Usuário</target> 1135 <target>Usuário</target>
@@ -1122,67 +1140,67 @@
1122 <source>Username or email address</source> 1140 <source>Username or email address</source>
1123 <target>Nome de usuário ou endereço de e-mail</target> 1141 <target>Nome de usuário ou endereço de e-mail</target>
1124 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1142 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1143 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1144 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1145 <context-group purpose="location">
1146 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1147 <context context-type="linenumber">33,34</context>
1148 </context-group>
1125 </trans-unit> 1149 </trans-unit>
1126 <trans-unit id="1431416938026210429"> 1150 <trans-unit id="1431416938026210429">
1127 <source>Password</source> 1151 <source>Password</source>
1128 <target>Senha</target> 1152 <target>Senha</target>
1129 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1153
1130 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1154
1131 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1155
1132 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1156
1133 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1157
1134 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1158
1135 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1159
1136 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1160
1137 </trans-unit> 1161 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1138 <trans-unit id="8715156686857791956" datatype="html"> 1162 <trans-unit id="8715156686857791956" datatype="html">
1139 <source>Click here to reset your password</source> 1163 <source>Click here to reset your password</source>
1140 <target state="translated">Clique aqui para redefinir sua senha</target> 1164 <target state="translated">Clique aqui para redefinir sua senha</target>
1141 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1165
1142 </trans-unit> 1166 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1143 <trans-unit id="892063502898494584" datatype="html"> 1167 <trans-unit id="892063502898494584" datatype="html">
1144 <source>I forgot my password</source> 1168 <source>I forgot my password</source>
1145 <target state="new">I forgot my password</target> 1169 <target state="new">I forgot my password</target>
1146 <context-group purpose="location"> 1170
1147 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1171 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1148 <context context-type="linenumber">47</context>
1149 </context-group>
1150 </trans-unit>
1151 <trans-unit id="2101170466365500913" datatype="html"> 1172 <trans-unit id="2101170466365500913" datatype="html">
1152 <source>Logging into an account lets you publish content</source> 1173 <source>Logging into an account lets you publish content</source>
1153 <target state="new"> Logging into an account lets you publish content </target> 1174 <target state="new"> Logging into an account lets you publish content </target>
1154 <context-group purpose="location"> 1175
1155 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1176 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1156 <context context-type="linenumber">56,57</context>
1157 </context-group>
1158 </trans-unit>
1159 <trans-unit id="2454050363478003966"> 1177 <trans-unit id="2454050363478003966">
1160 <source>Login</source> 1178 <source>Login</source>
1161 <target>Entrar</target> 1179 <target>Entrar</target>
1162 1180
1163 1181
1164 1182
1165 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1183 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1166 <trans-unit id="3183213940445113677" datatype="html"> 1184 <trans-unit id="3183213940445113677" datatype="html">
1167 <source>Or sign in with</source> 1185 <source>Or sign in with</source>
1168 <target state="new">Or sign in with</target> 1186 <target state="new">Or sign in with</target>
1169 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1187
1170 </trans-unit> 1188 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1171 <trans-unit id="3238209155172574367"> 1189 <trans-unit id="3238209155172574367">
1172 <source>Forgot your password</source> 1190 <source>Forgot your password</source>
1173 <target>Esqueceu sua senha</target> 1191 <target>Esqueceu sua senha</target>
1174 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1192
1175 </trans-unit> 1193 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1176 <trans-unit id="87327320394367488" datatype="html"> 1194 <trans-unit id="87327320394367488" datatype="html">
1177 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1195 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1178 <target state="translated">Lamentamos, não podemos recuperar sua senha porque o administrador dessa instância não ativou o envio de e-mail pelo PeerTube.</target> 1196 <target state="translated">Lamentamos, não podemos recuperar sua senha porque o administrador dessa instância não ativou o envio de e-mail pelo PeerTube.</target>
1179 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1197
1180 </trans-unit> 1198 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1181 <trans-unit id="3188014010833256853" datatype="html"> 1199 <trans-unit id="3188014010833256853" datatype="html">
1182 <source>Enter your email address and we will send you a link to reset your password.</source> 1200 <source>Enter your email address and we will send you a link to reset your password.</source>
1183 <target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1201 <target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1184 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1202
1185 </trans-unit> 1203 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1186 <trans-unit id="1190256911880544559" datatype="html"> 1204 <trans-unit id="1190256911880544559" datatype="html">
1187 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1205 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1188The link will expire within 1 hour.</source> 1206The link will expire within 1 hour.</source>
@@ -1193,26 +1211,26 @@ The link will expire within 1 hour.</target>
1193 <trans-unit id="4768749765465246664"> 1211 <trans-unit id="4768749765465246664">
1194 <source>Email</source> 1212 <source>Email</source>
1195 <target>E-mail</target> 1213 <target>E-mail</target>
1196 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1214
1197 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1215
1198 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1216
1199 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1217
1200 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1218
1201 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1219
1202 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1220
1203 </trans-unit> 1221 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1204 <trans-unit id="3967269098753656610"> 1222 <trans-unit id="3967269098753656610">
1205 <source>Email address</source> 1223 <source>Email address</source>
1206 <target>Endereço de e-mail</target> 1224 <target>Endereço de e-mail</target>
1207 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1225
1208 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1226
1209 </trans-unit> 1227 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1210 <trans-unit id="7808756054397155068" datatype="html"> 1228 <trans-unit id="7808756054397155068" datatype="html">
1211 <source>Reset</source> 1229 <source>Reset</source>
1212 <target state="new">Reset</target> 1230 <target state="new">Reset</target>
1213 <note priority="1" from="description">Password reset button</note> 1231 <note priority="1" from="description">Password reset button</note>
1214 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1232
1215 </trans-unit> 1233 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1216 <trans-unit id="4319634264526091601" datatype="html"> 1234 <trans-unit id="4319634264526091601" datatype="html">
1217 <source>on this instance</source> 1235 <source>on this instance</source>
1218 <target state="new">on this instance</target> 1236 <target state="new">on this instance</target>
@@ -1576,7 +1594,7 @@ The link will expire within 1 hour.</target>
1576 <target>Criar uma conta</target> 1594 <target>Criar uma conta</target>
1577 1595
1578 1596
1579 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1597 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1580 1598
1581 <trans-unit id="3058024914967508975" datatype="html"> 1599 <trans-unit id="3058024914967508975" datatype="html">
1582 <source>My videos</source> 1600 <source>My videos</source>
@@ -1644,7 +1662,7 @@ The link will expire within 1 hour.</target>
1644 1662
1645 1663
1646 1664
1647 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 1665 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1648 <trans-unit id="667372110624203230" datatype="html"> 1666 <trans-unit id="667372110624203230" datatype="html">
1649 <source>Import jobs concurrency</source> 1667 <source>Import jobs concurrency</source>
1650 <target state="new">Import jobs concurrency</target> 1668 <target state="new">Import jobs concurrency</target>
@@ -1722,8 +1740,8 @@ The link will expire within 1 hour.</target>
1722 <trans-unit id="4424964105331349857" datatype="html"> 1740 <trans-unit id="4424964105331349857" datatype="html">
1723 <source>I'm a teapot</source> 1741 <source>I'm a teapot</source>
1724 <target state="new">I'm a teapot</target> 1742 <target state="new">I'm a teapot</target>
1725 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1743
1726 </trans-unit> 1744 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1727 <trans-unit id="1597262876035959248" datatype="html"> 1745 <trans-unit id="1597262876035959248" datatype="html">
1728 <source>That's an error.</source> 1746 <source>That's an error.</source>
1729 <target state="new">That's an error.</target> 1747 <target state="new">That's an error.</target>
@@ -1816,8 +1834,8 @@ The link will expire within 1 hour.</target>
1816 <trans-unit id="2971365540217107489" datatype="html"> 1834 <trans-unit id="2971365540217107489" datatype="html">
1817 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1835 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1818 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1836 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1819 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1837
1820 </trans-unit> 1838 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1821 1839
1822 <trans-unit id="5131854469652959713" datatype="html"> 1840 <trans-unit id="5131854469652959713" datatype="html">
1823 <source>GLOBAL SEARCH</source> 1841 <source>GLOBAL SEARCH</source>
@@ -2579,7 +2597,7 @@ The link will expire within 1 hour.</target>
2579 <source>Upload on hold</source> 2597 <source>Upload on hold</source>
2580 <target state="new">Upload on hold</target> 2598 <target state="new">Upload on hold</target>
2581 2599
2582 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2600 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2583 <trans-unit id="285180972645018518" datatype="html"> 2601 <trans-unit id="285180972645018518" datatype="html">
2584 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2602 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2585 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2603 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3296,11 +3314,7 @@ The link will expire within 1 hour.</target>
3296 <target>ID</target> 3314 <target>ID</target>
3297 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3315 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3298 </trans-unit> 3316 </trans-unit>
3299 <trans-unit id="2265605798180116441" datatype="html"> 3317
3300 <source>Follower handle</source>
3301 <target state="translated">Identificador de inscritos</target>
3302 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3303 </trans-unit>
3304 <trans-unit id="5911214550882917183"> 3318 <trans-unit id="5911214550882917183">
3305 <source>State</source> 3319 <source>State</source>
3306 <target>Estado</target> 3320 <target>Estado</target>
@@ -3375,11 +3389,7 @@ The link will expire within 1 hour.</target>
3375 </target> 3389 </target>
3376 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3390 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3377 </trans-unit> 3391 </trans-unit>
3378 <trans-unit id="6641024648411549335"> 3392
3379 <source>Host</source>
3380 <target>Host</target>
3381 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3382 </trans-unit>
3383 <trans-unit id="6571718060636962350" datatype="html"> 3393 <trans-unit id="6571718060636962350" datatype="html">
3384 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3394 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3385 <target state="translated">Redundância permitida 3395 <target state="translated">Redundância permitida
@@ -3391,9 +3401,9 @@ The link will expire within 1 hour.</target>
3391 <trans-unit id="9160510009013134726" datatype="html"> 3401 <trans-unit id="9160510009013134726" datatype="html">
3392 <source>Unfollow</source> 3402 <source>Unfollow</source>
3393 <target state="new">Unfollow</target> 3403 <target state="new">Unfollow</target>
3394 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3404
3395 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3405
3396 </trans-unit> 3406 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3397 <trans-unit id="8246779176913476983" datatype="html"> 3407 <trans-unit id="8246779176913476983" datatype="html">
3398 <source>Open instance in a new tab</source> 3408 <source>Open instance in a new tab</source>
3399 <target state="translated">Abrir instância em uma nova aba</target> 3409 <target state="translated">Abrir instância em uma nova aba</target>
@@ -3404,13 +3414,13 @@ The link will expire within 1 hour.</target>
3404 <trans-unit id="9132918641931433659" datatype="html"> 3414 <trans-unit id="9132918641931433659" datatype="html">
3405 <source>No host found matching current filters.</source> 3415 <source>No host found matching current filters.</source>
3406 <target state="translated">Nenhum host encontrado correspondendo aos filtros atuais.</target> 3416 <target state="translated">Nenhum host encontrado correspondendo aos filtros atuais.</target>
3407 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3417
3408 </trans-unit> 3418 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3409 <trans-unit id="7274241885665071790" datatype="html"> 3419 <trans-unit id="7274241885665071790" datatype="html">
3410 <source>Your instance is not following anyone.</source> 3420 <source>Your instance is not following anyone.</source>
3411 <target state="translated">Sua instância não possui seguidores.</target> 3421 <target state="translated">Sua instância não possui seguidores.</target>
3412 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3422
3413 </trans-unit> 3423 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3414 <trans-unit id="4774348799569692380" datatype="html"> 3424 <trans-unit id="4774348799569692380" datatype="html">
3415 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3425 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3416 <target state="translated">Mostrando 3426 <target state="translated">Mostrando
@@ -3420,16 +3430,8 @@ The link will expire within 1 hour.</target>
3420 </target> 3430 </target>
3421 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3431 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3422 </trans-unit> 3432 </trans-unit>
3423 <trans-unit id="6275803119759621687" datatype="html"> 3433
3424 <source>Follow domains</source> 3434
3425 <target state="translated">Seguir domínios</target>
3426 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3427 </trans-unit>
3428 <trans-unit id="1268699198448750610" datatype="html">
3429 <source>Follow instances</source>
3430 <target state="new">Follow instances</target>
3431 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3432 </trans-unit>
3433 <trans-unit id="9216117865911519658" datatype="html"> 3435 <trans-unit id="9216117865911519658" datatype="html">
3434 <source>Action</source> 3436 <source>Action</source>
3435 <target state="new">Action</target> 3437 <target state="new">Action</target>
@@ -3479,11 +3481,11 @@ The link will expire within 1 hour.</target>
3479 <trans-unit id="5248717555542428023"> 3481 <trans-unit id="5248717555542428023">
3480 <source>Username</source> 3482 <source>Username</source>
3481 <target>Nome de usuário</target> 3483 <target>Nome de usuário</target>
3482 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3484
3483 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3485
3484 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3486
3485 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3487
3486 </trans-unit> 3488 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3487 <trans-unit id="5428411040014095392" datatype="html"> 3489 <trans-unit id="5428411040014095392" datatype="html">
3488 <source>e.g. jane_doe</source> 3490 <source>e.g. jane_doe</source>
3489 <target state="new">e.g. jane_doe</target> 3491 <target state="new">e.g. jane_doe</target>
@@ -3511,9 +3513,9 @@ The link will expire within 1 hour.</target>
3511 <trans-unit id="4145496584631696119"> 3513 <trans-unit id="4145496584631696119">
3512 <source>Role</source> 3514 <source>Role</source>
3513 <target>Papel</target> 3515 <target>Papel</target>
3514 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3516
3515 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3517
3516 </trans-unit> 3518 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3517 <trans-unit id="7046347992315328430" datatype="html"> 3519 <trans-unit id="7046347992315328430" datatype="html">
3518 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3520 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3519 <target state="translated">A transcodificação está habilitada. A quota de vídeo só leva em consideração o tamanho do vídeo 3521 <target state="translated">A transcodificação está habilitada. A quota de vídeo só leva em consideração o tamanho do vídeo
@@ -3535,15 +3537,9 @@ The link will expire within 1 hour.</target>
3535 <trans-unit id="2622255144026150901" datatype="html"> 3537 <trans-unit id="2622255144026150901" datatype="html">
3536 <source>Auth plugin</source> 3538 <source>Auth plugin</source>
3537 <target state="new">Auth plugin</target> 3539 <target state="new">Auth plugin</target>
3538 <context-group purpose="location"> 3540
3539 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3541
3540 <context context-type="linenumber">188</context> 3542 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3541 </context-group>
3542 <context-group purpose="location">
3543 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3544 <context context-type="linenumber">188</context>
3545 </context-group>
3546 </trans-unit>
3547 <trans-unit id="588099657508661970" datatype="html"> 3543 <trans-unit id="588099657508661970" datatype="html">
3548 <source>None (local authentication)</source> 3544 <source>None (local authentication)</source>
3549 <target state="new">None (local authentication)</target> 3545 <target state="new">None (local authentication)</target>
@@ -3808,6 +3804,12 @@ The link will expire within 1 hour.</target>
3808 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3804 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3809 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3805 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3810 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3806 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3807 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3808 <source>Follower</source><target state="new">Follower</target>
3809 <context-group purpose="location">
3810 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3811 <context context-type="linenumber">24</context>
3812 </context-group>
3811 </trans-unit> 3813 </trans-unit>
3812 <trans-unit id="4691552465058437520" datatype="html"> 3814 <trans-unit id="4691552465058437520" datatype="html">
3813 <source>Commented video</source> 3815 <source>Commented video</source>
@@ -4148,8 +4150,8 @@ The link will expire within 1 hour.</target>
4148 <target state="new"> 4150 <target state="new">
4149 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 4151 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
4150 </target> 4152 </target>
4151 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 4153
4152 </trans-unit> 4154 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
4153 <trans-unit id="4058814854824495833" datatype="html"> 4155 <trans-unit id="4058814854824495833" datatype="html">
4154 <source>Mute domains</source> 4156 <source>Mute domains</source>
4155 <target state="new">Mute domains</target> 4157 <target state="new">Mute domains</target>
@@ -6138,11 +6140,8 @@ color: red;
6138 <trans-unit id="5512878593724620692" datatype="html"> 6140 <trans-unit id="5512878593724620692" datatype="html">
6139 <source>CHANNELS</source> 6141 <source>CHANNELS</source>
6140 <target state="new">CHANNELS</target> 6142 <target state="new">CHANNELS</target>
6141 <context-group purpose="location"> 6143
6142 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 6144 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
6143 <context context-type="linenumber">82</context>
6144 </context-group>
6145 </trans-unit>
6146 <trans-unit id="3666829335406793239" datatype="html"> 6145 <trans-unit id="3666829335406793239" datatype="html">
6147 <source>This account does not have channels.</source> 6146 <source>This account does not have channels.</source>
6148 <target state="translated">Essa conta não possui canais.</target> 6147 <target state="translated">Essa conta não possui canais.</target>
@@ -6187,6 +6186,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6187It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 6186It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
6188channel with the same name (<x id="PH_2"/>)!</target> 6187channel with the same name (<x id="PH_2"/>)!</target>
6189 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 6188 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
6189 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
6190 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
6191 <context-group purpose="location">
6192 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
6193 <context context-type="linenumber">48</context>
6194 </context-group>
6190 </trans-unit> 6195 </trans-unit>
6191 <trans-unit id="5387007581996837469" datatype="html"> 6196 <trans-unit id="5387007581996837469" datatype="html">
6192 <source>My Channels</source> 6197 <source>My Channels</source>
@@ -6728,12 +6733,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6728 <source>Your message has been sent.</source> 6733 <source>Your message has been sent.</source>
6729 <target>Sua mensagem foi enviada.</target> 6734 <target>Sua mensagem foi enviada.</target>
6730 6735
6731 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6736 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6732 <trans-unit id="2072135752262464360"> 6737 <trans-unit id="2072135752262464360">
6733 <source>You already sent this form recently</source> 6738 <source>You already sent this form recently</source>
6734 <target>Você já enviou este formulário recentemente</target> 6739 <target>Você já enviou este formulário recentemente</target>
6735 6740
6736 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6741 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6737 <trans-unit id="819067926858619041" datatype="html"> 6742 <trans-unit id="819067926858619041" datatype="html">
6738 <source>Account videos</source> 6743 <source>Account videos</source>
6739 <target state="new">Account videos</target> 6744 <target state="new">Account videos</target>
@@ -6779,13 +6784,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6779 <target state="new"> 6784 <target state="new">
6780 <x id="PH"/> direct account followers 6785 <x id="PH"/> direct account followers
6781 </target> 6786 </target>
6782 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6787
6783 </trans-unit> 6788 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6784 <trans-unit id="6250999352462648289" datatype="html"> 6789 <trans-unit id="6250999352462648289" datatype="html">
6785 <source>Report this account</source> 6790 <source>Report this account</source>
6786 <target state="new">Report this account</target> 6791 <target state="new">Report this account</target>
6787 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6792
6788 </trans-unit> 6793 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6789 <trans-unit id="1504521795586863905" datatype="html"> 6794 <trans-unit id="1504521795586863905" datatype="html">
6790 <source>VIDEOS</source> 6795 <source>VIDEOS</source>
6791 <target state="new">VIDEOS</target> 6796 <target state="new">VIDEOS</target>
@@ -6795,31 +6800,21 @@ channel with the same name (<x id="PH_2"/>)!</target>
6795 <trans-unit id="25349740244798533" datatype="html"> 6800 <trans-unit id="25349740244798533" datatype="html">
6796 <source>Username copied</source> 6801 <source>Username copied</source>
6797 <target state="translated">Nome de usuário copiado</target> 6802 <target state="translated">Nome de usuário copiado</target>
6798 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6803
6799 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6804
6800 </trans-unit> 6805 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6801 <trans-unit id="9221735175659318025" datatype="html"> 6806 <trans-unit id="9221735175659318025" datatype="html">
6802 <source>1 subscriber</source> 6807 <source>1 subscriber</source>
6803 <target state="new">1 subscriber</target> 6808 <target state="new">1 subscriber</target>
6804 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6809
6805 </trans-unit> 6810 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6806 <trans-unit id="4097331874769079975" datatype="html"> 6811 <trans-unit id="4097331874769079975" datatype="html">
6807 <source><x id="PH"/> subscribers</source> 6812 <source><x id="PH"/> subscribers</source>
6808 <target state="new"><x id="PH"/> subscribers</target> 6813 <target state="new"><x id="PH"/> subscribers</target>
6809 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6814
6810 </trans-unit> 6815 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6811 <trans-unit id="4682675125751819107" datatype="html"> 6816
6812 <source>Instances you follow</source> 6817
6813 <target state="new">Instances you follow</target>
6814 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6815 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6816 </trans-unit>
6817 <trans-unit id="8899833753704589712" datatype="html">
6818 <source>Instances following you</source>
6819 <target state="new">Instances following you</target>
6820 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6821 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6822 </trans-unit>
6823 <trans-unit id="1035838766454786107" datatype="html"> 6818 <trans-unit id="1035838766454786107" datatype="html">
6824 <source>Audio-only</source> 6819 <source>Audio-only</source>
6825 <target state="translated">Áudio-somente</target> 6820 <target state="translated">Áudio-somente</target>
@@ -6869,6 +6864,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6869 <source>Auto (via ffmpeg)</source> 6864 <source>Auto (via ffmpeg)</source>
6870 <target>Automático (via ffmpeg)</target> 6865 <target>Automático (via ffmpeg)</target>
6871 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6866 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6867 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6868 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6869 <context-group purpose="location">
6870 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6871 <context context-type="linenumber">3</context>
6872 </context-group>
6872 </trans-unit> 6873 </trans-unit>
6873 <trans-unit id="931255636742351800" datatype="html"> 6874 <trans-unit id="931255636742351800" datatype="html">
6874 <source>No limit</source> 6875 <source>No limit</source>
@@ -7019,18 +7020,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
7019 <trans-unit id="2127446333083057097" datatype="html"> 7020 <trans-unit id="2127446333083057097" datatype="html">
7020 <source>Domain is required.</source> 7021 <source>Domain is required.</source>
7021 <target state="new">Domain is required.</target> 7022 <target state="new">Domain is required.</target>
7022 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 7023
7023 </trans-unit> 7024 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
7024 <trans-unit id="6780793142903080663" datatype="html"> 7025 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
7025 <source>Domains entered are invalid.</source> 7026 <context-group purpose="location">
7026 <target state="new">Domains entered are invalid.</target> 7027 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7027 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 7028 <context context-type="linenumber">93</context>
7028 </trans-unit> 7029 </context-group>
7029 <trans-unit id="5886492514458202177" datatype="html"> 7030 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
7030 <source>Domains entered contain duplicates.</source> 7031 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
7031 <target state="new">Domains entered contain duplicates.</target> 7032 <context-group purpose="location">
7032 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 7033 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7034 <context context-type="linenumber">94</context>
7035 </context-group>
7036 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
7037 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
7038 <context-group purpose="location">
7039 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7040 <context context-type="linenumber">102</context>
7041 </context-group>
7042 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
7043 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
7044 <context-group purpose="location">
7045 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7046 <context context-type="linenumber">103</context>
7047 </context-group>
7033 </trans-unit> 7048 </trans-unit>
7049
7050
7034 <trans-unit id="240806681889331244"> 7051 <trans-unit id="240806681889331244">
7035 <source>Unlimited</source> 7052 <source>Unlimited</source>
7036 <target>Ilimitado</target> 7053 <target>Ilimitado</target>
@@ -7190,24 +7207,50 @@ channel with the same name (<x id="PH_2"/>)!</target>
7190 <x id="PH"/> removido dos seguidores da sua instância 7207 <x id="PH"/> removido dos seguidores da sua instância
7191 </target> 7208 </target>
7192 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 7209 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
7210 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
7211 <source>Follow</source><target state="new">Follow</target>
7212 <context-group purpose="location">
7213 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7214 <context context-type="linenumber">3</context>
7215 </context-group>
7216 <context-group purpose="location">
7217 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7218 <context context-type="linenumber">37</context>
7219 </context-group>
7220 <context-group purpose="location">
7221 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7222 <context context-type="linenumber">18</context>
7223 </context-group>
7224 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
7225 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
7226 <context-group purpose="location">
7227 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7228 <context context-type="linenumber">11</context>
7229 </context-group>
7193 </trans-unit> 7230 </trans-unit>
7194 <trans-unit id="2740793005745065895"> 7231 <trans-unit id="2740793005745065895">
7195 <source><x id="PH"/> is not valid </source> 7232 <source><x id="PH"/> is not valid </source>
7196 <target> 7233 <target>
7197 <x id="PH"/> não é válido 7234 <x id="PH"/> não é válido
7198 </target> 7235 </target>
7199 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 7236
7200 </trans-unit> 7237 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
7201 <trans-unit id="2355066641781598196"> 7238 <trans-unit id="2355066641781598196">
7202 <source>Follow request(s) sent!</source> 7239 <source>Follow request(s) sent!</source>
7203 <target>Solicitação de seguir(s) enviada!</target> 7240 <target>Solicitação de seguir(s) enviada!</target>
7204 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 7241
7242 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
7243 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
7244 <context-group purpose="location">
7245 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7246 <context context-type="linenumber">3</context>
7247 </context-group>
7205 </trans-unit> 7248 </trans-unit>
7206 <trans-unit id="4245720728052819482"> 7249 <trans-unit id="4245720728052819482">
7207 <source>Do you really want to unfollow <x id="PH"/>?</source> 7250 <source>Do you really want to unfollow <x id="PH"/>?</source>
7208 <target>Você realmente deseja parar de seguir <x id="PH"/>?</target> 7251 <target>Você realmente deseja parar de seguir <x id="PH"/>?</target>
7209 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 7252
7210 </trans-unit> 7253 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
7211 <trans-unit id="9160510009013134726"> 7254 <trans-unit id="9160510009013134726">
7212 <source>Unfollow</source> 7255 <source>Unfollow</source>
7213 <target>Parar de seguir</target> 7256 <target>Parar de seguir</target>
@@ -7216,8 +7259,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7216 <trans-unit id="3935234189109112926"> 7259 <trans-unit id="3935234189109112926">
7217 <source>You are not following <x id="PH"/> anymore.</source> 7260 <source>You are not following <x id="PH"/> anymore.</source>
7218 <target>Você não está mais seguindo <x id="PH"/>.</target> 7261 <target>Você não está mais seguindo <x id="PH"/>.</target>
7219 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 7262
7220 </trans-unit> 7263 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
7221 <trans-unit id="2593763089859685916"> 7264 <trans-unit id="2593763089859685916">
7222 <source>enabled</source> 7265 <source>enabled</source>
7223 <target>habilitado</target> 7266 <target>habilitado</target>
@@ -7702,9 +7745,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7702 <trans-unit id="1519954996184640001"> 7745 <trans-unit id="1519954996184640001">
7703 <source>Error</source> 7746 <source>Error</source>
7704 <target>Erro</target> 7747 <target>Erro</target>
7705 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7748
7706 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7749
7707 </trans-unit> 7750 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7708 <trans-unit id="5076187961693950167" datatype="html"> 7751 <trans-unit id="5076187961693950167" datatype="html">
7709 <source>Standard logs</source> 7752 <source>Standard logs</source>
7710 <target state="translated">Registros padrões</target> 7753 <target state="translated">Registros padrões</target>
@@ -7749,16 +7792,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7749 <target>Atualizar senha do usuário</target> 7792 <target>Atualizar senha do usuário</target>
7750 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7793 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7751 </trans-unit> 7794 </trans-unit>
7752 <trans-unit id="177544274549739411" datatype="html"> 7795
7753 <source>Following list</source> 7796
7754 <target state="new">Following list</target>
7755 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7756 </trans-unit>
7757 <trans-unit id="8092429110007204784" datatype="html">
7758 <source>Followers list</source>
7759 <target state="new">Followers list</target>
7760 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7761 </trans-unit>
7762 <trans-unit id="780323526182667308" datatype="html"> 7797 <trans-unit id="780323526182667308" datatype="html">
7763 <source>User <x id="PH"/> updated.</source> 7798 <source>User <x id="PH"/> updated.</source>
7764 <target state="new">User 7799 <target state="new">User
@@ -7798,16 +7833,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7798 <target state="new">Federation</target> 7833 <target state="new">Federation</target>
7799 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7834 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7800 </trans-unit> 7835 </trans-unit>
7801 <trans-unit id="4682675125751819107" datatype="html"> 7836
7802 <source>Instances you follow</source> 7837
7803 <target state="new">Instances you follow</target>
7804 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7805 </trans-unit>
7806 <trans-unit id="8899833753704589712" datatype="html">
7807 <source>Instances following you</source>
7808 <target state="new">Instances following you</target>
7809 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7810 </trans-unit>
7811 <trans-unit id="3767259920053407667" datatype="html"> 7838 <trans-unit id="3767259920053407667" datatype="html">
7812 <source>Videos will be deleted, comments will be tombstoned.</source> 7839 <source>Videos will be deleted, comments will be tombstoned.</source>
7813 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7840 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7838,7 +7865,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7838 <target>Definir Email como Confirmado</target> 7865 <target>Definir Email como Confirmado</target>
7839 7866
7840 7867
7841 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7868 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7869 <source>Created</source><target state="new">Created</target>
7870 <context-group purpose="location">
7871 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7872 <context context-type="linenumber">115</context>
7873 </context-group>
7874 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7875 <source>Daily quota</source><target state="new">Daily quota</target>
7876 <context-group purpose="location">
7877 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7878 <context context-type="linenumber">120</context>
7879 </context-group>
7880 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7881 <source>Last login</source><target state="new">Last login</target>
7882 <context-group purpose="location">
7883 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7884 <context context-type="linenumber">122</context>
7885 </context-group>
7886 </trans-unit>
7842 <trans-unit id="3403978719736970622"> 7887 <trans-unit id="3403978719736970622">
7843 <source>You cannot ban root.</source> 7888 <source>You cannot ban root.</source>
7844 <target>Você não pode banir root.</target> 7889 <target>Você não pode banir root.</target>
@@ -8149,13 +8194,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
8149 <target>Canal de vídeo 8194 <target>Canal de vídeo
8150 <x id="PH"/> criado. 8195 <x id="PH"/> criado.
8151 </target> 8196 </target>
8152 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 8197
8153 </trans-unit> 8198 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
8154 <trans-unit id="8723777130353305761"> 8199 <trans-unit id="8723777130353305761">
8155 <source>This name already exists on this instance.</source> 8200 <source>This name already exists on this instance.</source>
8156 <target>Este nome já existe nesta instância.</target> 8201 <target>Este nome já existe nesta instância.</target>
8157 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 8202
8158 </trans-unit> 8203 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
8159 <trans-unit id="7589345916094713536"> 8204 <trans-unit id="7589345916094713536">
8160 <source>Video channel <x id="PH"/> updated.</source> 8205 <source>Video channel <x id="PH"/> updated.</source>
8161 <target>Canal de vídeo 8206 <target>Canal de vídeo
@@ -8178,13 +8223,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8178 <target state="new">Banner deleted.</target> 8223 <target state="new">Banner deleted.</target>
8179 8224
8180 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 8225 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
8181 <trans-unit id="2575302837003821736"> 8226
8182 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
8183 <target>Por favor, digite o nome de exibição do canal (
8184 <x id="PH"/>) para confirmar
8185 </target>
8186 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
8187 </trans-unit>
8188 <trans-unit id="624066830180032195"> 8227 <trans-unit id="624066830180032195">
8189 <source>Video channel <x id="PH"/> deleted.</source> 8228 <source>Video channel <x id="PH"/> deleted.</source>
8190 <target>Canal de vídeo 8229 <target>Canal de vídeo
@@ -8346,6 +8385,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8346 <source>Ownership change request sent.</source> 8385 <source>Ownership change request sent.</source>
8347 <target>Solicitação para mudar dono enviada.</target> 8386 <target>Solicitação para mudar dono enviada.</target>
8348 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8387 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8388 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8389 <source>Sort by</source><target state="new">Sort by</target>
8390 <context-group purpose="location">
8391 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8392 <context context-type="linenumber">26</context>
8393 </context-group>
8349 </trans-unit> 8394 </trans-unit>
8350 <trans-unit id="3245220240937722814"> 8395 <trans-unit id="3245220240937722814">
8351 <source>My channels</source> 8396 <source>My channels</source>
@@ -8446,7 +8491,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8446 <target>Inscreva-se na conta</target> 8491 <target>Inscreva-se na conta</target>
8447 8492
8448 8493
8449 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8494 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8450 <trans-unit id="3131904093925601441" datatype="html"> 8495 <trans-unit id="3131904093925601441" datatype="html">
8451 <source>PLAYLISTS</source> 8496 <source>PLAYLISTS</source>
8452 <target state="new">PLAYLISTS</target> 8497 <target state="new">PLAYLISTS</target>
@@ -8493,35 +8538,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8493 <trans-unit id="3779524668013120370"> 8538 <trans-unit id="3779524668013120370">
8494 <source>Go to my subscriptions</source> 8539 <source>Go to my subscriptions</source>
8495 <target>Ir às minhas inscrições</target> 8540 <target>Ir às minhas inscrições</target>
8496 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8541
8497 </trans-unit> 8542 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8498 <trans-unit id="1136469849928650779"> 8543 <trans-unit id="1136469849928650779">
8499 <source>Go to my videos</source> 8544 <source>Go to my videos</source>
8500 <target>Ir aos meus vídeos</target> 8545 <target>Ir aos meus vídeos</target>
8501 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8546
8502 </trans-unit> 8547 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8503 <trans-unit id="7836683738999600376"> 8548 <trans-unit id="7836683738999600376">
8504 <source>Go to my imports</source> 8549 <source>Go to my imports</source>
8505 <target>Ir às minhas importações</target> 8550 <target>Ir às minhas importações</target>
8506 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8551
8507 </trans-unit> 8552 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8508 <trans-unit id="7511292153332773503"> 8553 <trans-unit id="7511292153332773503">
8509 <source>Go to my channels</source> 8554 <source>Go to my channels</source>
8510 <target>Ir aos meus canais</target> 8555 <target>Ir aos meus canais</target>
8511 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8556
8512 </trans-unit> 8557 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8513 <trans-unit id="2013324644839511073" datatype="html"> 8558 <trans-unit id="2013324644839511073" datatype="html">
8514 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8559 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8515Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8560Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8516 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8561 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8517Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8562Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8518 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8563
8519 </trans-unit> 8564 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8520 <trans-unit id="375263728166936544"> 8565 <trans-unit id="375263728166936544">
8521 <source>You need to reconnect.</source> 8566 <source>You need to reconnect.</source>
8522 <target>você precisa se reconectar.</target> 8567 <target>você precisa se reconectar.</target>
8523 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8568
8524 </trans-unit> 8569 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8525 <trans-unit id="2206638022166154361"> 8570 <trans-unit id="2206638022166154361">
8526 <source>Keyboard Shortcuts:</source> 8571 <source>Keyboard Shortcuts:</source>
8527 <target>Atalhos de teclado:</target> 8572 <target>Atalhos de teclado:</target>
@@ -8532,6 +8577,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8532 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8577 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8533 <context context-type="linenumber">98</context> 8578 <context context-type="linenumber">98</context>
8534 </context-group> 8579 </context-group>
8580 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8581 <source>In my library</source><target state="new">In my library</target>
8582 <context-group purpose="location">
8583 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8584 <context context-type="linenumber">104</context>
8585 </context-group>
8535 </trans-unit> 8586 </trans-unit>
8536 <trans-unit id="232050922346936574" datatype="html"> 8587 <trans-unit id="232050922346936574" datatype="html">
8537 <source>Trending</source> 8588 <source>Trending</source>
@@ -8558,39 +8609,39 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8558 <trans-unit id="1266887509445371246"> 8609 <trans-unit id="1266887509445371246">
8559 <source>Incorrect username or password.</source> 8610 <source>Incorrect username or password.</source>
8560 <target>Nome de usuário ou senha incorretos.</target> 8611 <target>Nome de usuário ou senha incorretos.</target>
8561 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8612
8562 </trans-unit> 8613 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8563 <trans-unit id="6974874606619467663" datatype="html"> 8614 <trans-unit id="6974874606619467663" datatype="html">
8564 <source>Your account is blocked.</source> 8615 <source>Your account is blocked.</source>
8565 <target state="translated">Sua conta foi bloqueada.</target> 8616 <target state="translated">Sua conta foi bloqueada.</target>
8566 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8617
8567 </trans-unit> 8618 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8568 <trans-unit id="7939914198003891823" datatype="html"> 8619 <trans-unit id="7939914198003891823" datatype="html">
8569 <source>any language</source> 8620 <source>any language</source>
8570 <target state="new">any language</target> 8621 <target state="new">any language</target>
8571 8622
8572 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8623 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8573 8624
8574 <trans-unit id="5633144232269377096" datatype="html"> 8625 <trans-unit id="5633144232269377096" datatype="html">
8575 <source>hide</source> 8626 <source>hide</source>
8576 <target state="new">hide</target> 8627 <target state="new">hide</target>
8577 8628
8578 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8629 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8579 <trans-unit id="8603861867909474404" datatype="html"> 8630 <trans-unit id="8603861867909474404" datatype="html">
8580 <source>blur</source> 8631 <source>blur</source>
8581 <target state="new">blur</target> 8632 <target state="new">blur</target>
8582 8633
8583 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8634 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8584 <trans-unit id="4534458451100881847" datatype="html"> 8635 <trans-unit id="4534458451100881847" datatype="html">
8585 <source>display</source> 8636 <source>display</source>
8586 <target state="new">display</target> 8637 <target state="new">display</target>
8587 8638
8588 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8639 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8589 <trans-unit id="4467323362722952678" datatype="html"> 8640 <trans-unit id="4467323362722952678" datatype="html">
8590 <source>Unknown</source> 8641 <source>Unknown</source>
8591 <target state="new">Unknown</target> 8642 <target state="new">Unknown</target>
8592 8643
8593 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8644 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8594 <trans-unit id="8781423666414310853"> 8645 <trans-unit id="8781423666414310853">
8595 <source>Your password has been successfully reset!</source> 8646 <source>Your password has been successfully reset!</source>
8596 <target>Sua senha foi redefinida com sucesso!</target> 8647 <target>Sua senha foi redefinida com sucesso!</target>
@@ -10193,18 +10244,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10193 <target>Muitas tentativas, por favor tente novamente depois de 10244 <target>Muitas tentativas, por favor tente novamente depois de
10194 <x id="PH"/> minutos. 10245 <x id="PH"/> minutos.
10195 </target> 10246 </target>
10196 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 10247
10197 </trans-unit> 10248 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
10198 <trans-unit id="4965472196059235310"> 10249 <trans-unit id="4965472196059235310">
10199 <source>Too many attempts, please try again later.</source> 10250 <source>Too many attempts, please try again later.</source>
10200 <target>Muitas tentativas, por favor tente novamente depois.</target> 10251 <target>Muitas tentativas, por favor tente novamente depois.</target>
10201 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 10252
10202 </trans-unit> 10253 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
10203 <trans-unit id="1693549688987384699"> 10254 <trans-unit id="1693549688987384699">
10204 <source>Server error. Please retry later.</source> 10255 <source>Server error. Please retry later.</source>
10205 <target>Erro de servidor. Por favor, tente novamente depois.</target> 10256 <target>Erro de servidor. Por favor, tente novamente depois.</target>
10206 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 10257
10207 </trans-unit> 10258 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
10208 <trans-unit id="5927402622550505067" datatype="html"> 10259 <trans-unit id="5927402622550505067" datatype="html">
10209 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 10260 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
10210 <target state="translated">Inscrito em todos os canais atuais de 10261 <target state="translated">Inscrito em todos os canais atuais de
@@ -10803,34 +10854,34 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10803 <source>Your video was uploaded to your account and is private.</source> 10854 <source>Your video was uploaded to your account and is private.</source>
10804 <target>Seu vídeo foi enviado para sua conta e é privado.</target> 10855 <target>Seu vídeo foi enviado para sua conta e é privado.</target>
10805 10856
10806 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10857 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10807 <trans-unit id="5699822024600815733"> 10858 <trans-unit id="5699822024600815733">
10808 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10859 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10809 <target>Mas dados associados (tags, descrição…) serão perdidas, tem certeza que deseja sair dessa página?</target> 10860 <target>Mas dados associados (tags, descrição…) serão perdidas, tem certeza que deseja sair dessa página?</target>
10810 10861
10811 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10862 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10812 <trans-unit id="1219739004043110649"> 10863 <trans-unit id="1219739004043110649">
10813 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10864 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10814 <target>Seu vídeo ainda não foi atualizado, você tem certeza que deseja sair dessa página?</target> 10865 <target>Seu vídeo ainda não foi atualizado, você tem certeza que deseja sair dessa página?</target>
10815 10866
10816 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10867 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10817 <trans-unit id="6932865105766151309" datatype="html"> 10868 <trans-unit id="6932865105766151309" datatype="html">
10818 <source>Upload</source> 10869 <source>Upload</source>
10819 <target state="new">Upload</target> 10870 <target state="new">Upload</target>
10820 10871
10821 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10872 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10822 <trans-unit id="8278735427925094503" datatype="html"> 10873 <trans-unit id="8278735427925094503" datatype="html">
10823 <source>Upload <x id="PH"/> </source> 10874 <source>Upload <x id="PH"/> </source>
10824 <target state="translated">Subir 10875 <target state="translated">Subir
10825 <x id="PH"/> 10876 <x id="PH"/>
10826 </target> 10877 </target>
10827 10878
10828 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10879 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10829 <trans-unit id="5981816353437801748"> 10880 <trans-unit id="5981816353437801748">
10830 <source>Video published.</source> 10881 <source>Video published.</source>
10831 <target>Vídeo publicado.</target> 10882 <target>Vídeo publicado.</target>
10832 10883
10833 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10884 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10834 <trans-unit id="764164089183618119"> 10885 <trans-unit id="764164089183618119">
10835 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10886 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10836 <target>Você tem modificações não salvas! Se sair desta páginas, as modificações serão perdidas.</target> 10887 <target>Você tem modificações não salvas! Se sair desta páginas, as modificações serão perdidas.</target>
@@ -10878,27 +10929,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10878 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10929 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10879 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10930 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10880 10931
10881 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10932 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10882 <trans-unit id="5761611056224181752" datatype="html"> 10933 <trans-unit id="5761611056224181752" datatype="html">
10883 <source>Redirection</source> 10934 <source>Redirection</source>
10884 <target state="new">Redirection</target> 10935 <target state="new">Redirection</target>
10885 10936
10886 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10937 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10887 <trans-unit id="8858527736400081688"> 10938 <trans-unit id="8858527736400081688">
10888 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10939 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10889 <target>Este vídeo possui conteúdo adulto ou explícito. Você tem certeza que deseja assisti-lo?</target> 10940 <target>Este vídeo possui conteúdo adulto ou explícito. Você tem certeza que deseja assisti-lo?</target>
10890 10941
10891 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10942 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10892 <trans-unit id="3937119019020041049"> 10943 <trans-unit id="3937119019020041049">
10893 <source>Mature or explicit content</source> 10944 <source>Mature or explicit content</source>
10894 <target>Conteúdo adulto ou explícito</target> 10945 <target>Conteúdo adulto ou explícito</target>
10895 10946
10896 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10947 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10897 <trans-unit id="1755474755114288376" datatype="html"> 10948 <trans-unit id="1755474755114288376" datatype="html">
10898 <source>Up Next</source> 10949 <source>Up Next</source>
10899 <target state="translated">Seguinte</target> 10950 <target state="translated">Seguinte</target>
10900 10951
10901 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10952 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10902 <trans-unit id="2159130950882492111" datatype="html"> 10953 <trans-unit id="2159130950882492111" datatype="html">
10903 <source>Cancel</source> 10954 <source>Cancel</source>
10904 <target state="new">Cancel</target> 10955 <target state="new">Cancel</target>
@@ -10908,62 +10959,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10908 <source>Autoplay is suspended</source> 10959 <source>Autoplay is suspended</source>
10909 <target state="translated">Auto-leitura está suspensa</target> 10960 <target state="translated">Auto-leitura está suspensa</target>
10910 10961
10911 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10962 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10912 <trans-unit id="7895294730547405228" datatype="html"> 10963 <trans-unit id="7895294730547405228" datatype="html">
10913 <source>Enter/exit fullscreen (requires player focus)</source> 10964 <source>Enter/exit fullscreen (requires player focus)</source>
10914 <target state="translated">Entrar/Sair da tela cheia (requer foco do leitor)</target> 10965 <target state="translated">Entrar/Sair da tela cheia (requer foco do leitor)</target>
10915 10966
10916 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10967 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10917 <trans-unit id="7618388257165864759" datatype="html"> 10968 <trans-unit id="7618388257165864759" datatype="html">
10918 <source>Play/Pause the video (requires player focus)</source> 10969 <source>Play/Pause the video (requires player focus)</source>
10919 <target state="translated">Tocar/Pausar o vídeo (requer foco do leitor)</target> 10970 <target state="translated">Tocar/Pausar o vídeo (requer foco do leitor)</target>
10920 10971
10921 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10972 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10922 <trans-unit id="7761890399634216630" datatype="html"> 10973 <trans-unit id="7761890399634216630" datatype="html">
10923 <source>Mute/unmute the video (requires player focus)</source> 10974 <source>Mute/unmute the video (requires player focus)</source>
10924 <target state="translated">Silenciar/Pôr som no vídeo (requer foco do leitor)</target> 10975 <target state="translated">Silenciar/Pôr som no vídeo (requer foco do leitor)</target>
10925 10976
10926 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10977 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10927 <trans-unit id="5996585232248234904" datatype="html"> 10978 <trans-unit id="5996585232248234904" datatype="html">
10928 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10979 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10929 <target state="translated">Pular para uma percentagem do vídeo: 0 é 0% e 9 é 90% (requer foco do leitor)</target> 10980 <target state="translated">Pular para uma percentagem do vídeo: 0 é 0% e 9 é 90% (requer foco do leitor)</target>
10930 10981
10931 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10982 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10932 <trans-unit id="3748765405903319998" datatype="html"> 10983 <trans-unit id="3748765405903319998" datatype="html">
10933 <source>Increase the volume (requires player focus)</source> 10984 <source>Increase the volume (requires player focus)</source>
10934 <target state="translated">Aumentar o volume (requer foco do leitor)</target> 10985 <target state="translated">Aumentar o volume (requer foco do leitor)</target>
10935 10986
10936 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10987 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10937 <trans-unit id="5810704036407159982" datatype="html"> 10988 <trans-unit id="5810704036407159982" datatype="html">
10938 <source>Decrease the volume (requires player focus)</source> 10989 <source>Decrease the volume (requires player focus)</source>
10939 <target state="translated">Reduzir o volume (requer foco do leitor)</target> 10990 <target state="translated">Reduzir o volume (requer foco do leitor)</target>
10940 10991
10941 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10992 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10942 <trans-unit id="2622048822548065691" datatype="html"> 10993 <trans-unit id="2622048822548065691" datatype="html">
10943 <source>Seek the video forward (requires player focus)</source> 10994 <source>Seek the video forward (requires player focus)</source>
10944 <target state="translated">Procure o vídeo para frente (requer foco do leitor)</target> 10995 <target state="translated">Procure o vídeo para frente (requer foco do leitor)</target>
10945 10996
10946 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10997 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10947 <trans-unit id="6540078205109221153" datatype="html"> 10998 <trans-unit id="6540078205109221153" datatype="html">
10948 <source>Seek the video backward (requires player focus)</source> 10999 <source>Seek the video backward (requires player focus)</source>
10949 <target state="translated">Procure o vídeo para trás (requer foco do leitor)</target> 11000 <target state="translated">Procure o vídeo para trás (requer foco do leitor)</target>
10950 11001
10951 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 11002 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10952 <trans-unit id="1956491957766210808" datatype="html"> 11003 <trans-unit id="1956491957766210808" datatype="html">
10953 <source>Increase playback rate (requires player focus)</source> 11004 <source>Increase playback rate (requires player focus)</source>
10954 <target state="translated">Aumenta a taxa de reprodução (requer foco do leitor)</target> 11005 <target state="translated">Aumenta a taxa de reprodução (requer foco do leitor)</target>
10955 11006
10956 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 11007 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10957 <trans-unit id="5495529997674803186" datatype="html"> 11008 <trans-unit id="5495529997674803186" datatype="html">
10958 <source>Decrease playback rate (requires player focus)</source> 11009 <source>Decrease playback rate (requires player focus)</source>
10959 <target state="translated">Diminuir a taxa de reprodução (requer foco do leitor)</target> 11010 <target state="translated">Diminuir a taxa de reprodução (requer foco do leitor)</target>
10960 11011
10961 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 11012 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10962 <trans-unit id="3178343147230721210" datatype="html"> 11013 <trans-unit id="3178343147230721210" datatype="html">
10963 <source>Navigate in the video frame by frame (requires player focus)</source> 11014 <source>Navigate in the video frame by frame (requires player focus)</source>
10964 <target state="translated">Navegue no vídeo quadro por quadro (requer foco do leitor)</target> 11015 <target state="translated">Navegue no vídeo quadro por quadro (requer foco do leitor)</target>
10965 11016
10966 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 11017 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10967 <trans-unit id="8025996572234182184"> 11018 <trans-unit id="8025996572234182184">
10968 <source>Like the video</source> 11019 <source>Like the video</source>
10969 <target>Gostar do vídeo</target> 11020 <target>Gostar do vídeo</target>
diff --git a/client/src/locale/angular.pt-PT.xlf b/client/src/locale/angular.pt-PT.xlf
index 2fbff7854..6bf8129f3 100644
--- a/client/src/locale/angular.pt-PT.xlf
+++ b/client/src/locale/angular.pt-PT.xlf
@@ -299,7 +299,7 @@
299 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 299 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
300 </target> 300 </target>
301 301
302 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html"> 302 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html">
303 <source>My watch history</source><target state="new">My watch history</target> 303 <source>My watch history</source><target state="new">My watch history</target>
304 304
305 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 305 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
@@ -380,12 +380,12 @@
380 380
381 381
382 382
383 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 383 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
384 <trans-unit id="1006562256968398209" datatype="html"> 384 <trans-unit id="1006562256968398209" datatype="html">
385 <source>video</source> 385 <source>video</source>
386 <target state="new">video</target> 386 <target state="new">video</target>
387 387
388 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 388 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
389 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 389 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
390 390
391 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 391 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -447,10 +447,10 @@
447 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 447 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
448 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 448 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
449 449
450 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 450 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
451 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 451 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
452 452
453 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html"> 453 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html">
454 <source>subtitles</source><target state="new">subtitles</target> 454 <source>subtitles</source><target state="new">subtitles</target>
455 455
456 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 456 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
@@ -833,7 +833,7 @@
833 833
834 834
835 835
836 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group></trans-unit> 836 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
837 <trans-unit id="1502595455339510144"> 837 <trans-unit id="1502595455339510144">
838 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 838 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
839 <target>Ilimitado 839 <target>Ilimitado
@@ -915,7 +915,31 @@
915 <source>Federation</source> 915 <source>Federation</source>
916 <target state="translated">Federação</target> 916 <target state="translated">Federação</target>
917 917
918 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 918 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
919 <source>Following</source><target state="new">Following</target>
920 <context-group purpose="location">
921 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
922 <context context-type="linenumber">29</context>
923 </context-group>
924 <context-group purpose="location">
925 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
926 <context context-type="linenumber">31</context>
927 </context-group>
928 <context-group purpose="location">
929 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
930 <context context-type="linenumber">28</context>
931 </context-group>
932 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
933 <source>Followers</source><target state="new">Followers</target>
934 <context-group purpose="location">
935 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
936 <context context-type="linenumber">34</context>
937 </context-group>
938 <context-group purpose="location">
939 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
940 <context context-type="linenumber">37</context>
941 </context-group>
942 </trans-unit>
919 <trans-unit id="3541687134897970106" datatype="html"> 943 <trans-unit id="3541687134897970106" datatype="html">
920 <source>followers</source> 944 <source>followers</source>
921 <target state="translated">seguidores</target> 945 <target state="translated">seguidores</target>
@@ -975,7 +999,7 @@
975 999
976 1000
977 1001
978 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 1002 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
979 <trans-unit id="3616223838716839702"> 1003 <trans-unit id="3616223838716839702">
980 <source>Ban this user</source> 1004 <source>Ban this user</source>
981 <target>Banir este utilizador</target> 1005 <target>Banir este utilizador</target>
@@ -1039,17 +1063,11 @@
1039 1063
1040 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html"> 1064 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html">
1041 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1065 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1042 <context-group purpose="location"> 1066
1043 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1067 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7215649348148521605" datatype="html">
1044 <context context-type="linenumber">60,62</context>
1045 </context-group>
1046 </trans-unit><trans-unit id="7215649348148521605" datatype="html">
1047 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1068 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1048 <context-group purpose="location"> 1069
1049 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1070 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1050 <context context-type="linenumber">65,67</context>
1051 </context-group>
1052 </trans-unit>
1053 <trans-unit id="2392488717875840729"> 1071 <trans-unit id="2392488717875840729">
1054 <source>User</source> 1072 <source>User</source>
1055 <target>Utilizador</target> 1073 <target>Utilizador</target>
@@ -1060,7 +1078,13 @@
1060 <source>Username or email address</source> 1078 <source>Username or email address</source>
1061 <target>Nome de utilizador ou endereço de correio eletrónico</target> 1079 <target>Nome de utilizador ou endereço de correio eletrónico</target>
1062 1080
1063 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit> 1081 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="1758058452376026925" datatype="html">
1082 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1083 <context-group purpose="location">
1084 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1085 <context context-type="linenumber">33,34</context>
1086 </context-group>
1087 </trans-unit>
1064 1088
1065 <trans-unit id="1431416938026210429"> 1089 <trans-unit id="1431416938026210429">
1066 <source>Password</source> 1090 <source>Password</source>
@@ -1073,50 +1097,44 @@
1073 1097
1074 1098
1075 1099
1076 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group></trans-unit> 1100 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1077 <trans-unit id="8715156686857791956" datatype="html"> 1101 <trans-unit id="8715156686857791956" datatype="html">
1078 <source>Click here to reset your password</source> 1102 <source>Click here to reset your password</source>
1079 <target state="translated">Clique aqui para redefinir a sua senha</target> 1103 <target state="translated">Clique aqui para redefinir a sua senha</target>
1080 1104
1081 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html"> 1105 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html">
1082 <source>I forgot my password</source><target state="new">I forgot my password</target> 1106 <source>I forgot my password</source><target state="new">I forgot my password</target>
1083 <context-group purpose="location"> 1107
1084 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1108 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="2101170466365500913" datatype="html">
1085 <context context-type="linenumber">47</context>
1086 </context-group>
1087 </trans-unit><trans-unit id="2101170466365500913" datatype="html">
1088 <source> Logging into an account lets you publish content </source><target state="new"> Logging into an account lets you publish content </target> 1109 <source> Logging into an account lets you publish content </source><target state="new"> Logging into an account lets you publish content </target>
1089 <context-group purpose="location"> 1110
1090 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1111 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1091 <context context-type="linenumber">56,57</context>
1092 </context-group>
1093 </trans-unit>
1094 <trans-unit id="2454050363478003966"> 1112 <trans-unit id="2454050363478003966">
1095 <source>Login</source> 1113 <source>Login</source>
1096 <target>Iniciar sessão</target> 1114 <target>Iniciar sessão</target>
1097 1115
1098 1116
1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1117 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1100 <trans-unit id="3183213940445113677" datatype="html"> 1118 <trans-unit id="3183213940445113677" datatype="html">
1101 <source>Or sign in with</source> 1119 <source>Or sign in with</source>
1102 <target state="new">Or sign in with</target> 1120 <target state="new">Or sign in with</target>
1103 1121
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit> 1122 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1105 <trans-unit id="3238209155172574367"> 1123 <trans-unit id="3238209155172574367">
1106 <source>Forgot your password</source> 1124 <source>Forgot your password</source>
1107 <target>Esqueceu-se da sua palavra-passe</target> 1125 <target>Esqueceu-se da sua palavra-passe</target>
1108 1126
1109 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group></trans-unit> 1127 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1110 <trans-unit id="87327320394367488" datatype="html"> 1128 <trans-unit id="87327320394367488" datatype="html">
1111 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1129 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1112 <target state="new"> 1130 <target state="new">
1113 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1131 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
1114 </target> 1132 </target>
1115 1133
1116 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html"> 1134 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html">
1117 <source> Enter your email address and we will send you a link to reset your password. </source><target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1135 <source> Enter your email address and we will send you a link to reset your password. </source><target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1118 1136
1119 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html"> 1137 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html">
1120 <source>An email with the reset password instructions will be sent to <x id="PH"/>. 1138 <source>An email with the reset password instructions will be sent to <x id="PH"/>.
1121The link will expire within 1 hour.</source><target state="new">An email with the reset password instructions will be sent to <x id="PH"/>. 1139The link will expire within 1 hour.</source><target state="new">An email with the reset password instructions will be sent to <x id="PH"/>.
1122The link will expire within 1 hour.</target> 1140The link will expire within 1 hour.</target>
@@ -1132,17 +1150,17 @@ The link will expire within 1 hour.</target>
1132 1150
1133 1151
1134 1152
1135 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group></trans-unit> 1153 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1136 <trans-unit id="3967269098753656610"> 1154 <trans-unit id="3967269098753656610">
1137 <source>Email address</source> 1155 <source>Email address</source>
1138 <target>Endereço de correio eletrónico</target> 1156 <target>Endereço de correio eletrónico</target>
1139 1157
1140 1158
1141 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html"> 1159 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html">
1142 <source>Reset</source><target state="new">Reset</target> 1160 <source>Reset</source><target state="new">Reset</target>
1143 1161
1144 <note priority="1" from="description">Password reset button</note> 1162 <note priority="1" from="description">Password reset button</note>
1145 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group></trans-unit> 1163 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1146 1164
1147 1165
1148 <trans-unit id="4319634264526091601" datatype="html"> 1166 <trans-unit id="4319634264526091601" datatype="html">
@@ -1495,7 +1513,7 @@ The link will expire within 1 hour.</target>
1495 <source>Create an account</source> 1513 <source>Create an account</source>
1496 <target>Criar uma conta</target> 1514 <target>Criar uma conta</target>
1497 1515
1498 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1516 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1499 1517
1500 <trans-unit id="3058024914967508975" datatype="html"> 1518 <trans-unit id="3058024914967508975" datatype="html">
1501 <source>My videos</source><target state="new">My videos</target> 1519 <source>My videos</source><target state="new">My videos</target>
@@ -1541,7 +1559,7 @@ The link will expire within 1 hour.</target>
1541 <target state="new">VIDEOS</target> 1559 <target state="new">VIDEOS</target>
1542 1560
1543 1561
1544 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html"> 1562 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html">
1545 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target> 1563 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target>
1546 1564
1547 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html"> 1565 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html">
@@ -1609,7 +1627,7 @@ The link will expire within 1 hour.</target>
1609 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html"> 1627 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html">
1610 <source>I'm a teapot</source><target state="new">I'm a teapot</target> 1628 <source>I'm a teapot</source><target state="new">I'm a teapot</target>
1611 1629
1612 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html"> 1630 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html">
1613 <source>That's an error.</source><target state="new">That's an error.</target> 1631 <source>That's an error.</source><target state="new">That's an error.</target>
1614 <context-group purpose="location"> 1632 <context-group purpose="location">
1615 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context> 1633 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context>
@@ -1675,7 +1693,7 @@ The link will expire within 1 hour.</target>
1675 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html"> 1693 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html">
1676 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1694 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1677 1695
1678 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group></trans-unit> 1696 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1679 1697
1680 <trans-unit id="5131854469652959713" datatype="html"> 1698 <trans-unit id="5131854469652959713" datatype="html">
1681 <source>GLOBAL SEARCH</source> 1699 <source>GLOBAL SEARCH</source>
@@ -2365,7 +2383,7 @@ The link will expire within 1 hour.</target>
2365 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2383 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2366 <source>Upload on hold</source><target state="new">Upload on hold</target> 2384 <source>Upload on hold</source><target state="new">Upload on hold</target>
2367 2385
2368 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2386 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2369 <trans-unit id="285180972645018518" datatype="html"> 2387 <trans-unit id="285180972645018518" datatype="html">
2370 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2388 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2371 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2389 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3029,11 +3047,7 @@ The link will expire within 1 hour.</target>
3029 <target>ID</target> 3047 <target>ID</target>
3030 3048
3031 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit> 3049 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit>
3032 <trans-unit id="2265605798180116441" datatype="html"> 3050
3033 <source>Follower handle</source>
3034 <target state="new">Follower handle</target>
3035
3036 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
3037 <trans-unit id="5911214550882917183"> 3051 <trans-unit id="5911214550882917183">
3038 <source>State</source> 3052 <source>State</source>
3039 <target>Estado</target> 3053 <target>Estado</target>
@@ -3115,11 +3129,7 @@ The link will expire within 1 hour.</target>
3115 </target> 3129 </target>
3116 3130
3117 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit> 3131 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
3118 <trans-unit id="6641024648411549335"> 3132
3119 <source>Host</source>
3120 <target>Host</target>
3121
3122 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3123 <trans-unit id="6571718060636962350" datatype="html"> 3133 <trans-unit id="6571718060636962350" datatype="html">
3124 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3134 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3125 <target state="new">Redundancy allowed 3135 <target state="new">Redundancy allowed
@@ -3130,7 +3140,7 @@ The link will expire within 1 hour.</target>
3130 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html"> 3140 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html">
3131 <source>Unfollow</source><target state="new">Unfollow</target> 3141 <source>Unfollow</source><target state="new">Unfollow</target>
3132 3142
3133 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3143 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3134 <trans-unit id="8246779176913476983" datatype="html"> 3144 <trans-unit id="8246779176913476983" datatype="html">
3135 <source>Open instance in a new tab</source> 3145 <source>Open instance in a new tab</source>
3136 <target state="translated">Abre instância numa nova tabulação</target> 3146 <target state="translated">Abre instância numa nova tabulação</target>
@@ -3142,12 +3152,12 @@ The link will expire within 1 hour.</target>
3142 <source>No host found matching current filters.</source> 3152 <source>No host found matching current filters.</source>
3143 <target state="translated">Não encontrou algum host que corresponda aos filtros actuais.</target> 3153 <target state="translated">Não encontrou algum host que corresponda aos filtros actuais.</target>
3144 3154
3145 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3155 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3146 <trans-unit id="7274241885665071790" datatype="html"> 3156 <trans-unit id="7274241885665071790" datatype="html">
3147 <source>Your instance is not following anyone.</source> 3157 <source>Your instance is not following anyone.</source>
3148 <target state="translated">A sua instância não está a seguir alguém.</target> 3158 <target state="translated">A sua instância não está a seguir alguém.</target>
3149 3159
3150 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3160 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3151 <trans-unit id="4774348799569692380" datatype="html"> 3161 <trans-unit id="4774348799569692380" datatype="html">
3152 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3162 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3153 <target state="translated"> 3163 <target state="translated">
@@ -3157,14 +3167,7 @@ The link will expire within 1 hour.</target>
3157 </target> 3167 </target>
3158 3168
3159 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3169 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3160 <trans-unit id="6275803119759621687" datatype="html"> 3170 <trans-unit id="9216117865911519658" datatype="html">
3161 <source>Follow domains</source>
3162 <target state="translated">Siga domínios</target>
3163
3164 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit><trans-unit id="1268699198448750610" datatype="html">
3165 <source>Follow instances</source><target state="new">Follow instances</target>
3166
3167 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3168 <source>Action</source><target state="new">Action</target> 3171 <source>Action</source><target state="new">Action</target>
3169 3172
3170 3173
@@ -3214,7 +3217,7 @@ The link will expire within 1 hour.</target>
3214 3217
3215 3218
3216 3219
3217 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html"> 3220 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html">
3218 <source>e.g. jane_doe</source><target state="new">e.g. jane_doe</target> 3221 <source>e.g. jane_doe</source><target state="new">e.g. jane_doe</target>
3219 3222
3220 <note priority="1" from="description">Username choice placeholder in the registration form</note> 3223 <note priority="1" from="description">Username choice placeholder in the registration form</note>
@@ -3242,7 +3245,7 @@ The link will expire within 1 hour.</target>
3242 <target>Papel</target> 3245 <target>Papel</target>
3243 3246
3244 3247
3245 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group></trans-unit> 3248 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3246 <trans-unit id="7046347992315328430" datatype="html"> 3249 <trans-unit id="7046347992315328430" datatype="html">
3247 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3250 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3248 <target state="translated">Transcodificação está activada. A quota de vídeos tem em conta apenas o tamanho de vídeo 3251 <target state="translated">Transcodificação está activada. A quota de vídeos tem em conta apenas o tamanho de vídeo
@@ -3262,15 +3265,9 @@ The link will expire within 1 hour.</target>
3262 3265
3263 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html"> 3266 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html">
3264 <source>Auth plugin</source><target state="new">Auth plugin</target> 3267 <source>Auth plugin</source><target state="new">Auth plugin</target>
3265 <context-group purpose="location"> 3268
3266 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3269
3267 <context context-type="linenumber">188</context> 3270 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit><trans-unit id="588099657508661970" datatype="html">
3268 </context-group>
3269 <context-group purpose="location">
3270 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3271 <context context-type="linenumber">188</context>
3272 </context-group>
3273 </trans-unit><trans-unit id="588099657508661970" datatype="html">
3274 <source>None (local authentication)</source><target state="new">None (local authentication)</target> 3271 <source>None (local authentication)</source><target state="new">None (local authentication)</target>
3275 <context-group purpose="location"> 3272 <context-group purpose="location">
3276 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3273 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
@@ -3495,7 +3492,13 @@ The link will expire within 1 hour.</target>
3495 3492
3496 3493
3497 3494
3498 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="4691552465058437520" datatype="html"> 3495 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3496 <source>Follower</source><target state="new">Follower</target>
3497 <context-group purpose="location">
3498 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3499 <context context-type="linenumber">24</context>
3500 </context-group>
3501 </trans-unit><trans-unit id="4691552465058437520" datatype="html">
3499 <source>Commented video</source><target state="new">Commented video</target> 3502 <source>Commented video</source><target state="new">Commented video</target>
3500 3503
3501 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html"> 3504 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html">
@@ -3813,7 +3816,7 @@ The link will expire within 1 hour.</target>
3813 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3816 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3814 <target state="translated">Parece que não está em um servidor HTTPS. O seu servidor necessita de ter TLS activado para conseguir seguir outros servidores.</target> 3817 <target state="translated">Parece que não está em um servidor HTTPS. O seu servidor necessita de ter TLS activado para conseguir seguir outros servidores.</target>
3815 3818
3816 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3819 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3817 <trans-unit id="4058814854824495833" datatype="html"> 3820 <trans-unit id="4058814854824495833" datatype="html">
3818 <source>Mute domains</source> 3821 <source>Mute domains</source>
3819 <target state="translated">Silenciar domínios</target> 3822 <target state="translated">Silenciar domínios</target>
@@ -5618,11 +5621,8 @@ color: red;
5618 5621
5619 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5622 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5620 <source>CHANNELS</source><target state="new">CHANNELS</target> 5623 <source>CHANNELS</source><target state="new">CHANNELS</target>
5621 <context-group purpose="location"> 5624
5622 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5625 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5623 <context context-type="linenumber">82</context>
5624 </context-group>
5625 </trans-unit>
5626 5626
5627 <trans-unit id="3666829335406793239" datatype="html"> 5627 <trans-unit id="3666829335406793239" datatype="html">
5628 <source>This account does not have channels.</source> 5628 <source>This account does not have channels.</source>
@@ -5660,7 +5660,13 @@ channel with the same name (<x id="PH_2"/>)!</source><target state="new">Do you
5660It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 5660It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
5661channel with the same name (<x id="PH_2"/>)!</target> 5661channel with the same name (<x id="PH_2"/>)!</target>
5662 5662
5663 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5663 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5664 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5665 <context-group purpose="location">
5666 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5667 <context context-type="linenumber">48</context>
5668 </context-group>
5669 </trans-unit>
5664 <trans-unit id="5387007581996837469" datatype="html"> 5670 <trans-unit id="5387007581996837469" datatype="html">
5665 <source>My Channels</source> 5671 <source>My Channels</source>
5666 <target state="new">My Channels</target> 5672 <target state="new">My Channels</target>
@@ -6250,12 +6256,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6250 <source>Your message has been sent.</source> 6256 <source>Your message has been sent.</source>
6251 <target state="new">Your message has been sent.</target> 6257 <target state="new">Your message has been sent.</target>
6252 6258
6253 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6259 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6254 <trans-unit id="2072135752262464360"> 6260 <trans-unit id="2072135752262464360">
6255 <source>You already sent this form recently</source> 6261 <source>You already sent this form recently</source>
6256 <target state="new">You already sent this form recently</target> 6262 <target state="new">You already sent this form recently</target>
6257 6263
6258 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6264 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6259 <trans-unit id="819067926858619041" datatype="html"> 6265 <trans-unit id="819067926858619041" datatype="html">
6260 <source>Account videos</source><target state="new">Account videos</target> 6266 <source>Account videos</source><target state="new">Account videos</target>
6261 6267
@@ -6290,10 +6296,10 @@ channel with the same name (<x id="PH_2"/>)!</target>
6290 <x id="PH"/> direct account followers 6296 <x id="PH"/> direct account followers
6291 </target> 6297 </target>
6292 6298
6293 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html"> 6299 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html">
6294 <source>Report this account</source><target state="new">Report this account</target> 6300 <source>Report this account</source><target state="new">Report this account</target>
6295 6301
6296 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6302 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6297 6303
6298 <trans-unit id="1504521795586863905" datatype="html"> 6304 <trans-unit id="1504521795586863905" datatype="html">
6299 <source>VIDEOS</source><target state="new">VIDEOS</target> 6305 <source>VIDEOS</source><target state="new">VIDEOS</target>
@@ -6305,24 +6311,16 @@ channel with the same name (<x id="PH_2"/>)!</target>
6305 <target state="new">Username copied</target> 6311 <target state="new">Username copied</target>
6306 6312
6307 6313
6308 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html"> 6314 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html">
6309 <source>1 subscriber</source><target state="new">1 subscriber</target> 6315 <source>1 subscriber</source><target state="new">1 subscriber</target>
6310 6316
6311 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html"> 6317 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html">
6312 <source><x id="PH"/> subscribers</source><target state="new"><x id="PH"/> subscribers</target> 6318 <source><x id="PH"/> subscribers</source><target state="new"><x id="PH"/> subscribers</target>
6313 6319
6314 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6320 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6321
6322
6315 6323
6316 <trans-unit id="4682675125751819107" datatype="html">
6317 <source>Instances you follow</source>
6318 <target state="new">Instances you follow</target>
6319
6320 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6321 <trans-unit id="8899833753704589712" datatype="html">
6322 <source>Instances following you</source>
6323 <target state="new">Instances following you</target>
6324
6325 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6326 <trans-unit id="1035838766454786107" datatype="html"> 6324 <trans-unit id="1035838766454786107" datatype="html">
6327 <source>Audio-only</source> 6325 <source>Audio-only</source>
6328 <target state="new">Audio-only</target> 6326 <target state="new">Audio-only</target>
@@ -6370,7 +6368,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6370 <source>Auto (via ffmpeg)</source> 6368 <source>Auto (via ffmpeg)</source>
6371 <target state="new">Auto (via ffmpeg)</target> 6369 <target state="new">Auto (via ffmpeg)</target>
6372 6370
6373 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="931255636742351800" datatype="html"> 6371 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="3642770981085338761" datatype="html">
6372 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6373 <context-group purpose="location">
6374 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6375 <context context-type="linenumber">3</context>
6376 </context-group>
6377 </trans-unit><trans-unit id="931255636742351800" datatype="html">
6374 <source>No limit</source><target state="new">No limit</target> 6378 <source>No limit</source><target state="new">No limit</target>
6375 6379
6376 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html"> 6380 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html">
@@ -6489,17 +6493,33 @@ channel with the same name (<x id="PH_2"/>)!</target>
6489 <source>Domain is required.</source> 6493 <source>Domain is required.</source>
6490 <target state="new">Domain is required.</target> 6494 <target state="new">Domain is required.</target>
6491 6495
6492 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group></trans-unit> 6496 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6493 <trans-unit id="6780793142903080663" datatype="html"> 6497 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6494 <source>Domains entered are invalid.</source> 6498 <context-group purpose="location">
6495 <target state="new">Domains entered are invalid.</target> 6499 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6496 6500 <context context-type="linenumber">93</context>
6497 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6501 </context-group>
6498 <trans-unit id="5886492514458202177" datatype="html"> 6502 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6499 <source>Domains entered contain duplicates.</source> 6503 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6500 <target state="new">Domains entered contain duplicates.</target> 6504 <context-group purpose="location">
6501 6505 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6502 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 6506 <context context-type="linenumber">94</context>
6507 </context-group>
6508 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6509 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6510 <context-group purpose="location">
6511 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6512 <context context-type="linenumber">102</context>
6513 </context-group>
6514 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6515 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6516 <context-group purpose="location">
6517 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6518 <context context-type="linenumber">103</context>
6519 </context-group>
6520 </trans-unit>
6521
6522
6503 <trans-unit id="240806681889331244"> 6523 <trans-unit id="240806681889331244">
6504 <source>Unlimited</source> 6524 <source>Unlimited</source>
6505 <target>Ilimitado</target> 6525 <target>Ilimitado</target>
@@ -6630,7 +6650,27 @@ channel with the same name (<x id="PH_2"/>)!</target>
6630 <x id="PH"/> removed from instance followers 6650 <x id="PH"/> removed from instance followers
6631 </target> 6651 </target>
6632 6652
6633 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit> 6653 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit><trans-unit id="6018246591673612412" datatype="html">
6654 <source>Follow</source><target state="new">Follow</target>
6655 <context-group purpose="location">
6656 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6657 <context context-type="linenumber">3</context>
6658 </context-group>
6659 <context-group purpose="location">
6660 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6661 <context context-type="linenumber">37</context>
6662 </context-group>
6663 <context-group purpose="location">
6664 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6665 <context context-type="linenumber">18</context>
6666 </context-group>
6667 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6668 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6669 <context-group purpose="location">
6670 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6671 <context context-type="linenumber">11</context>
6672 </context-group>
6673 </trans-unit>
6634 <trans-unit id="2740793005745065895"> 6674 <trans-unit id="2740793005745065895">
6635 <source> 6675 <source>
6636 <x id="PH"/> is not valid 6676 <x id="PH"/> is not valid
@@ -6639,19 +6679,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
6639 <x id="PH"/> não é válido 6679 <x id="PH"/> não é válido
6640 </target> 6680 </target>
6641 6681
6642 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group></trans-unit> 6682 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6643 <trans-unit id="2355066641781598196"> 6683 <trans-unit id="2355066641781598196">
6644 <source>Follow request(s) sent!</source> 6684 <source>Follow request(s) sent!</source>
6645 <target>Solicitação de seguir(s) enviada!</target> 6685 <target>Solicitação de seguir(s) enviada!</target>
6646 6686
6647 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit> 6687 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6688 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6689 <context-group purpose="location">
6690 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6691 <context context-type="linenumber">3</context>
6692 </context-group>
6693 </trans-unit>
6648 <trans-unit id="4245720728052819482"> 6694 <trans-unit id="4245720728052819482">
6649 <source>Do you really want to unfollow <x id="PH"/>?</source> 6695 <source>Do you really want to unfollow <x id="PH"/>?</source>
6650 <target>Você realmente deseja parar de seguir 6696 <target>Você realmente deseja parar de seguir
6651 <x id="PH"/>? 6697 <x id="PH"/>?
6652 </target> 6698 </target>
6653 6699
6654 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6700 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6655 <trans-unit id="9160510009013134726"> 6701 <trans-unit id="9160510009013134726">
6656 <source>Unfollow</source> 6702 <source>Unfollow</source>
6657 <target>Parar de seguir</target> 6703 <target>Parar de seguir</target>
@@ -6663,7 +6709,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
6663 <x id="PH"/>. 6709 <x id="PH"/>.
6664 </target> 6710 </target>
6665 6711
6666 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 6712 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6667 <trans-unit id="2593763089859685916"> 6713 <trans-unit id="2593763089859685916">
6668 <source>enabled</source> 6714 <source>enabled</source>
6669 <target state="new">enabled</target> 6715 <target state="new">enabled</target>
@@ -7121,7 +7167,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7121 7167
7122 7168
7123 7169
7124 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit> 7170 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7125 <trans-unit id="5076187961693950167" datatype="html"> 7171 <trans-unit id="5076187961693950167" datatype="html">
7126 <source>Standard logs</source> 7172 <source>Standard logs</source>
7127 <target state="new">Standard logs</target> 7173 <target state="new">Standard logs</target>
@@ -7159,13 +7205,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7159 <source>Update user password</source> 7205 <source>Update user password</source>
7160 <target state="new">Update user password</target> 7206 <target state="new">Update user password</target>
7161 7207
7162 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit><trans-unit id="177544274549739411" datatype="html"> 7208 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit>
7163 <source>Following list</source><target state="new">Following list</target>
7164
7165 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group></trans-unit><trans-unit id="8092429110007204784" datatype="html">
7166 <source>Followers list</source><target state="new">Followers list</target>
7167
7168 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group></trans-unit>
7169 <trans-unit id="780323526182667308" datatype="html"> 7209 <trans-unit id="780323526182667308" datatype="html">
7170 <source>User <x id="PH"/> updated.</source> 7210 <source>User <x id="PH"/> updated.</source>
7171 <target state="new">User 7211 <target state="new">User
@@ -7196,13 +7236,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7196 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html"> 7236 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html">
7197 <source>Federation</source><target state="new">Federation</target> 7237 <source>Federation</source><target state="new">Federation</target>
7198 7238
7199 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="4682675125751819107" datatype="html"> 7239 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit>
7200 <source>Instances you follow</source><target state="new">Instances you follow</target>
7201
7202 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group></trans-unit><trans-unit id="8899833753704589712" datatype="html">
7203 <source>Instances following you</source><target state="new">Instances following you</target>
7204
7205 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit>
7206 <trans-unit id="3767259920053407667" datatype="html"> 7240 <trans-unit id="3767259920053407667" datatype="html">
7207 <source>Videos will be deleted, comments will be tombstoned.</source> 7241 <source>Videos will be deleted, comments will be tombstoned.</source>
7208 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7242 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7230,7 +7264,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7230 <target state="new">Set Email as Verified</target> 7264 <target state="new">Set Email as Verified</target>
7231 7265
7232 7266
7233 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7267 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7268 <source>Created</source><target state="new">Created</target>
7269 <context-group purpose="location">
7270 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7271 <context context-type="linenumber">115</context>
7272 </context-group>
7273 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7274 <source>Daily quota</source><target state="new">Daily quota</target>
7275 <context-group purpose="location">
7276 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7277 <context context-type="linenumber">120</context>
7278 </context-group>
7279 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7280 <source>Last login</source><target state="new">Last login</target>
7281 <context-group purpose="location">
7282 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7283 <context context-type="linenumber">122</context>
7284 </context-group>
7285 </trans-unit>
7234 <trans-unit id="3403978719736970622"> 7286 <trans-unit id="3403978719736970622">
7235 <source>You cannot ban root.</source> 7287 <source>You cannot ban root.</source>
7236 <target>Você não pode banir root.</target> 7288 <target>Você não pode banir root.</target>
@@ -7535,12 +7587,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
7535 <x id="PH"/> criado. 7587 <x id="PH"/> criado.
7536 </target> 7588 </target>
7537 7589
7538 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7590 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7539 <trans-unit id="8723777130353305761"> 7591 <trans-unit id="8723777130353305761">
7540 <source>This name already exists on this instance.</source> 7592 <source>This name already exists on this instance.</source>
7541 <target state="new">This name already exists on this instance.</target> 7593 <target state="new">This name already exists on this instance.</target>
7542 7594
7543 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7595 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7544 <trans-unit id="7589345916094713536"> 7596 <trans-unit id="7589345916094713536">
7545 <source>Video channel <x id="PH"/> updated.</source> 7597 <source>Video channel <x id="PH"/> updated.</source>
7546 <target>Canal de vídeo 7598 <target>Canal de vídeo
@@ -7557,13 +7609,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7557 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 7609 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7558 7610
7559 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7611 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7560 <trans-unit id="2575302837003821736"> 7612
7561 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7562 <target state="new">Please type the display name of the video channel (
7563 <x id="PH"/>) to confirm
7564 </target>
7565
7566 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7567 <trans-unit id="624066830180032195"> 7613 <trans-unit id="624066830180032195">
7568 <source>Video channel <x id="PH"/> deleted.</source> 7614 <source>Video channel <x id="PH"/> deleted.</source>
7569 <target>Canal de vídeo 7615 <target>Canal de vídeo
@@ -7706,7 +7752,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
7706 <source>Ownership change request sent.</source> 7752 <source>Ownership change request sent.</source>
7707 <target>Solicitação para mudar dono enviada.</target> 7753 <target>Solicitação para mudar dono enviada.</target>
7708 7754
7709 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 7755 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7699622144571229146" datatype="html">
7756 <source>Sort by</source><target state="new">Sort by</target>
7757 <context-group purpose="location">
7758 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7759 <context context-type="linenumber">26</context>
7760 </context-group>
7761 </trans-unit>
7710 <trans-unit id="3245220240937722814"> 7762 <trans-unit id="3245220240937722814">
7711 <source>My channels</source> 7763 <source>My channels</source>
7712 <target>Meus canais</target> 7764 <target>Meus canais</target>
@@ -7801,7 +7853,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7801 <target state="new">Subscribe to the account</target> 7853 <target state="new">Subscribe to the account</target>
7802 7854
7803 7855
7804 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 7856 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
7805 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 7857 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
7806 <context-group purpose="location"> 7858 <context-group purpose="location">
7807 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 7859 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -7848,34 +7900,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
7848 <source>Go to my subscriptions</source> 7900 <source>Go to my subscriptions</source>
7849 <target state="new">Go to my subscriptions</target> 7901 <target state="new">Go to my subscriptions</target>
7850 7902
7851 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 7903 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
7852 <trans-unit id="1136469849928650779"> 7904 <trans-unit id="1136469849928650779">
7853 <source>Go to my videos</source> 7905 <source>Go to my videos</source>
7854 <target state="new">Go to my videos</target> 7906 <target state="new">Go to my videos</target>
7855 7907
7856 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit> 7908 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
7857 <trans-unit id="7836683738999600376"> 7909 <trans-unit id="7836683738999600376">
7858 <source>Go to my imports</source> 7910 <source>Go to my imports</source>
7859 <target state="new">Go to my imports</target> 7911 <target state="new">Go to my imports</target>
7860 7912
7861 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 7913 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
7862 <trans-unit id="7511292153332773503"> 7914 <trans-unit id="7511292153332773503">
7863 <source>Go to my channels</source> 7915 <source>Go to my channels</source>
7864 <target state="new">Go to my channels</target> 7916 <target state="new">Go to my channels</target>
7865 7917
7866 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html"> 7918 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html">
7867 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>. 7919 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7868Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 7920Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7869Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 7921Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
7870 7922
7871 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group></trans-unit> 7923 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
7872 7924
7873 7925
7874 <trans-unit id="375263728166936544"> 7926 <trans-unit id="375263728166936544">
7875 <source>You need to reconnect.</source> 7927 <source>You need to reconnect.</source>
7876 <target>você precisa se reconectar.</target> 7928 <target>você precisa se reconectar.</target>
7877 7929
7878 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group></trans-unit> 7930 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
7879 <trans-unit id="2206638022166154361"> 7931 <trans-unit id="2206638022166154361">
7880 <source>Keyboard Shortcuts:</source> 7932 <source>Keyboard Shortcuts:</source>
7881 <target state="new">Keyboard Shortcuts:</target> 7933 <target state="new">Keyboard Shortcuts:</target>
@@ -7886,6 +7938,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7886 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 7938 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7887 <context context-type="linenumber">98</context> 7939 <context context-type="linenumber">98</context>
7888 </context-group> 7940 </context-group>
7941 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
7942 <source>In my library</source><target state="new">In my library</target>
7943 <context-group purpose="location">
7944 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
7945 <context context-type="linenumber">104</context>
7946 </context-group>
7889 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 7947 </trans-unit><trans-unit id="232050922346936574" datatype="html">
7890 <source>Trending</source><target state="new">Trending</target> 7948 <source>Trending</source><target state="new">Trending</target>
7891 7949
@@ -7909,38 +7967,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
7909 <source>Incorrect username or password.</source> 7967 <source>Incorrect username or password.</source>
7910 <target>Nome de usuário ou senha incorretos.</target> 7968 <target>Nome de usuário ou senha incorretos.</target>
7911 7969
7912 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 7970 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
7913 <trans-unit id="6974874606619467663" datatype="html"> 7971 <trans-unit id="6974874606619467663" datatype="html">
7914 <source>Your account is blocked.</source> 7972 <source>Your account is blocked.</source>
7915 <target state="new">Your account is blocked.</target> 7973 <target state="new">Your account is blocked.</target>
7916 7974
7917 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 7975 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
7918 7976
7919 <trans-unit id="7939914198003891823" datatype="html"> 7977 <trans-unit id="7939914198003891823" datatype="html">
7920 <source>any language</source> 7978 <source>any language</source>
7921 <target state="new">any language</target> 7979 <target state="new">any language</target>
7922 7980
7923 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 7981 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
7924 <trans-unit id="5633144232269377096" datatype="html"> 7982 <trans-unit id="5633144232269377096" datatype="html">
7925 <source>hide</source> 7983 <source>hide</source>
7926 <target state="new">hide</target> 7984 <target state="new">hide</target>
7927 7985
7928 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 7986 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
7929 <trans-unit id="8603861867909474404" datatype="html"> 7987 <trans-unit id="8603861867909474404" datatype="html">
7930 <source>blur</source> 7988 <source>blur</source>
7931 <target state="new">blur</target> 7989 <target state="new">blur</target>
7932 7990
7933 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 7991 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
7934 <trans-unit id="4534458451100881847" datatype="html"> 7992 <trans-unit id="4534458451100881847" datatype="html">
7935 <source>display</source> 7993 <source>display</source>
7936 <target state="new">display</target> 7994 <target state="new">display</target>
7937 7995
7938 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 7996 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
7939 <trans-unit id="4467323362722952678" datatype="html"> 7997 <trans-unit id="4467323362722952678" datatype="html">
7940 <source>Unknown</source> 7998 <source>Unknown</source>
7941 <target state="new">Unknown</target> 7999 <target state="new">Unknown</target>
7942 8000
7943 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8001 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
7944 <trans-unit id="8781423666414310853"> 8002 <trans-unit id="8781423666414310853">
7945 <source>Your password has been successfully reset!</source> 8003 <source>Your password has been successfully reset!</source>
7946 <target>Sua senha foi redefinida com sucesso!</target> 8004 <target>Sua senha foi redefinida com sucesso!</target>
@@ -9484,17 +9542,17 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9484 <x id="PH"/> minutos. 9542 <x id="PH"/> minutos.
9485 </target> 9543 </target>
9486 9544
9487 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 9545 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9488 <trans-unit id="4965472196059235310"> 9546 <trans-unit id="4965472196059235310">
9489 <source>Too many attempts, please try again later.</source> 9547 <source>Too many attempts, please try again later.</source>
9490 <target>Muitas tentativas, por favor tente novamente depois.</target> 9548 <target>Muitas tentativas, por favor tente novamente depois.</target>
9491 9549
9492 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group></trans-unit> 9550 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9493 <trans-unit id="1693549688987384699"> 9551 <trans-unit id="1693549688987384699">
9494 <source>Server error. Please retry later.</source> 9552 <source>Server error. Please retry later.</source>
9495 <target>Erro de servidor. Por favor, tente novamente depois.</target> 9553 <target>Erro de servidor. Por favor, tente novamente depois.</target>
9496 9554
9497 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 9555 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9498 <trans-unit id="5927402622550505067" datatype="html"> 9556 <trans-unit id="5927402622550505067" datatype="html">
9499 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9557 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9500 <target state="new">Subscribed to all current channels of 9558 <target state="new">Subscribed to all current channels of
@@ -9992,20 +10050,20 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9992 <source>Your video was uploaded to your account and is private.</source> 10050 <source>Your video was uploaded to your account and is private.</source>
9993 <target>Seu vídeo foi enviado para sua conta e é privado.</target> 10051 <target>Seu vídeo foi enviado para sua conta e é privado.</target>
9994 10052
9995 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10053 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
9996 <trans-unit id="5699822024600815733"> 10054 <trans-unit id="5699822024600815733">
9997 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10055 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
9998 <target>Mas dados associados (tags, descrição…) serão perdidas, tem certeza que deseja sair dessa página?</target> 10056 <target>Mas dados associados (tags, descrição…) serão perdidas, tem certeza que deseja sair dessa página?</target>
9999 10057
10000 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10058 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10001 <trans-unit id="1219739004043110649"> 10059 <trans-unit id="1219739004043110649">
10002 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10060 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10003 <target>Seu vídeo ainda não foi atualizado, você tem certeza que deseja sair dessa página?</target> 10061 <target>Seu vídeo ainda não foi atualizado, você tem certeza que deseja sair dessa página?</target>
10004 10062
10005 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html"> 10063 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html">
10006 <source>Upload</source><target state="new">Upload</target> 10064 <source>Upload</source><target state="new">Upload</target>
10007 10065
10008 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10066 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10009 <trans-unit id="8278735427925094503" datatype="html"> 10067 <trans-unit id="8278735427925094503" datatype="html">
10010 <source>Upload 10068 <source>Upload
10011 <x id="PH"/> 10069 <x id="PH"/>
@@ -10014,13 +10072,13 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10014 <x id="PH"/> 10072 <x id="PH"/>
10015 </target> 10073 </target>
10016 10074
10017 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10075 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10018 10076
10019 <trans-unit id="5981816353437801748"> 10077 <trans-unit id="5981816353437801748">
10020 <source>Video published.</source> 10078 <source>Video published.</source>
10021 <target>Vídeo publicado.</target> 10079 <target>Vídeo publicado.</target>
10022 10080
10023 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10081 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10024 10082
10025 10083
10026 <trans-unit id="764164089183618119"> 10084 <trans-unit id="764164089183618119">
@@ -10070,26 +10128,26 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10070 <trans-unit id="961774488937452220" datatype="html"> 10128 <trans-unit id="961774488937452220" datatype="html">
10071 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10129 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10072 10130
10073 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html"> 10131 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html">
10074 <source>Redirection</source><target state="new">Redirection</target> 10132 <source>Redirection</source><target state="new">Redirection</target>
10075 10133
10076 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10134 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10077 10135
10078 <trans-unit id="8858527736400081688"> 10136 <trans-unit id="8858527736400081688">
10079 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10137 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10080 <target>Este vídeo possui conteúdo adulto ou explícito. Você tem certeza que deseja assisti-lo?</target> 10138 <target>Este vídeo possui conteúdo adulto ou explícito. Você tem certeza que deseja assisti-lo?</target>
10081 10139
10082 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10140 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10083 <trans-unit id="3937119019020041049"> 10141 <trans-unit id="3937119019020041049">
10084 <source>Mature or explicit content</source> 10142 <source>Mature or explicit content</source>
10085 <target>Conteúdo adulto ou explícito</target> 10143 <target>Conteúdo adulto ou explícito</target>
10086 10144
10087 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10145 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10088 <trans-unit id="1755474755114288376" datatype="html"> 10146 <trans-unit id="1755474755114288376" datatype="html">
10089 <source>Up Next</source> 10147 <source>Up Next</source>
10090 <target state="new">Up Next</target> 10148 <target state="new">Up Next</target>
10091 10149
10092 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html"> 10150 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html">
10093 <source>Cancel</source><target state="new">Cancel</target> 10151 <source>Cancel</source><target state="new">Cancel</target>
10094 10152
10095 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit> 10153 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit>
@@ -10097,62 +10155,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10097 <source>Autoplay is suspended</source> 10155 <source>Autoplay is suspended</source>
10098 <target state="new">Autoplay is suspended</target> 10156 <target state="new">Autoplay is suspended</target>
10099 10157
10100 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10158 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10101 <trans-unit id="7895294730547405228" datatype="html"> 10159 <trans-unit id="7895294730547405228" datatype="html">
10102 <source>Enter/exit fullscreen (requires player focus)</source> 10160 <source>Enter/exit fullscreen (requires player focus)</source>
10103 <target state="new">Enter/exit fullscreen (requires player focus)</target> 10161 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10104 10162
10105 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10163 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10106 <trans-unit id="7618388257165864759" datatype="html"> 10164 <trans-unit id="7618388257165864759" datatype="html">
10107 <source>Play/Pause the video (requires player focus)</source> 10165 <source>Play/Pause the video (requires player focus)</source>
10108 <target state="new">Play/Pause the video (requires player focus)</target> 10166 <target state="new">Play/Pause the video (requires player focus)</target>
10109 10167
10110 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10168 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10111 <trans-unit id="7761890399634216630" datatype="html"> 10169 <trans-unit id="7761890399634216630" datatype="html">
10112 <source>Mute/unmute the video (requires player focus)</source> 10170 <source>Mute/unmute the video (requires player focus)</source>
10113 <target state="new">Mute/unmute the video (requires player focus)</target> 10171 <target state="new">Mute/unmute the video (requires player focus)</target>
10114 10172
10115 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10173 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10116 <trans-unit id="5996585232248234904" datatype="html"> 10174 <trans-unit id="5996585232248234904" datatype="html">
10117 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10175 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10118 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10176 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10119 10177
10120 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10178 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10121 <trans-unit id="3748765405903319998" datatype="html"> 10179 <trans-unit id="3748765405903319998" datatype="html">
10122 <source>Increase the volume (requires player focus)</source> 10180 <source>Increase the volume (requires player focus)</source>
10123 <target state="new">Increase the volume (requires player focus)</target> 10181 <target state="new">Increase the volume (requires player focus)</target>
10124 10182
10125 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10183 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10126 <trans-unit id="5810704036407159982" datatype="html"> 10184 <trans-unit id="5810704036407159982" datatype="html">
10127 <source>Decrease the volume (requires player focus)</source> 10185 <source>Decrease the volume (requires player focus)</source>
10128 <target state="new">Decrease the volume (requires player focus)</target> 10186 <target state="new">Decrease the volume (requires player focus)</target>
10129 10187
10130 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10188 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10131 <trans-unit id="2622048822548065691" datatype="html"> 10189 <trans-unit id="2622048822548065691" datatype="html">
10132 <source>Seek the video forward (requires player focus)</source> 10190 <source>Seek the video forward (requires player focus)</source>
10133 <target state="new">Seek the video forward (requires player focus)</target> 10191 <target state="new">Seek the video forward (requires player focus)</target>
10134 10192
10135 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10193 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10136 <trans-unit id="6540078205109221153" datatype="html"> 10194 <trans-unit id="6540078205109221153" datatype="html">
10137 <source>Seek the video backward (requires player focus)</source> 10195 <source>Seek the video backward (requires player focus)</source>
10138 <target state="new">Seek the video backward (requires player focus)</target> 10196 <target state="new">Seek the video backward (requires player focus)</target>
10139 10197
10140 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10198 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10141 <trans-unit id="1956491957766210808" datatype="html"> 10199 <trans-unit id="1956491957766210808" datatype="html">
10142 <source>Increase playback rate (requires player focus)</source> 10200 <source>Increase playback rate (requires player focus)</source>
10143 <target state="new">Increase playback rate (requires player focus)</target> 10201 <target state="new">Increase playback rate (requires player focus)</target>
10144 10202
10145 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10203 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10146 <trans-unit id="5495529997674803186" datatype="html"> 10204 <trans-unit id="5495529997674803186" datatype="html">
10147 <source>Decrease playback rate (requires player focus)</source> 10205 <source>Decrease playback rate (requires player focus)</source>
10148 <target state="new">Decrease playback rate (requires player focus)</target> 10206 <target state="new">Decrease playback rate (requires player focus)</target>
10149 10207
10150 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10208 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10151 <trans-unit id="3178343147230721210" datatype="html"> 10209 <trans-unit id="3178343147230721210" datatype="html">
10152 <source>Navigate in the video frame by frame (requires player focus)</source> 10210 <source>Navigate in the video frame by frame (requires player focus)</source>
10153 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 10211 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10154 10212
10155 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10213 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10156 <trans-unit id="8025996572234182184"> 10214 <trans-unit id="8025996572234182184">
10157 <source>Like the video</source> 10215 <source>Like the video</source>
10158 <target state="new">Like the video</target> 10216 <target state="new">Like the video</target>
diff --git a/client/src/locale/angular.ru-RU.xlf b/client/src/locale/angular.ru-RU.xlf
index d72d8420d..af1ceec56 100644
--- a/client/src/locale/angular.ru-RU.xlf
+++ b/client/src/locale/angular.ru-RU.xlf
@@ -160,19 +160,19 @@
160 <trans-unit id="187187500641108332" datatype="html"> 160 <trans-unit id="187187500641108332" datatype="html">
161 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source> 161 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source>
162 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target> 162 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target>
163 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group> 163
164 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group> 164
165 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group> 165
166 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group> 166
167 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group> 167
168 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 168
169 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group> 169
170 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group> 170
171 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group> 171
172 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group> 172
173 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group> 173
174 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group> 174
175 </trans-unit> 175 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
176 <trans-unit id="1486537403020619891" datatype="html"> 176 <trans-unit id="1486537403020619891" datatype="html">
177 <source>My watch history</source> 177 <source>My watch history</source>
178 <target state="translated">Моя история просмотров</target> 178 <target state="translated">Моя история просмотров</target>
@@ -241,22 +241,22 @@
241 <trans-unit id="5674286808255988565"> 241 <trans-unit id="5674286808255988565">
242 <source>Create</source> 242 <source>Create</source>
243 <target>Создать</target> 243 <target>Создать</target>
244 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 244
245 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 245
246 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 246
247 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 247
248 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 248
249 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 249
250 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 250
251 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 251
252 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 252
253 </trans-unit> 253 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
254 <trans-unit id="1006562256968398209" datatype="html"> 254 <trans-unit id="1006562256968398209" datatype="html">
255 <source>video</source> 255 <source>video</source>
256 <target state="translated">видео</target> 256 <target state="translated">видео</target>
257 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group> 257
258 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group> 258
259 </trans-unit> 259 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
260 <trans-unit id="6438815964972582865" datatype="html"> 260 <trans-unit id="6438815964972582865" datatype="html">
261 <source>The following link contains a private token and should not be shared with anyone.</source> 261 <source>The following link contains a private token and should not be shared with anyone.</source>
262 <target state="translated">Следующая ссылка содержит личный токен и никому не может быть передана.</target> 262 <target state="translated">Следующая ссылка содержит личный токен и никому не может быть передана.</target>
@@ -326,13 +326,13 @@
326 <trans-unit id="6995024616159044376" datatype="html"> 326 <trans-unit id="6995024616159044376" datatype="html">
327 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 327 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
328 <target state="translated">Ваша квота для этого видео превышена (размер видео: <x id="PH" equiv-text="videoSizeBytes"/>, использовано: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, квота: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 328 <target state="translated">Ваша квота для этого видео превышена (размер видео: <x id="PH" equiv-text="videoSizeBytes"/>, использовано: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, квота: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
329 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 329
330 </trans-unit> 330 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
331 <trans-unit id="7873395933409147217" datatype="html"> 331 <trans-unit id="7873395933409147217" datatype="html">
332 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 332 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
333 <target state="translated">Ваша дневная квота для этого видео превышена (размер видео: <x id="PH" equiv-text="videoSizeBytes"/>, использовано: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, квота: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 333 <target state="translated">Ваша дневная квота для этого видео превышена (размер видео: <x id="PH" equiv-text="videoSizeBytes"/>, использовано: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, квота: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
334 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 334
335 </trans-unit> 335 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
336 <trans-unit id="5235042777215655908" datatype="html"> 336 <trans-unit id="5235042777215655908" datatype="html">
337 <source>subtitles</source> 337 <source>subtitles</source>
338 <target state="translated">субтитры</target> 338 <target state="translated">субтитры</target>
@@ -768,10 +768,10 @@
768 <trans-unit id="2602586221576511475"> 768 <trans-unit id="2602586221576511475">
769 <source>Video quota</source> 769 <source>Video quota</source>
770 <target>Квота на видео</target> 770 <target>Квота на видео</target>
771 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 771
772 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 772
773 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 773
774 </trans-unit> 774 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
775 <trans-unit id="1502595455339510144"> 775 <trans-unit id="1502595455339510144">
776 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 776 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
777 <target>Неограниченно <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> в день)<x id="CLOSE_TAG_NG_CONTAINER"/></target> 777 <target>Неограниченно <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> в день)<x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -851,6 +851,30 @@
851 <target state="translated">Федерация</target> 851 <target state="translated">Федерация</target>
852 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 852 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
853 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 853 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
854 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
855 <source>Following</source><target state="new">Following</target>
856 <context-group purpose="location">
857 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
858 <context context-type="linenumber">29</context>
859 </context-group>
860 <context-group purpose="location">
861 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
862 <context context-type="linenumber">31</context>
863 </context-group>
864 <context-group purpose="location">
865 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
866 <context context-type="linenumber">28</context>
867 </context-group>
868 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
869 <source>Followers</source><target state="new">Followers</target>
870 <context-group purpose="location">
871 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
872 <context context-type="linenumber">34</context>
873 </context-group>
874 <context-group purpose="location">
875 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
876 <context context-type="linenumber">37</context>
877 </context-group>
854 </trans-unit> 878 </trans-unit>
855 <trans-unit id="3541687134897970106" datatype="html"> 879 <trans-unit id="3541687134897970106" datatype="html">
856 <source>followers</source> 880 <source>followers</source>
@@ -914,25 +938,25 @@
914 <trans-unit id="2159130950882492111"> 938 <trans-unit id="2159130950882492111">
915 <source>Cancel</source> 939 <source>Cancel</source>
916 <target>Отменить</target> 940 <target>Отменить</target>
917 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group> 941
918 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group> 942
919 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group> 943
920 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group> 944
921 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group> 945
922 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group> 946
923 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group> 947
924 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group> 948
925 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group> 949
926 <context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group> 950
927 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group> 951
928 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group> 952
929 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 953
930 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 954
931 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group> 955
932 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group> 956
933 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group> 957
934 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group> 958
935 </trans-unit> 959 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
936 <trans-unit id="3616223838716839702"> 960 <trans-unit id="3616223838716839702">
937 <source>Ban this user</source> 961 <source>Ban this user</source>
938 <target>Заблокировать этого пользователя</target> 962 <target>Заблокировать этого пользователя</target>
@@ -996,19 +1020,13 @@
996 <trans-unit id="7252854992688790751" datatype="html"> 1020 <trans-unit id="7252854992688790751" datatype="html">
997 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1021 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
998 <target state="translated">Этот экземпляр разрешает регистрацию. Однако будьте осторожны, проверьте <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Условия пользования<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> перед созданием учетной записи. Вы также можете найти другой экземпляр, который точно соответствует вашим потребностям, на: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1022 <target state="translated">Этот экземпляр разрешает регистрацию. Однако будьте осторожны, проверьте <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Условия пользования<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> перед созданием учетной записи. Вы также можете найти другой экземпляр, который точно соответствует вашим потребностям, на: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
999 <context-group purpose="location"> 1023
1000 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1024 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1001 <context context-type="linenumber">60,62</context>
1002 </context-group>
1003 </trans-unit>
1004 <trans-unit id="7215649348148521605" datatype="html"> 1025 <trans-unit id="7215649348148521605" datatype="html">
1005 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1026 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1006 <target state="translated">В настоящее время этот экземпляр не позволяет регистрировать пользователей, проверьте <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Условия пользования<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> для получения дополнительных сведений, или найдите экземпляр, который дает вам возможность зарегистрировать учетную запись и загружать туда свои видео. Найдите свой среди множества экземпляров на: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1027 <target state="translated">В настоящее время этот экземпляр не позволяет регистрировать пользователей, проверьте <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Условия пользования<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> для получения дополнительных сведений, или найдите экземпляр, который дает вам возможность зарегистрировать учетную запись и загружать туда свои видео. Найдите свой среди множества экземпляров на: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1007 <context-group purpose="location"> 1028
1008 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1029 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1009 <context context-type="linenumber">65,67</context>
1010 </context-group>
1011 </trans-unit>
1012 <trans-unit id="2392488717875840729"> 1030 <trans-unit id="2392488717875840729">
1013 <source>User</source> 1031 <source>User</source>
1014 <target>Пользователь</target> 1032 <target>Пользователь</target>
@@ -1019,67 +1037,67 @@
1019 <source>Username or email address</source> 1037 <source>Username or email address</source>
1020 <target>Имя пользователя или электронный адрес</target> 1038 <target>Имя пользователя или электронный адрес</target>
1021 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1040 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1041 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1042 <context-group purpose="location">
1043 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1044 <context context-type="linenumber">33,34</context>
1045 </context-group>
1022 </trans-unit> 1046 </trans-unit>
1023 <trans-unit id="1431416938026210429"> 1047 <trans-unit id="1431416938026210429">
1024 <source>Password</source> 1048 <source>Password</source>
1025 <target>Пароль</target> 1049 <target>Пароль</target>
1026 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1050
1027 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1051
1028 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1052
1029 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1053
1030 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1054
1031 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1055
1032 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1056
1033 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1057
1034 </trans-unit> 1058 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1035 <trans-unit id="8715156686857791956" datatype="html"> 1059 <trans-unit id="8715156686857791956" datatype="html">
1036 <source>Click here to reset your password</source> 1060 <source>Click here to reset your password</source>
1037 <target state="translated">Нажмите здесь что бы сбросить пароль</target> 1061 <target state="translated">Нажмите здесь что бы сбросить пароль</target>
1038 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1062
1039 </trans-unit> 1063 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1040 <trans-unit id="892063502898494584" datatype="html"> 1064 <trans-unit id="892063502898494584" datatype="html">
1041 <source>I forgot my password</source> 1065 <source>I forgot my password</source>
1042 <target state="translated">Я забыл свой пароль</target> 1066 <target state="translated">Я забыл свой пароль</target>
1043 <context-group purpose="location"> 1067
1044 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1068 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1045 <context context-type="linenumber">47</context>
1046 </context-group>
1047 </trans-unit>
1048 <trans-unit id="2101170466365500913" datatype="html"> 1069 <trans-unit id="2101170466365500913" datatype="html">
1049 <source>Logging into an account lets you publish content</source> 1070 <source>Logging into an account lets you publish content</source>
1050 <target state="translated">Авторизация учетной записи позволяет публиковать контент</target> 1071 <target state="translated">Авторизация учетной записи позволяет публиковать контент</target>
1051 <context-group purpose="location"> 1072
1052 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1073 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1053 <context context-type="linenumber">56,57</context>
1054 </context-group>
1055 </trans-unit>
1056 <trans-unit id="2454050363478003966"> 1074 <trans-unit id="2454050363478003966">
1057 <source>Login</source> 1075 <source>Login</source>
1058 <target>Авторизация</target> 1076 <target>Авторизация</target>
1059 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1077
1060 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1078
1061 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1079
1062 </trans-unit> 1080 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1063 <trans-unit id="3183213940445113677" datatype="html"> 1081 <trans-unit id="3183213940445113677" datatype="html">
1064 <source>Or sign in with</source> 1082 <source>Or sign in with</source>
1065 <target state="translated">Или войдите с помощью</target> 1083 <target state="translated">Или войдите с помощью</target>
1066 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1084
1067 </trans-unit> 1085 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1068 <trans-unit id="3238209155172574367"> 1086 <trans-unit id="3238209155172574367">
1069 <source>Forgot your password</source> 1087 <source>Forgot your password</source>
1070 <target>Забыли пароль</target> 1088 <target>Забыли пароль</target>
1071 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1089
1072 </trans-unit> 1090 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1073 <trans-unit id="87327320394367488" datatype="html"> 1091 <trans-unit id="87327320394367488" datatype="html">
1074 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1092 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1075 <target state="translated">К сожалению, вы не можете восстановить свой пароль, так как администратор вашего экземпляра не настроил почтовую систему PeerTube.</target> 1093 <target state="translated">К сожалению, вы не можете восстановить свой пароль, так как администратор вашего экземпляра не настроил почтовую систему PeerTube.</target>
1076 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1094
1077 </trans-unit> 1095 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1078 <trans-unit id="3188014010833256853" datatype="html"> 1096 <trans-unit id="3188014010833256853" datatype="html">
1079 <source>Enter your email address and we will send you a link to reset your password.</source> 1097 <source>Enter your email address and we will send you a link to reset your password.</source>
1080 <target state="translated">Введите свой email и мы пришлём вам ссылку для сброса пароля.</target> 1098 <target state="translated">Введите свой email и мы пришлём вам ссылку для сброса пароля.</target>
1081 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1099
1082 </trans-unit> 1100 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1083 <trans-unit id="1190256911880544559" datatype="html"> 1101 <trans-unit id="1190256911880544559" datatype="html">
1084 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1102 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1085The link will expire within 1 hour.</source> 1103The link will expire within 1 hour.</source>
@@ -1089,26 +1107,26 @@ The link will expire within 1 hour.</source>
1089 <trans-unit id="4768749765465246664"> 1107 <trans-unit id="4768749765465246664">
1090 <source>Email</source> 1108 <source>Email</source>
1091 <target>Email</target> 1109 <target>Email</target>
1092 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1110
1093 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1111
1094 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1112
1095 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1113
1096 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1114
1097 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1115
1098 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1116
1099 </trans-unit> 1117 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1100 <trans-unit id="3967269098753656610"> 1118 <trans-unit id="3967269098753656610">
1101 <source>Email address</source> 1119 <source>Email address</source>
1102 <target>Email адрес</target> 1120 <target>Email адрес</target>
1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1121
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1122
1105 </trans-unit> 1123 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1106 <trans-unit id="7808756054397155068" datatype="html"> 1124 <trans-unit id="7808756054397155068" datatype="html">
1107 <source>Reset</source> 1125 <source>Reset</source>
1108 <target state="translated">Сброс</target> 1126 <target state="translated">Сброс</target>
1109 <note priority="1" from="description">Password reset button</note> 1127 <note priority="1" from="description">Password reset button</note>
1110 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1128
1111 </trans-unit> 1129 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1112 <trans-unit id="4319634264526091601" datatype="html"> 1130 <trans-unit id="4319634264526091601" datatype="html">
1113 <source>on this instance</source> 1131 <source>on this instance</source>
1114 <target state="translated">на этом экземпляре</target> 1132 <target state="translated">на этом экземпляре</target>
@@ -1438,9 +1456,9 @@ The link will expire within 1 hour.</source>
1438 <trans-unit id="2308975396733519902"> 1456 <trans-unit id="2308975396733519902">
1439 <source>Create an account</source> 1457 <source>Create an account</source>
1440 <target>Создать учетную запись</target> 1458 <target>Создать учетную запись</target>
1441 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1459
1442 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1460
1443 </trans-unit> 1461 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1444 <trans-unit id="3058024914967508975" datatype="html"> 1462 <trans-unit id="3058024914967508975" datatype="html">
1445 <source>My videos</source> 1463 <source>My videos</source>
1446 <target state="translated">Мои видео</target> 1464 <target state="translated">Мои видео</target>
@@ -1507,10 +1525,10 @@ The link will expire within 1 hour.</source>
1507 <trans-unit id="1504521795586863905" datatype="html"> 1525 <trans-unit id="1504521795586863905" datatype="html">
1508 <source>VIDEOS</source> 1526 <source>VIDEOS</source>
1509 <target state="translated">ВИДЕО</target> 1527 <target state="translated">ВИДЕО</target>
1510 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1528
1511 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1529
1512 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1530
1513 </trans-unit> 1531 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1514 <trans-unit id="667372110624203230" datatype="html"> 1532 <trans-unit id="667372110624203230" datatype="html">
1515 <source>Import jobs concurrency</source> 1533 <source>Import jobs concurrency</source>
1516 <target state="translated">Параллельный импорт заданий</target> 1534 <target state="translated">Параллельный импорт заданий</target>
@@ -1589,8 +1607,8 @@ The link will expire within 1 hour.</source>
1589 <trans-unit id="4424964105331349857" datatype="html"> 1607 <trans-unit id="4424964105331349857" datatype="html">
1590 <source>I'm a teapot</source> 1608 <source>I'm a teapot</source>
1591 <target state="translated">Я чайник</target> 1609 <target state="translated">Я чайник</target>
1592 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1610
1593 </trans-unit> 1611 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1594 <trans-unit id="1597262876035959248" datatype="html"> 1612 <trans-unit id="1597262876035959248" datatype="html">
1595 <source>That's an error.</source> 1613 <source>That's an error.</source>
1596 <target state="translated">Это ошибка.</target> 1614 <target state="translated">Это ошибка.</target>
@@ -1683,8 +1701,8 @@ The link will expire within 1 hour.</source>
1683 <trans-unit id="2971365540217107489" datatype="html"> 1701 <trans-unit id="2971365540217107489" datatype="html">
1684 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1702 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1685 <target state="translated">Видео слишком большое для сервера. Пожалуйста свяжитесь со вашим администратором для увеличение лимита.</target> 1703 <target state="translated">Видео слишком большое для сервера. Пожалуйста свяжитесь со вашим администратором для увеличение лимита.</target>
1686 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1704
1687 </trans-unit> 1705 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1688 <trans-unit id="5131854469652959713" datatype="html"> 1706 <trans-unit id="5131854469652959713" datatype="html">
1689 <source>GLOBAL SEARCH</source> 1707 <source>GLOBAL SEARCH</source>
1690 <target state="translated">ГЛОБАЛЬНЫЙ ПОИСК</target> 1708 <target state="translated">ГЛОБАЛЬНЫЙ ПОИСК</target>
@@ -2424,8 +2442,8 @@ The link will expire within 1 hour.</source>
2424 <trans-unit id="6161604372916832458" datatype="html"> 2442 <trans-unit id="6161604372916832458" datatype="html">
2425 <source>Upload on hold</source> 2443 <source>Upload on hold</source>
2426 <target state="translated">Загрузка приостановлена</target> 2444 <target state="translated">Загрузка приостановлена</target>
2427 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2445
2428 </trans-unit> 2446 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2429 <trans-unit id="285180972645018518" datatype="html"> 2447 <trans-unit id="285180972645018518" datatype="html">
2430 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2448 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2431 <target state="translated">Извините, загрузка файлов недоступна для вашей учётной записи. Если вы хотите добавлять видео, свяжитесь с администратором.</target> 2449 <target state="translated">Извините, загрузка файлов недоступна для вашей учётной записи. Если вы хотите добавлять видео, свяжитесь с администратором.</target>
@@ -3117,11 +3135,7 @@ The link will expire within 1 hour.</source>
3117 <target>ID</target> 3135 <target>ID</target>
3118 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3136 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3119 </trans-unit> 3137 </trans-unit>
3120 <trans-unit id="2265605798180116441" datatype="html"> 3138
3121 <source>Follower handle</source>
3122 <target state="translated">Подписчик</target>
3123 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3124 </trans-unit>
3125 <trans-unit id="5911214550882917183"> 3139 <trans-unit id="5911214550882917183">
3126 <source>State</source> 3140 <source>State</source>
3127 <target>Состояние</target> 3141 <target>Состояние</target>
@@ -3187,11 +3201,7 @@ The link will expire within 1 hour.</source>
3187 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target> 3201 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target>
3188 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3202 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3189 </trans-unit> 3203 </trans-unit>
3190 <trans-unit id="6641024648411549335"> 3204
3191 <source>Host</source>
3192 <target>Хост</target>
3193 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3194 </trans-unit>
3195 <trans-unit id="6571718060636962350" datatype="html"> 3205 <trans-unit id="6571718060636962350" datatype="html">
3196 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3206 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3197 <target state="translated">Избыточность разрешена <x id="START_TAG_P-SORTICON"/> <x id="CLOSE_TAG_P-SORTICON"/></target> 3207 <target state="translated">Избыточность разрешена <x id="START_TAG_P-SORTICON"/> <x id="CLOSE_TAG_P-SORTICON"/></target>
@@ -3200,9 +3210,9 @@ The link will expire within 1 hour.</source>
3200 <trans-unit id="9160510009013134726" datatype="html"> 3210 <trans-unit id="9160510009013134726" datatype="html">
3201 <source>Unfollow</source> 3211 <source>Unfollow</source>
3202 <target state="translated">Отписаться</target> 3212 <target state="translated">Отписаться</target>
3203 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3213
3204 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3214
3205 </trans-unit> 3215 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3206 <trans-unit id="8246779176913476983" datatype="html"> 3216 <trans-unit id="8246779176913476983" datatype="html">
3207 <source>Open instance in a new tab</source> 3217 <source>Open instance in a new tab</source>
3208 <target state="translated">Открыть экземпляр в новой вкладке</target> 3218 <target state="translated">Открыть экземпляр в новой вкладке</target>
@@ -3213,28 +3223,20 @@ The link will expire within 1 hour.</source>
3213 <trans-unit id="9132918641931433659" datatype="html"> 3223 <trans-unit id="9132918641931433659" datatype="html">
3214 <source>No host found matching current filters.</source> 3224 <source>No host found matching current filters.</source>
3215 <target state="translated">Не найдено ни одного хоста, соответствующего текущим фильтрам.</target> 3225 <target state="translated">Не найдено ни одного хоста, соответствующего текущим фильтрам.</target>
3216 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3226
3217 </trans-unit> 3227 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3218 <trans-unit id="7274241885665071790" datatype="html"> 3228 <trans-unit id="7274241885665071790" datatype="html">
3219 <source>Your instance is not following anyone.</source> 3229 <source>Your instance is not following anyone.</source>
3220 <target state="translated">Ваш экземпляр ни за кем не подписан.</target> 3230 <target state="translated">Ваш экземпляр ни за кем не подписан.</target>
3221 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3231
3222 </trans-unit> 3232 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3223 <trans-unit id="4774348799569692380" datatype="html"> 3233 <trans-unit id="4774348799569692380" datatype="html">
3224 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3234 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3225 <target state="translated">Показано <x id="INTERPOLATION"/> по <x id="INTERPOLATION_1"/> из <x id="INTERPOLATION_2"/> хостов</target> 3235 <target state="translated">Показано <x id="INTERPOLATION"/> по <x id="INTERPOLATION_1"/> из <x id="INTERPOLATION_2"/> хостов</target>
3226 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3236 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3227 </trans-unit> 3237 </trans-unit>
3228 <trans-unit id="6275803119759621687" datatype="html"> 3238
3229 <source>Follow domains</source> 3239
3230 <target state="translated">Следить за доменами</target>
3231 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3232 </trans-unit>
3233 <trans-unit id="1268699198448750610" datatype="html">
3234 <source>Follow instances</source>
3235 <target state="translated">Следить за экземплярами</target>
3236 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3237 </trans-unit>
3238 <trans-unit id="9216117865911519658" datatype="html"> 3240 <trans-unit id="9216117865911519658" datatype="html">
3239 <source>Action</source> 3241 <source>Action</source>
3240 <target state="translated">Действие</target> 3242 <target state="translated">Действие</target>
@@ -3284,11 +3286,11 @@ The link will expire within 1 hour.</source>
3284 <trans-unit id="5248717555542428023"> 3286 <trans-unit id="5248717555542428023">
3285 <source>Username</source> 3287 <source>Username</source>
3286 <target>Имя пользователя</target> 3288 <target>Имя пользователя</target>
3287 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3289
3288 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3290
3289 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3291
3290 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3292
3291 </trans-unit> 3293 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3292 <trans-unit id="5428411040014095392" datatype="html"> 3294 <trans-unit id="5428411040014095392" datatype="html">
3293 <source>e.g. jane_doe</source> 3295 <source>e.g. jane_doe</source>
3294 <target state="translated">прим. ivan_ivanov</target> 3296 <target state="translated">прим. ivan_ivanov</target>
@@ -3316,9 +3318,9 @@ The link will expire within 1 hour.</source>
3316 <trans-unit id="4145496584631696119"> 3318 <trans-unit id="4145496584631696119">
3317 <source>Role</source> 3319 <source>Role</source>
3318 <target>Роль</target> 3320 <target>Роль</target>
3319 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3321
3320 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3322
3321 </trans-unit> 3323 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3322 <trans-unit id="7046347992315328430" datatype="html"> 3324 <trans-unit id="7046347992315328430" datatype="html">
3323 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3325 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3324 <target state="translated">Транскодирование включено. Квота видео учитывает только <x id="START_TAG_STRONG"/>оригинальный<x id="CLOSE_TAG_STRONG"/> размер видео. <x id="LINE_BREAK"/>Максимум, этот пользователь мог загрузить ~ <x id="INTERPOLATION"/>. </target> 3326 <target state="translated">Транскодирование включено. Квота видео учитывает только <x id="START_TAG_STRONG"/>оригинальный<x id="CLOSE_TAG_STRONG"/> размер видео. <x id="LINE_BREAK"/>Максимум, этот пользователь мог загрузить ~ <x id="INTERPOLATION"/>. </target>
@@ -3335,15 +3337,9 @@ The link will expire within 1 hour.</source>
3335 <trans-unit id="2622255144026150901" datatype="html"> 3337 <trans-unit id="2622255144026150901" datatype="html">
3336 <source>Auth plugin</source> 3338 <source>Auth plugin</source>
3337 <target state="translated">Плагин авторизации</target> 3339 <target state="translated">Плагин авторизации</target>
3338 <context-group purpose="location"> 3340
3339 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3341
3340 <context context-type="linenumber">188</context> 3342 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3341 </context-group>
3342 <context-group purpose="location">
3343 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3344 <context context-type="linenumber">188</context>
3345 </context-group>
3346 </trans-unit>
3347 <trans-unit id="588099657508661970" datatype="html"> 3343 <trans-unit id="588099657508661970" datatype="html">
3348 <source>None (local authentication)</source> 3344 <source>None (local authentication)</source>
3349 <target state="translated">Нет (локальная аутентификация)</target> 3345 <target state="translated">Нет (локальная аутентификация)</target>
@@ -3594,6 +3590,12 @@ The link will expire within 1 hour.</source>
3594 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3590 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3595 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3591 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3596 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3592 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3593 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3594 <source>Follower</source><target state="new">Follower</target>
3595 <context-group purpose="location">
3596 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3597 <context context-type="linenumber">24</context>
3598 </context-group>
3597 </trans-unit> 3599 </trans-unit>
3598 <trans-unit id="4691552465058437520" datatype="html"> 3600 <trans-unit id="4691552465058437520" datatype="html">
3599 <source>Commented video</source> 3601 <source>Commented video</source>
@@ -3895,8 +3897,8 @@ The link will expire within 1 hour.</source>
3895 <trans-unit id="4917252294930256268" datatype="html"> 3897 <trans-unit id="4917252294930256268" datatype="html">
3896 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3898 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3897 <target state="translated">Похоже, вы не на сервере HTTPS. На вашем веб-сервере должен быть активирован TLS, чтобы следить за серверами.</target> 3899 <target state="translated">Похоже, вы не на сервере HTTPS. На вашем веб-сервере должен быть активирован TLS, чтобы следить за серверами.</target>
3898 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 3900
3899 </trans-unit> 3901 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3900 <trans-unit id="4058814854824495833" datatype="html"> 3902 <trans-unit id="4058814854824495833" datatype="html">
3901 <source>Mute domains</source> 3903 <source>Mute domains</source>
3902 <target state="translated">Отключить домены</target> 3904 <target state="translated">Отключить домены</target>
@@ -5839,11 +5841,8 @@ color: red;
5839 <trans-unit id="5512878593724620692" datatype="html"> 5841 <trans-unit id="5512878593724620692" datatype="html">
5840 <source>CHANNELS</source> 5842 <source>CHANNELS</source>
5841 <target state="translated">КАНАЛЫ</target> 5843 <target state="translated">КАНАЛЫ</target>
5842 <context-group purpose="location"> 5844
5843 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5845 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5844 <context context-type="linenumber">82</context>
5845 </context-group>
5846 </trans-unit>
5847 <trans-unit id="3666829335406793239" datatype="html"> 5846 <trans-unit id="3666829335406793239" datatype="html">
5848 <source>This account does not have channels.</source> 5847 <source>This account does not have channels.</source>
5849 <target state="translated">Эта учетная запись не имеет каналов.</target> 5848 <target state="translated">Эта учетная запись не имеет каналов.</target>
@@ -5882,6 +5881,12 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
5882channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 5881channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
5883 <target state="translated">Вы действительно хотите удалить <x id="PH" equiv-text="videoChannel.displayName"/>? Будет удалено <x id="PH_1" equiv-text="videoChannel.videosCount"/> видео загруженное на этот канал, и вы не сможете создать другой канал с таким же именем (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target> 5882 <target state="translated">Вы действительно хотите удалить <x id="PH" equiv-text="videoChannel.displayName"/>? Будет удалено <x id="PH_1" equiv-text="videoChannel.videosCount"/> видео загруженное на этот канал, и вы не сможете создать другой канал с таким же именем (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target>
5884 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 5883 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
5884 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
5885 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5886 <context-group purpose="location">
5887 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5888 <context context-type="linenumber">48</context>
5889 </context-group>
5885 </trans-unit> 5890 </trans-unit>
5886 <trans-unit id="5387007581996837469" datatype="html"> 5891 <trans-unit id="5387007581996837469" datatype="html">
5887 <source>My Channels</source> 5892 <source>My Channels</source>
@@ -6397,13 +6402,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6397 <trans-unit id="6979021199788941693"> 6402 <trans-unit id="6979021199788941693">
6398 <source>Your message has been sent.</source> 6403 <source>Your message has been sent.</source>
6399 <target>Ваше сообщение было отправлено.</target> 6404 <target>Ваше сообщение было отправлено.</target>
6400 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group> 6405
6401 </trans-unit> 6406 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6402 <trans-unit id="2072135752262464360"> 6407 <trans-unit id="2072135752262464360">
6403 <source>You already sent this form recently</source> 6408 <source>You already sent this form recently</source>
6404 <target>Вы уже отправили эту форму совсем недавно</target> 6409 <target>Вы уже отправили эту форму совсем недавно</target>
6405 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group> 6410
6406 </trans-unit> 6411 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6407 <trans-unit id="819067926858619041" datatype="html"> 6412 <trans-unit id="819067926858619041" datatype="html">
6408 <source>Account videos</source> 6413 <source>Account videos</source>
6409 <target state="translated">Видео аккаунта</target> 6414 <target state="translated">Видео аккаунта</target>
@@ -6446,13 +6451,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6446 <trans-unit id="4856575356061361269" datatype="html"> 6451 <trans-unit id="4856575356061361269" datatype="html">
6447 <source><x id="PH"/> direct account followers </source> 6452 <source><x id="PH"/> direct account followers </source>
6448 <target state="translated"><x id="PH"/> прямые подписчики аккаунта </target> 6453 <target state="translated"><x id="PH"/> прямые подписчики аккаунта </target>
6449 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6454
6450 </trans-unit> 6455 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6451 <trans-unit id="6250999352462648289" datatype="html"> 6456 <trans-unit id="6250999352462648289" datatype="html">
6452 <source>Report this account</source> 6457 <source>Report this account</source>
6453 <target state="translated">Пожаловаться на этот аккаунт</target> 6458 <target state="translated">Пожаловаться на этот аккаунт</target>
6454 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6459
6455 </trans-unit> 6460 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6456 <trans-unit id="1504521795586863905" datatype="html"> 6461 <trans-unit id="1504521795586863905" datatype="html">
6457 <source>VIDEOS</source> 6462 <source>VIDEOS</source>
6458 <target state="translated">ВИДЕО</target> 6463 <target state="translated">ВИДЕО</target>
@@ -6462,31 +6467,21 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6462 <trans-unit id="25349740244798533" datatype="html"> 6467 <trans-unit id="25349740244798533" datatype="html">
6463 <source>Username copied</source> 6468 <source>Username copied</source>
6464 <target state="translated">Имя пользователя скопировано</target> 6469 <target state="translated">Имя пользователя скопировано</target>
6465 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6470
6466 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6471
6467 </trans-unit> 6472 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6468 <trans-unit id="9221735175659318025" datatype="html"> 6473 <trans-unit id="9221735175659318025" datatype="html">
6469 <source>1 subscriber</source> 6474 <source>1 subscriber</source>
6470 <target state="translated">1 подписчик</target> 6475 <target state="translated">1 подписчик</target>
6471 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6476
6472 </trans-unit> 6477 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6473 <trans-unit id="4097331874769079975" datatype="html"> 6478 <trans-unit id="4097331874769079975" datatype="html">
6474 <source><x id="PH"/> subscribers</source> 6479 <source><x id="PH"/> subscribers</source>
6475 <target state="translated"><x id="PH"/> подписчиков</target> 6480 <target state="translated"><x id="PH"/> подписчиков</target>
6476 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6481
6477 </trans-unit> 6482 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6478 <trans-unit id="4682675125751819107" datatype="html"> 6483
6479 <source>Instances you follow</source> 6484
6480 <target state="translated">Экземпляры, за которыми вы следите</target>
6481 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6482 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6483 </trans-unit>
6484 <trans-unit id="8899833753704589712" datatype="html">
6485 <source>Instances following you</source>
6486 <target state="translated">Следующие за вами экземпляры</target>
6487 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6488 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6489 </trans-unit>
6490 <trans-unit id="1035838766454786107" datatype="html"> 6485 <trans-unit id="1035838766454786107" datatype="html">
6491 <source>Audio-only</source> 6486 <source>Audio-only</source>
6492 <target state="translated">Только для аудио</target> 6487 <target state="translated">Только для аудио</target>
@@ -6536,6 +6531,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6536 <source>Auto (via ffmpeg)</source> 6531 <source>Auto (via ffmpeg)</source>
6537 <target>Авто (используя ffmpeg)</target> 6532 <target>Авто (используя ffmpeg)</target>
6538 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6533 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6534 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6535 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6536 <context-group purpose="location">
6537 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6538 <context context-type="linenumber">3</context>
6539 </context-group>
6539 </trans-unit> 6540 </trans-unit>
6540 <trans-unit id="931255636742351800" datatype="html"> 6541 <trans-unit id="931255636742351800" datatype="html">
6541 <source>No limit</source> 6542 <source>No limit</source>
@@ -6684,18 +6685,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6684 <trans-unit id="2127446333083057097" datatype="html"> 6685 <trans-unit id="2127446333083057097" datatype="html">
6685 <source>Domain is required.</source> 6686 <source>Domain is required.</source>
6686 <target state="translated">Требуется домен.</target> 6687 <target state="translated">Требуется домен.</target>
6687 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6688
6688 </trans-unit> 6689 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6689 <trans-unit id="6780793142903080663" datatype="html"> 6690 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6690 <source>Domains entered are invalid.</source> 6691 <context-group purpose="location">
6691 <target state="translated">Введенные домены недействительны.</target> 6692 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6692 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6693 <context context-type="linenumber">93</context>
6693 </trans-unit> 6694 </context-group>
6694 <trans-unit id="5886492514458202177" datatype="html"> 6695 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6695 <source>Domains entered contain duplicates.</source> 6696 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6696 <target state="translated">Введенные домены содержат дубликаты.</target> 6697 <context-group purpose="location">
6697 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6698 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6699 <context context-type="linenumber">94</context>
6700 </context-group>
6701 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6702 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6703 <context-group purpose="location">
6704 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6705 <context context-type="linenumber">102</context>
6706 </context-group>
6707 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6708 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6709 <context-group purpose="location">
6710 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6711 <context context-type="linenumber">103</context>
6712 </context-group>
6698 </trans-unit> 6713 </trans-unit>
6714
6715
6699 <trans-unit id="240806681889331244"> 6716 <trans-unit id="240806681889331244">
6700 <source>Unlimited</source> 6717 <source>Unlimited</source>
6701 <target>Неограниченно</target> 6718 <target>Неограниченно</target>
@@ -6853,24 +6870,50 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6853 <x id="PH"/> удалено из последователей экземпляра 6870 <x id="PH"/> удалено из последователей экземпляра
6854 </target> 6871 </target>
6855 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6872 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6873 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6874 <source>Follow</source><target state="new">Follow</target>
6875 <context-group purpose="location">
6876 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6877 <context context-type="linenumber">3</context>
6878 </context-group>
6879 <context-group purpose="location">
6880 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6881 <context context-type="linenumber">37</context>
6882 </context-group>
6883 <context-group purpose="location">
6884 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6885 <context context-type="linenumber">18</context>
6886 </context-group>
6887 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6888 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6889 <context-group purpose="location">
6890 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6891 <context context-type="linenumber">11</context>
6892 </context-group>
6856 </trans-unit> 6893 </trans-unit>
6857 <trans-unit id="2740793005745065895"> 6894 <trans-unit id="2740793005745065895">
6858 <source><x id="PH"/> is not valid </source> 6895 <source><x id="PH"/> is not valid </source>
6859 <target> 6896 <target>
6860 <x id="PH"/> недействителен 6897 <x id="PH"/> недействителен
6861 </target> 6898 </target>
6862 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6899
6863 </trans-unit> 6900 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6864 <trans-unit id="2355066641781598196"> 6901 <trans-unit id="2355066641781598196">
6865 <source>Follow request(s) sent!</source> 6902 <source>Follow request(s) sent!</source>
6866 <target>Запрос (ы) на подписку отправлены!</target> 6903 <target>Запрос (ы) на подписку отправлены!</target>
6867 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6904
6905 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6906 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6907 <context-group purpose="location">
6908 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6909 <context context-type="linenumber">3</context>
6910 </context-group>
6868 </trans-unit> 6911 </trans-unit>
6869 <trans-unit id="4245720728052819482"> 6912 <trans-unit id="4245720728052819482">
6870 <source>Do you really want to unfollow <x id="PH"/>?</source> 6913 <source>Do you really want to unfollow <x id="PH"/>?</source>
6871 <target>Вы действительно хотите отписаться от <x id="PH"/>?</target> 6914 <target>Вы действительно хотите отписаться от <x id="PH"/>?</target>
6872 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6915
6873 </trans-unit> 6916 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6874 <trans-unit id="9160510009013134726"> 6917 <trans-unit id="9160510009013134726">
6875 <source>Unfollow</source> 6918 <source>Unfollow</source>
6876 <target>Отписаться</target> 6919 <target>Отписаться</target>
@@ -6879,8 +6922,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6879 <trans-unit id="3935234189109112926"> 6922 <trans-unit id="3935234189109112926">
6880 <source>You are not following <x id="PH"/> anymore.</source> 6923 <source>You are not following <x id="PH"/> anymore.</source>
6881 <target>Вы больше не подписаны на <x id="PH"/>.</target> 6924 <target>Вы больше не подписаны на <x id="PH"/>.</target>
6882 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6925
6883 </trans-unit> 6926 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6884 <trans-unit id="2593763089859685916"> 6927 <trans-unit id="2593763089859685916">
6885 <source>enabled</source> 6928 <source>enabled</source>
6886 <target>включено</target> 6929 <target>включено</target>
@@ -7352,9 +7395,9 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7352 <trans-unit id="1519954996184640001"> 7395 <trans-unit id="1519954996184640001">
7353 <source>Error</source> 7396 <source>Error</source>
7354 <target>Ошибка</target> 7397 <target>Ошибка</target>
7355 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7398
7356 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7399
7357 </trans-unit> 7400 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7358 <trans-unit id="5076187961693950167" datatype="html"> 7401 <trans-unit id="5076187961693950167" datatype="html">
7359 <source>Standard logs</source> 7402 <source>Standard logs</source>
7360 <target state="translated">Стандартные журналы</target> 7403 <target state="translated">Стандартные журналы</target>
@@ -7395,16 +7438,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7395 <target>Изменить пароль пользователя</target> 7438 <target>Изменить пароль пользователя</target>
7396 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7439 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7397 </trans-unit> 7440 </trans-unit>
7398 <trans-unit id="177544274549739411" datatype="html"> 7441
7399 <source>Following list</source> 7442
7400 <target state="translated">Следующий список</target>
7401 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7402 </trans-unit>
7403 <trans-unit id="8092429110007204784" datatype="html">
7404 <source>Followers list</source>
7405 <target state="translated">Список подписчиков</target>
7406 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7407 </trans-unit>
7408 <trans-unit id="780323526182667308" datatype="html"> 7443 <trans-unit id="780323526182667308" datatype="html">
7409 <source>User <x id="PH"/> updated.</source> 7444 <source>User <x id="PH"/> updated.</source>
7410 <target state="translated">Пользователь <x id="PH"/> обновлён.</target> 7445 <target state="translated">Пользователь <x id="PH"/> обновлён.</target>
@@ -7440,16 +7475,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7440 <target state="translated">Федерация</target> 7475 <target state="translated">Федерация</target>
7441 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7476 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7442 </trans-unit> 7477 </trans-unit>
7443 <trans-unit id="4682675125751819107" datatype="html"> 7478
7444 <source>Instances you follow</source> 7479
7445 <target state="translated">Экземпляры, за которыми вы следите</target>
7446 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7447 </trans-unit>
7448 <trans-unit id="8899833753704589712" datatype="html">
7449 <source>Instances following you</source>
7450 <target state="translated">Экземпляры подписанные на вас</target>
7451 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7452 </trans-unit>
7453 <trans-unit id="3767259920053407667" datatype="html"> 7480 <trans-unit id="3767259920053407667" datatype="html">
7454 <source>Videos will be deleted, comments will be tombstoned.</source> 7481 <source>Videos will be deleted, comments will be tombstoned.</source>
7455 <target state="translated">Видео будет удалено, комментарии будут заморожены.</target> 7482 <target state="translated">Видео будет удалено, комментарии будут заморожены.</target>
@@ -7480,6 +7507,24 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7480 <target>Пометить электронную почту как подтверждённую</target> 7507 <target>Пометить электронную почту как подтверждённую</target>
7481 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7508 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7482 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7509 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7510 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7511 <source>Created</source><target state="new">Created</target>
7512 <context-group purpose="location">
7513 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7514 <context context-type="linenumber">115</context>
7515 </context-group>
7516 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7517 <source>Daily quota</source><target state="new">Daily quota</target>
7518 <context-group purpose="location">
7519 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7520 <context context-type="linenumber">120</context>
7521 </context-group>
7522 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7523 <source>Last login</source><target state="new">Last login</target>
7524 <context-group purpose="location">
7525 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7526 <context context-type="linenumber">122</context>
7527 </context-group>
7483 </trans-unit> 7528 </trans-unit>
7484 <trans-unit id="3403978719736970622"> 7529 <trans-unit id="3403978719736970622">
7485 <source>You cannot ban root.</source> 7530 <source>You cannot ban root.</source>
@@ -7784,13 +7829,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7784 <trans-unit id="1137937154872046253"> 7829 <trans-unit id="1137937154872046253">
7785 <source>Video channel <x id="PH"/> created.</source> 7830 <source>Video channel <x id="PH"/> created.</source>
7786 <target>Видеоканал <x id="PH"/> был создан.</target> 7831 <target>Видеоканал <x id="PH"/> был создан.</target>
7787 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 7832
7788 </trans-unit> 7833 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7789 <trans-unit id="8723777130353305761"> 7834 <trans-unit id="8723777130353305761">
7790 <source>This name already exists on this instance.</source> 7835 <source>This name already exists on this instance.</source>
7791 <target>Данное название уже занято на этом сервере.</target> 7836 <target>Данное название уже занято на этом сервере.</target>
7792 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 7837
7793 </trans-unit> 7838 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7794 <trans-unit id="7589345916094713536"> 7839 <trans-unit id="7589345916094713536">
7795 <source>Video channel <x id="PH"/> updated.</source> 7840 <source>Video channel <x id="PH"/> updated.</source>
7796 <target>Видеоканал <x id="PH"/> обновлён.</target> 7841 <target>Видеоканал <x id="PH"/> обновлён.</target>
@@ -7811,11 +7856,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7811 <target state="translated">Баннер удален.</target> 7856 <target state="translated">Баннер удален.</target>
7812 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 7857 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
7813 </trans-unit> 7858 </trans-unit>
7814 <trans-unit id="2575302837003821736"> 7859
7815 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7816 <target>Пожалуйста, напишите название канала ( <x id="PH"/>) для подтверждения</target>
7817 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
7818 </trans-unit>
7819 <trans-unit id="624066830180032195"> 7860 <trans-unit id="624066830180032195">
7820 <source>Video channel <x id="PH"/> deleted.</source> 7861 <source>Video channel <x id="PH"/> deleted.</source>
7821 <target>Видеоканал <x id="PH"/> был удалён.</target> 7862 <target>Видеоканал <x id="PH"/> был удалён.</target>
@@ -7967,6 +8008,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7967 <source>Ownership change request sent.</source> 8008 <source>Ownership change request sent.</source>
7968 <target>Заявка на смена владельца отправлена.</target> 8009 <target>Заявка на смена владельца отправлена.</target>
7969 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8010 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8011 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8012 <source>Sort by</source><target state="new">Sort by</target>
8013 <context-group purpose="location">
8014 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8015 <context context-type="linenumber">26</context>
8016 </context-group>
7970 </trans-unit> 8017 </trans-unit>
7971 <trans-unit id="3245220240937722814"> 8018 <trans-unit id="3245220240937722814">
7972 <source>My channels</source> 8019 <source>My channels</source>
@@ -8065,7 +8112,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8065 <target>Подписаться на аккаунт</target> 8112 <target>Подписаться на аккаунт</target>
8066 8113
8067 8114
8068 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8115 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8069 <trans-unit id="3131904093925601441" datatype="html"> 8116 <trans-unit id="3131904093925601441" datatype="html">
8070 <source>PLAYLISTS</source> 8117 <source>PLAYLISTS</source>
8071 <target state="translated">ПЛЕЙЛИСТЫ</target> 8118 <target state="translated">ПЛЕЙЛИСТЫ</target>
@@ -8112,34 +8159,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8112 <trans-unit id="3779524668013120370"> 8159 <trans-unit id="3779524668013120370">
8113 <source>Go to my subscriptions</source> 8160 <source>Go to my subscriptions</source>
8114 <target>Перейти на мои подписки</target> 8161 <target>Перейти на мои подписки</target>
8115 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8162
8116 </trans-unit> 8163 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8117 <trans-unit id="1136469849928650779"> 8164 <trans-unit id="1136469849928650779">
8118 <source>Go to my videos</source> 8165 <source>Go to my videos</source>
8119 <target>Перейти на мои видео</target> 8166 <target>Перейти на мои видео</target>
8120 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8167
8121 </trans-unit> 8168 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8122 <trans-unit id="7836683738999600376"> 8169 <trans-unit id="7836683738999600376">
8123 <source>Go to my imports</source> 8170 <source>Go to my imports</source>
8124 <target>Перейти на мои импортированные видео</target> 8171 <target>Перейти на мои импортированные видео</target>
8125 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8172
8126 </trans-unit> 8173 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8127 <trans-unit id="7511292153332773503"> 8174 <trans-unit id="7511292153332773503">
8128 <source>Go to my channels</source> 8175 <source>Go to my channels</source>
8129 <target>Перейти на мои каналы</target> 8176 <target>Перейти на мои каналы</target>
8130 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8177
8131 </trans-unit> 8178 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8132 <trans-unit id="2013324644839511073" datatype="html"> 8179 <trans-unit id="2013324644839511073" datatype="html">
8133 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8180 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8134Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8181Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8135 <target state="translated">Не удается получить учетные данные клиента OAuth: <x id="PH" equiv-text="error.text"/>. Убедитесь, что вы правильно настроили PeerTube (config / directory), в частности раздел «веб-сервер».</target> 8182 <target state="translated">Не удается получить учетные данные клиента OAuth: <x id="PH" equiv-text="error.text"/>. Убедитесь, что вы правильно настроили PeerTube (config / directory), в частности раздел «веб-сервер».</target>
8136 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8183
8137 </trans-unit> 8184 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8138 <trans-unit id="375263728166936544"> 8185 <trans-unit id="375263728166936544">
8139 <source>You need to reconnect.</source> 8186 <source>You need to reconnect.</source>
8140 <target>Вам необходимо переподключиться.</target> 8187 <target>Вам необходимо переподключиться.</target>
8141 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8188
8142 </trans-unit> 8189 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8143 <trans-unit id="2206638022166154361"> 8190 <trans-unit id="2206638022166154361">
8144 <source>Keyboard Shortcuts:</source> 8191 <source>Keyboard Shortcuts:</source>
8145 <target>Комбинации клавиш:</target> 8192 <target>Комбинации клавиш:</target>
@@ -8152,6 +8199,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8152 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8199 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8153 <context context-type="linenumber">98</context> 8200 <context context-type="linenumber">98</context>
8154 </context-group> 8201 </context-group>
8202 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8203 <source>In my library</source><target state="new">In my library</target>
8204 <context-group purpose="location">
8205 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8206 <context context-type="linenumber">104</context>
8207 </context-group>
8155 </trans-unit> 8208 </trans-unit>
8156 <trans-unit id="232050922346936574" datatype="html"> 8209 <trans-unit id="232050922346936574" datatype="html">
8157 <source>Trending</source> 8210 <source>Trending</source>
@@ -8180,38 +8233,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8180 <trans-unit id="1266887509445371246"> 8233 <trans-unit id="1266887509445371246">
8181 <source>Incorrect username or password.</source> 8234 <source>Incorrect username or password.</source>
8182 <target>Неверное имя пользователя или пароль.</target> 8235 <target>Неверное имя пользователя или пароль.</target>
8183 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8236
8184 </trans-unit> 8237 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8185 <trans-unit id="6974874606619467663" datatype="html"> 8238 <trans-unit id="6974874606619467663" datatype="html">
8186 <source>Your account is blocked.</source> 8239 <source>Your account is blocked.</source>
8187 <target state="translated">Ваш аккаунт заблокирован.</target> 8240 <target state="translated">Ваш аккаунт заблокирован.</target>
8188 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8241
8189 </trans-unit> 8242 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8190 <trans-unit id="7939914198003891823" datatype="html"> 8243 <trans-unit id="7939914198003891823" datatype="html">
8191 <source>any language</source> 8244 <source>any language</source>
8192 <target state="translated">любой язык</target> 8245 <target state="translated">любой язык</target>
8193 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8246
8194 </trans-unit> 8247 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8195 <trans-unit id="5633144232269377096" datatype="html"> 8248 <trans-unit id="5633144232269377096" datatype="html">
8196 <source>hide</source> 8249 <source>hide</source>
8197 <target state="translated">скрыть</target> 8250 <target state="translated">скрыть</target>
8198 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8251
8199 </trans-unit> 8252 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8200 <trans-unit id="8603861867909474404" datatype="html"> 8253 <trans-unit id="8603861867909474404" datatype="html">
8201 <source>blur</source> 8254 <source>blur</source>
8202 <target state="translated">размытие</target> 8255 <target state="translated">размытие</target>
8203 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8256
8204 </trans-unit> 8257 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8205 <trans-unit id="4534458451100881847" datatype="html"> 8258 <trans-unit id="4534458451100881847" datatype="html">
8206 <source>display</source> 8259 <source>display</source>
8207 <target state="translated">отображение</target> 8260 <target state="translated">отображение</target>
8208 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8261
8209 </trans-unit> 8262 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8210 <trans-unit id="4467323362722952678" datatype="html"> 8263 <trans-unit id="4467323362722952678" datatype="html">
8211 <source>Unknown</source> 8264 <source>Unknown</source>
8212 <target state="translated">Неизвестно</target> 8265 <target state="translated">Неизвестно</target>
8213 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8266
8214 </trans-unit> 8267 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8215 <trans-unit id="8781423666414310853"> 8268 <trans-unit id="8781423666414310853">
8216 <source>Your password has been successfully reset!</source> 8269 <source>Your password has been successfully reset!</source>
8217 <target>Ваш пароль был успешно сброшен!</target> 8270 <target>Ваш пароль был успешно сброшен!</target>
@@ -9783,18 +9836,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9783 <trans-unit id="968295009933361070"> 9836 <trans-unit id="968295009933361070">
9784 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9837 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9785 <target>Слишком много попыток, пожалуйста, попробуйте снова через <x id="PH"/> минут.</target> 9838 <target>Слишком много попыток, пожалуйста, попробуйте снова через <x id="PH"/> минут.</target>
9786 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9839
9787 </trans-unit> 9840 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9788 <trans-unit id="4965472196059235310"> 9841 <trans-unit id="4965472196059235310">
9789 <source>Too many attempts, please try again later.</source> 9842 <source>Too many attempts, please try again later.</source>
9790 <target>Слишком много попыток, пожалуйста, попробуйте ещё раз позже.</target> 9843 <target>Слишком много попыток, пожалуйста, попробуйте ещё раз позже.</target>
9791 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9844
9792 </trans-unit> 9845 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9793 <trans-unit id="1693549688987384699"> 9846 <trans-unit id="1693549688987384699">
9794 <source>Server error. Please retry later.</source> 9847 <source>Server error. Please retry later.</source>
9795 <target>Ошибка сервера. Пожалуйста, повторите попытку позже.</target> 9848 <target>Ошибка сервера. Пожалуйста, повторите попытку позже.</target>
9796 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9849
9797 </trans-unit> 9850 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9798 <trans-unit id="5927402622550505067" datatype="html"> 9851 <trans-unit id="5927402622550505067" datatype="html">
9799 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9852 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9800 <target state="translated">Подписан на все текущие каналы <x id="PH"/>. Вы будете уведомлены обо всех их новых видео.</target> 9853 <target state="translated">Подписан на все текущие каналы <x id="PH"/>. Вы будете уведомлены обо всех их новых видео.</target>
@@ -10385,33 +10438,33 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10385 <trans-unit id="3284171506518522275"> 10438 <trans-unit id="3284171506518522275">
10386 <source>Your video was uploaded to your account and is private.</source> 10439 <source>Your video was uploaded to your account and is private.</source>
10387 <target>Ваше видео было загружено на ваш аккаунт и является приватным.</target> 10440 <target>Ваше видео было загружено на ваш аккаунт и является приватным.</target>
10388 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10441
10389 </trans-unit> 10442 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10390 <trans-unit id="5699822024600815733"> 10443 <trans-unit id="5699822024600815733">
10391 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10444 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10392 <target>Но связанные данные (теги, описание...) будут потеряны, вы уверены, что хотите покинуть эту страницу?</target> 10445 <target>Но связанные данные (теги, описание...) будут потеряны, вы уверены, что хотите покинуть эту страницу?</target>
10393 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10446
10394 </trans-unit> 10447 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10395 <trans-unit id="1219739004043110649"> 10448 <trans-unit id="1219739004043110649">
10396 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10449 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10397 <target>Ваше видео еще не загружено, вы уверены, что хотите покинуть эту страницу?</target> 10450 <target>Ваше видео еще не загружено, вы уверены, что хотите покинуть эту страницу?</target>
10398 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10451
10399 </trans-unit> 10452 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10400 <trans-unit id="6932865105766151309" datatype="html"> 10453 <trans-unit id="6932865105766151309" datatype="html">
10401 <source>Upload</source> 10454 <source>Upload</source>
10402 <target state="translated">Загрузить</target> 10455 <target state="translated">Загрузить</target>
10403 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10456
10404 </trans-unit> 10457 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10405 <trans-unit id="8278735427925094503" datatype="html"> 10458 <trans-unit id="8278735427925094503" datatype="html">
10406 <source>Upload <x id="PH"/> </source> 10459 <source>Upload <x id="PH"/> </source>
10407 <target state="translated">Загрузить <x id="PH"/> </target> 10460 <target state="translated">Загрузить <x id="PH"/> </target>
10408 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10461
10409 </trans-unit> 10462 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10410 <trans-unit id="5981816353437801748"> 10463 <trans-unit id="5981816353437801748">
10411 <source>Video published.</source> 10464 <source>Video published.</source>
10412 <target>Видео опубликовано.</target> 10465 <target>Видео опубликовано.</target>
10413 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10466
10414 </trans-unit> 10467 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10415 <trans-unit id="764164089183618119"> 10468 <trans-unit id="764164089183618119">
10416 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10469 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10417 <target>У вас есть несохраненные изменения! Если вы уйдете, ваши изменения будут потеряны.</target> 10470 <target>У вас есть несохраненные изменения! Если вы уйдете, ваши изменения будут потеряны.</target>
@@ -10458,28 +10511,28 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10458 <trans-unit id="961774488937452220" datatype="html"> 10511 <trans-unit id="961774488937452220" datatype="html">
10459 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10512 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10460 <target state="translated">Это видео недоступно в этом экземпляре. Вы хотите, чтобы вас перенаправили на исходный экземпляр: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10513 <target state="translated">Это видео недоступно в этом экземпляре. Вы хотите, чтобы вас перенаправили на исходный экземпляр: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10461 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group> 10514
10462 </trans-unit> 10515 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10463 <trans-unit id="5761611056224181752" datatype="html"> 10516 <trans-unit id="5761611056224181752" datatype="html">
10464 <source>Redirection</source> 10517 <source>Redirection</source>
10465 <target state="translated">Перенаправление</target> 10518 <target state="translated">Перенаправление</target>
10466 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group> 10519
10467 </trans-unit> 10520 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10468 <trans-unit id="8858527736400081688"> 10521 <trans-unit id="8858527736400081688">
10469 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10522 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10470 <target>Это видео содержит зрелый или откровенный контент. Вы уверены, что хотите посмотреть его?</target> 10523 <target>Это видео содержит зрелый или откровенный контент. Вы уверены, что хотите посмотреть его?</target>
10471 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group> 10524
10472 </trans-unit> 10525 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10473 <trans-unit id="3937119019020041049"> 10526 <trans-unit id="3937119019020041049">
10474 <source>Mature or explicit content</source> 10527 <source>Mature or explicit content</source>
10475 <target>Зрелый или откровенный контент</target> 10528 <target>Зрелый или откровенный контент</target>
10476 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group> 10529
10477 </trans-unit> 10530 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10478 <trans-unit id="1755474755114288376" datatype="html"> 10531 <trans-unit id="1755474755114288376" datatype="html">
10479 <source>Up Next</source> 10532 <source>Up Next</source>
10480 <target state="translated">Следующий</target> 10533 <target state="translated">Следующий</target>
10481 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group> 10534
10482 </trans-unit> 10535 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10483 <trans-unit id="2159130950882492111" datatype="html"> 10536 <trans-unit id="2159130950882492111" datatype="html">
10484 <source>Cancel</source> 10537 <source>Cancel</source>
10485 <target state="translated">Отмена</target> 10538 <target state="translated">Отмена</target>
@@ -10488,63 +10541,63 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10488 <trans-unit id="3354816756665089864" datatype="html"> 10541 <trans-unit id="3354816756665089864" datatype="html">
10489 <source>Autoplay is suspended</source> 10542 <source>Autoplay is suspended</source>
10490 <target state="translated">Автовоспроизведение приостановлено</target> 10543 <target state="translated">Автовоспроизведение приостановлено</target>
10491 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group> 10544
10492 </trans-unit> 10545 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10493 <trans-unit id="7895294730547405228" datatype="html"> 10546 <trans-unit id="7895294730547405228" datatype="html">
10494 <source>Enter/exit fullscreen (requires player focus)</source> 10547 <source>Enter/exit fullscreen (requires player focus)</source>
10495 <target state="translated">Вход / выход в полноэкранный режим (требуется фокус проигрывателя)</target> 10548 <target state="translated">Вход / выход в полноэкранный режим (требуется фокус проигрывателя)</target>
10496 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group> 10549
10497 </trans-unit> 10550 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10498 <trans-unit id="7618388257165864759" datatype="html"> 10551 <trans-unit id="7618388257165864759" datatype="html">
10499 <source>Play/Pause the video (requires player focus)</source> 10552 <source>Play/Pause the video (requires player focus)</source>
10500 <target state="translated">Воспроизвести / приостановить видео (требуется фокус проигрывателя)</target> 10553 <target state="translated">Воспроизвести / приостановить видео (требуется фокус проигрывателя)</target>
10501 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group> 10554
10502 </trans-unit> 10555 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10503 <trans-unit id="7761890399634216630" datatype="html"> 10556 <trans-unit id="7761890399634216630" datatype="html">
10504 <source>Mute/unmute the video (requires player focus)</source> 10557 <source>Mute/unmute the video (requires player focus)</source>
10505 <target state="translated">Отключить / включить видео (требуется фокус проигрывателя)</target> 10558 <target state="translated">Отключить / включить видео (требуется фокус проигрывателя)</target>
10506 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group> 10559
10507 </trans-unit> 10560 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10508 <trans-unit id="5996585232248234904" datatype="html"> 10561 <trans-unit id="5996585232248234904" datatype="html">
10509 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10562 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10510 <target state="translated">Переход к проценту от видео: 0 - 0%, 9 - 90% (требуется фокус проигрывателя)</target> 10563 <target state="translated">Переход к проценту от видео: 0 - 0%, 9 - 90% (требуется фокус проигрывателя)</target>
10511 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group> 10564
10512 </trans-unit> 10565 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10513 <trans-unit id="3748765405903319998" datatype="html"> 10566 <trans-unit id="3748765405903319998" datatype="html">
10514 <source>Increase the volume (requires player focus)</source> 10567 <source>Increase the volume (requires player focus)</source>
10515 <target state="translated">Увеличьте громкость (требуется фокус проигрывателя)</target> 10568 <target state="translated">Увеличьте громкость (требуется фокус проигрывателя)</target>
10516 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group> 10569
10517 </trans-unit> 10570 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10518 <trans-unit id="5810704036407159982" datatype="html"> 10571 <trans-unit id="5810704036407159982" datatype="html">
10519 <source>Decrease the volume (requires player focus)</source> 10572 <source>Decrease the volume (requires player focus)</source>
10520 <target state="translated">Уменьшить громкость (требуется фокус проигрывателя)</target> 10573 <target state="translated">Уменьшить громкость (требуется фокус проигрывателя)</target>
10521 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group> 10574
10522 </trans-unit> 10575 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10523 <trans-unit id="2622048822548065691" datatype="html"> 10576 <trans-unit id="2622048822548065691" datatype="html">
10524 <source>Seek the video forward (requires player focus)</source> 10577 <source>Seek the video forward (requires player focus)</source>
10525 <target state="translated">Искать видео вперед(требуется фокус проигрывателя)</target> 10578 <target state="translated">Искать видео вперед(требуется фокус проигрывателя)</target>
10526 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group> 10579
10527 </trans-unit> 10580 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10528 <trans-unit id="6540078205109221153" datatype="html"> 10581 <trans-unit id="6540078205109221153" datatype="html">
10529 <source>Seek the video backward (requires player focus)</source> 10582 <source>Seek the video backward (requires player focus)</source>
10530 <target state="translated">Искать видео в обратном направлении (требуется фокус проигрывателя)</target> 10583 <target state="translated">Искать видео в обратном направлении (требуется фокус проигрывателя)</target>
10531 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group> 10584
10532 </trans-unit> 10585 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10533 <trans-unit id="1956491957766210808" datatype="html"> 10586 <trans-unit id="1956491957766210808" datatype="html">
10534 <source>Increase playback rate (requires player focus)</source> 10587 <source>Increase playback rate (requires player focus)</source>
10535 <target state="translated">Увеличить скорость воспроизведения(требуется фокус проигрывателя)</target> 10588 <target state="translated">Увеличить скорость воспроизведения(требуется фокус проигрывателя)</target>
10536 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group> 10589
10537 </trans-unit> 10590 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10538 <trans-unit id="5495529997674803186" datatype="html"> 10591 <trans-unit id="5495529997674803186" datatype="html">
10539 <source>Decrease playback rate (requires player focus)</source> 10592 <source>Decrease playback rate (requires player focus)</source>
10540 <target state="translated">Уменьшить скорость воспроизведения (требуется фокус проигрывателя)</target> 10593 <target state="translated">Уменьшить скорость воспроизведения (требуется фокус проигрывателя)</target>
10541 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group> 10594
10542 </trans-unit> 10595 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10543 <trans-unit id="3178343147230721210" datatype="html"> 10596 <trans-unit id="3178343147230721210" datatype="html">
10544 <source>Navigate in the video frame by frame (requires player focus)</source> 10597 <source>Navigate in the video frame by frame (requires player focus)</source>
10545 <target state="translated">Navigate in the video frame by frame (требуется фокус проигрывателя)</target> 10598 <target state="translated">Navigate in the video frame by frame (требуется фокус проигрывателя)</target>
10546 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group> 10599
10547 </trans-unit> 10600 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10548 <trans-unit id="8025996572234182184"> 10601 <trans-unit id="8025996572234182184">
10549 <source>Like the video</source> 10602 <source>Like the video</source>
10550 <target>Мне понравилось</target> 10603 <target>Мне понравилось</target>
diff --git a/client/src/locale/angular.sk-SK.xlf b/client/src/locale/angular.sk-SK.xlf
index 87a114685..4cd53b149 100644
--- a/client/src/locale/angular.sk-SK.xlf
+++ b/client/src/locale/angular.sk-SK.xlf
@@ -307,7 +307,7 @@
307 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 307 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
308 </target> 308 </target>
309 309
310 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html"> 310 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html">
311 <source>My watch history</source><target state="new">My watch history</target> 311 <source>My watch history</source><target state="new">My watch history</target>
312 312
313 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 313 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
@@ -388,12 +388,12 @@
388 388
389 389
390 390
391 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 391 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
392 <trans-unit id="1006562256968398209" datatype="html"> 392 <trans-unit id="1006562256968398209" datatype="html">
393 <source>video</source> 393 <source>video</source>
394 <target state="new">video</target> 394 <target state="new">video</target>
395 395
396 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 396 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
397 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 397 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
398 398
399 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 399 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -455,10 +455,10 @@
455 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 455 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
456 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 456 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
457 457
458 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 458 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
459 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 459 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
460 460
461 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html"> 461 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html">
462 <source>subtitles</source><target state="new">subtitles</target> 462 <source>subtitles</source><target state="new">subtitles</target>
463 463
464 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 464 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
@@ -847,7 +847,7 @@
847 847
848 848
849 849
850 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group></trans-unit> 850 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
851 <trans-unit id="1502595455339510144" datatype="html"> 851 <trans-unit id="1502595455339510144" datatype="html">
852 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 852 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
853 <target state="new"> 853 <target state="new">
@@ -930,7 +930,31 @@
930 <source>Federation</source> 930 <source>Federation</source>
931 <target state="new">Federation</target> 931 <target state="new">Federation</target>
932 932
933 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 933 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
934 <source>Following</source><target state="new">Following</target>
935 <context-group purpose="location">
936 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
937 <context context-type="linenumber">29</context>
938 </context-group>
939 <context-group purpose="location">
940 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
941 <context context-type="linenumber">31</context>
942 </context-group>
943 <context-group purpose="location">
944 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
945 <context context-type="linenumber">28</context>
946 </context-group>
947 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
948 <source>Followers</source><target state="new">Followers</target>
949 <context-group purpose="location">
950 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
951 <context context-type="linenumber">34</context>
952 </context-group>
953 <context-group purpose="location">
954 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
955 <context context-type="linenumber">37</context>
956 </context-group>
957 </trans-unit>
934 <trans-unit id="3541687134897970106" datatype="html"> 958 <trans-unit id="3541687134897970106" datatype="html">
935 <source>followers</source> 959 <source>followers</source>
936 <target state="new">followers</target> 960 <target state="new">followers</target>
@@ -994,7 +1018,7 @@
994 1018
995 1019
996 1020
997 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 1021 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
998 <trans-unit id="3616223838716839702" datatype="html"> 1022 <trans-unit id="3616223838716839702" datatype="html">
999 <source>Ban this user</source> 1023 <source>Ban this user</source>
1000 <target state="new">Ban this user</target> 1024 <target state="new">Ban this user</target>
@@ -1062,17 +1086,11 @@
1062 1086
1063 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html"> 1087 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html">
1064 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1088 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1065 <context-group purpose="location"> 1089
1066 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1090 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7215649348148521605" datatype="html">
1067 <context context-type="linenumber">60,62</context>
1068 </context-group>
1069 </trans-unit><trans-unit id="7215649348148521605" datatype="html">
1070 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1091 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1071 <context-group purpose="location"> 1092
1072 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1093 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1073 <context context-type="linenumber">65,67</context>
1074 </context-group>
1075 </trans-unit>
1076 <trans-unit id="2392488717875840729" datatype="html"> 1094 <trans-unit id="2392488717875840729" datatype="html">
1077 <source>User</source> 1095 <source>User</source>
1078 <target state="new">User</target> 1096 <target state="new">User</target>
@@ -1083,7 +1101,13 @@
1083 <source>Username or email address</source> 1101 <source>Username or email address</source>
1084 <target state="new">Username or email address</target> 1102 <target state="new">Username or email address</target>
1085 1103
1086 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit> 1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="1758058452376026925" datatype="html">
1105 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1106 <context-group purpose="location">
1107 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1108 <context context-type="linenumber">33,34</context>
1109 </context-group>
1110 </trans-unit>
1087 1111
1088 <trans-unit id="1431416938026210429" datatype="html"> 1112 <trans-unit id="1431416938026210429" datatype="html">
1089 <source>Password</source> 1113 <source>Password</source>
@@ -1096,40 +1120,34 @@
1096 1120
1097 1121
1098 1122
1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group></trans-unit> 1123 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1100 <trans-unit id="8715156686857791956" datatype="html"> 1124 <trans-unit id="8715156686857791956" datatype="html">
1101 <source>Click here to reset your password</source> 1125 <source>Click here to reset your password</source>
1102 <target state="new">Click here to reset your password</target> 1126 <target state="new">Click here to reset your password</target>
1103 1127
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html"> 1128 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html">
1105 <source>I forgot my password</source><target state="new">I forgot my password</target> 1129 <source>I forgot my password</source><target state="new">I forgot my password</target>
1106 <context-group purpose="location"> 1130
1107 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1131 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="2101170466365500913" datatype="html">
1108 <context context-type="linenumber">47</context>
1109 </context-group>
1110 </trans-unit><trans-unit id="2101170466365500913" datatype="html">
1111 <source> Logging into an account lets you publish content </source><target state="new"> Logging into an account lets you publish content </target> 1132 <source> Logging into an account lets you publish content </source><target state="new"> Logging into an account lets you publish content </target>
1112 <context-group purpose="location"> 1133
1113 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1134 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1114 <context context-type="linenumber">56,57</context>
1115 </context-group>
1116 </trans-unit>
1117 <trans-unit id="2454050363478003966" datatype="html"> 1135 <trans-unit id="2454050363478003966" datatype="html">
1118 <source>Login</source> 1136 <source>Login</source>
1119 <target state="new">Login</target> 1137 <target state="new">Login</target>
1120 1138
1121 1139
1122 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1140 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1123 <trans-unit id="3183213940445113677" datatype="html"> 1141 <trans-unit id="3183213940445113677" datatype="html">
1124 <source>Or sign in with</source> 1142 <source>Or sign in with</source>
1125 <target state="new">Or sign in with</target> 1143 <target state="new">Or sign in with</target>
1126 1144
1127 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit> 1145 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1128 <trans-unit id="3238209155172574367" datatype="html"> 1146 <trans-unit id="3238209155172574367" datatype="html">
1129 <source>Forgot your password</source> 1147 <source>Forgot your password</source>
1130 <target state="new">Forgot your password</target> 1148 <target state="new">Forgot your password</target>
1131 1149
1132 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group></trans-unit> 1150 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1133 <trans-unit id="87327320394367488" datatype="html"> 1151 <trans-unit id="87327320394367488" datatype="html">
1134 <source> 1152 <source>
1135 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1153 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
@@ -1138,10 +1156,10 @@
1138 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1156 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
1139 </target> 1157 </target>
1140 1158
1141 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html"> 1159 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html">
1142 <source> Enter your email address and we will send you a link to reset your password. </source><target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1160 <source> Enter your email address and we will send you a link to reset your password. </source><target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1143 1161
1144 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html"> 1162 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html">
1145 <source>An email with the reset password instructions will be sent to <x id="PH"/>. 1163 <source>An email with the reset password instructions will be sent to <x id="PH"/>.
1146The link will expire within 1 hour.</source><target state="new">An email with the reset password instructions will be sent to <x id="PH"/>. 1164The link will expire within 1 hour.</source><target state="new">An email with the reset password instructions will be sent to <x id="PH"/>.
1147The link will expire within 1 hour.</target> 1165The link will expire within 1 hour.</target>
@@ -1157,17 +1175,17 @@ The link will expire within 1 hour.</target>
1157 1175
1158 1176
1159 1177
1160 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group></trans-unit> 1178 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1161 <trans-unit id="3967269098753656610" datatype="html"> 1179 <trans-unit id="3967269098753656610" datatype="html">
1162 <source>Email address</source> 1180 <source>Email address</source>
1163 <target state="new">Email address</target> 1181 <target state="new">Email address</target>
1164 1182
1165 1183
1166 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html"> 1184 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html">
1167 <source>Reset</source><target state="new">Reset</target> 1185 <source>Reset</source><target state="new">Reset</target>
1168 1186
1169 <note priority="1" from="description">Password reset button</note> 1187 <note priority="1" from="description">Password reset button</note>
1170 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group></trans-unit> 1188 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1171 1189
1172 1190
1173 <trans-unit id="4319634264526091601" datatype="html"> 1191 <trans-unit id="4319634264526091601" datatype="html">
@@ -1537,7 +1555,7 @@ The link will expire within 1 hour.</target>
1537 <source>Create an account</source> 1555 <source>Create an account</source>
1538 <target state="new">Create an account</target> 1556 <target state="new">Create an account</target>
1539 1557
1540 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1558 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1541 1559
1542 <trans-unit id="3058024914967508975" datatype="html"> 1560 <trans-unit id="3058024914967508975" datatype="html">
1543 <source>My videos</source><target state="new">My videos</target> 1561 <source>My videos</source><target state="new">My videos</target>
@@ -1583,7 +1601,7 @@ The link will expire within 1 hour.</target>
1583 <target state="new">VIDEOS</target> 1601 <target state="new">VIDEOS</target>
1584 1602
1585 1603
1586 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html"> 1604 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html">
1587 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target> 1605 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target>
1588 1606
1589 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html"> 1607 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html">
@@ -1651,7 +1669,7 @@ The link will expire within 1 hour.</target>
1651 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html"> 1669 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html">
1652 <source>I'm a teapot</source><target state="new">I'm a teapot</target> 1670 <source>I'm a teapot</source><target state="new">I'm a teapot</target>
1653 1671
1654 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html"> 1672 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html">
1655 <source>That's an error.</source><target state="new">That's an error.</target> 1673 <source>That's an error.</source><target state="new">That's an error.</target>
1656 <context-group purpose="location"> 1674 <context-group purpose="location">
1657 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context> 1675 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context>
@@ -1717,7 +1735,7 @@ The link will expire within 1 hour.</target>
1717 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html"> 1735 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html">
1718 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1736 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1719 1737
1720 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group></trans-unit> 1738 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1721 1739
1722 <trans-unit id="5131854469652959713" datatype="html"> 1740 <trans-unit id="5131854469652959713" datatype="html">
1723 <source>GLOBAL SEARCH</source> 1741 <source>GLOBAL SEARCH</source>
@@ -2425,7 +2443,7 @@ The link will expire within 1 hour.</target>
2425 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2443 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2426 <source>Upload on hold</source><target state="new">Upload on hold</target> 2444 <source>Upload on hold</source><target state="new">Upload on hold</target>
2427 2445
2428 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2446 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2429 <trans-unit id="285180972645018518" datatype="html"> 2447 <trans-unit id="285180972645018518" datatype="html">
2430 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2448 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2431 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2449 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3097,11 +3115,7 @@ The link will expire within 1 hour.</target>
3097 <target state="new">ID</target> 3115 <target state="new">ID</target>
3098 3116
3099 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit> 3117 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit>
3100 <trans-unit id="2265605798180116441" datatype="html"> 3118
3101 <source>Follower handle</source>
3102 <target state="new">Follower handle</target>
3103
3104 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
3105 <trans-unit id="5911214550882917183" datatype="html"> 3119 <trans-unit id="5911214550882917183" datatype="html">
3106 <source>State</source> 3120 <source>State</source>
3107 <target state="new">State</target> 3121 <target state="new">State</target>
@@ -3183,11 +3197,7 @@ The link will expire within 1 hour.</target>
3183 </target> 3197 </target>
3184 3198
3185 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit> 3199 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
3186 <trans-unit id="6641024648411549335" datatype="html"> 3200
3187 <source>Host</source>
3188 <target state="new">Host</target>
3189
3190 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3191 <trans-unit id="6571718060636962350" datatype="html"> 3201 <trans-unit id="6571718060636962350" datatype="html">
3192 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3202 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3193 <target state="new">Redundancy allowed 3203 <target state="new">Redundancy allowed
@@ -3198,7 +3208,7 @@ The link will expire within 1 hour.</target>
3198 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html"> 3208 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html">
3199 <source>Unfollow</source><target state="new">Unfollow</target> 3209 <source>Unfollow</source><target state="new">Unfollow</target>
3200 3210
3201 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3211 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3202 <trans-unit id="8246779176913476983" datatype="html"> 3212 <trans-unit id="8246779176913476983" datatype="html">
3203 <source>Open instance in a new tab</source> 3213 <source>Open instance in a new tab</source>
3204 <target state="new">Open instance in a new tab</target> 3214 <target state="new">Open instance in a new tab</target>
@@ -3210,12 +3220,12 @@ The link will expire within 1 hour.</target>
3210 <source>No host found matching current filters.</source> 3220 <source>No host found matching current filters.</source>
3211 <target state="new">No host found matching current filters.</target> 3221 <target state="new">No host found matching current filters.</target>
3212 3222
3213 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3223 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3214 <trans-unit id="7274241885665071790" datatype="html"> 3224 <trans-unit id="7274241885665071790" datatype="html">
3215 <source>Your instance is not following anyone.</source> 3225 <source>Your instance is not following anyone.</source>
3216 <target state="new">Your instance is not following anyone.</target> 3226 <target state="new">Your instance is not following anyone.</target>
3217 3227
3218 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3228 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3219 <trans-unit id="4774348799569692380" datatype="html"> 3229 <trans-unit id="4774348799569692380" datatype="html">
3220 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3230 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3221 <target state="new">Showing 3231 <target state="new">Showing
@@ -3225,14 +3235,7 @@ The link will expire within 1 hour.</target>
3225 </target> 3235 </target>
3226 3236
3227 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3237 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3228 <trans-unit id="6275803119759621687" datatype="html"> 3238 <trans-unit id="9216117865911519658" datatype="html">
3229 <source>Follow domains</source>
3230 <target state="new">Follow domains</target>
3231
3232 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit><trans-unit id="1268699198448750610" datatype="html">
3233 <source>Follow instances</source><target state="new">Follow instances</target>
3234
3235 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3236 <source>Action</source><target state="new">Action</target> 3239 <source>Action</source><target state="new">Action</target>
3237 3240
3238 3241
@@ -3282,7 +3285,7 @@ The link will expire within 1 hour.</target>
3282 3285
3283 3286
3284 3287
3285 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html"> 3288 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html">
3286 <source>e.g. jane_doe</source><target state="new">e.g. jane_doe</target> 3289 <source>e.g. jane_doe</source><target state="new">e.g. jane_doe</target>
3287 3290
3288 <note priority="1" from="description">Username choice placeholder in the registration form</note> 3291 <note priority="1" from="description">Username choice placeholder in the registration form</note>
@@ -3314,7 +3317,7 @@ The link will expire within 1 hour.</target>
3314 <target state="new">Role</target> 3317 <target state="new">Role</target>
3315 3318
3316 3319
3317 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group></trans-unit> 3320 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3318 <trans-unit id="7046347992315328430" datatype="html"> 3321 <trans-unit id="7046347992315328430" datatype="html">
3319 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3322 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3320 <target state="new"> 3323 <target state="new">
@@ -3337,15 +3340,9 @@ The link will expire within 1 hour.</target>
3337 3340
3338 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html"> 3341 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html">
3339 <source>Auth plugin</source><target state="new">Auth plugin</target> 3342 <source>Auth plugin</source><target state="new">Auth plugin</target>
3340 <context-group purpose="location"> 3343
3341 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3344
3342 <context context-type="linenumber">188</context> 3345 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit><trans-unit id="588099657508661970" datatype="html">
3343 </context-group>
3344 <context-group purpose="location">
3345 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3346 <context context-type="linenumber">188</context>
3347 </context-group>
3348 </trans-unit><trans-unit id="588099657508661970" datatype="html">
3349 <source>None (local authentication)</source><target state="new">None (local authentication)</target> 3346 <source>None (local authentication)</source><target state="new">None (local authentication)</target>
3350 <context-group purpose="location"> 3347 <context-group purpose="location">
3351 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3348 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
@@ -3572,7 +3569,13 @@ The link will expire within 1 hour.</target>
3572 3569
3573 3570
3574 3571
3575 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="4691552465058437520" datatype="html"> 3572 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3573 <source>Follower</source><target state="new">Follower</target>
3574 <context-group purpose="location">
3575 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3576 <context context-type="linenumber">24</context>
3577 </context-group>
3578 </trans-unit><trans-unit id="4691552465058437520" datatype="html">
3576 <source>Commented video</source><target state="new">Commented video</target> 3579 <source>Commented video</source><target state="new">Commented video</target>
3577 3580
3578 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html"> 3581 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html">
@@ -3896,7 +3899,7 @@ The link will expire within 1 hour.</target>
3896 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 3899 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
3897 </target> 3900 </target>
3898 3901
3899 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3902 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3900 <trans-unit id="4058814854824495833" datatype="html"> 3903 <trans-unit id="4058814854824495833" datatype="html">
3901 <source>Mute domains</source> 3904 <source>Mute domains</source>
3902 <target state="new">Mute domains</target> 3905 <target state="new">Mute domains</target>
@@ -5711,11 +5714,8 @@ color: red;
5711 5714
5712 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5715 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5713 <source>CHANNELS</source><target state="new">CHANNELS</target> 5716 <source>CHANNELS</source><target state="new">CHANNELS</target>
5714 <context-group purpose="location"> 5717
5715 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5718 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5716 <context context-type="linenumber">82</context>
5717 </context-group>
5718 </trans-unit>
5719 5719
5720 <trans-unit id="3666829335406793239" datatype="html"> 5720 <trans-unit id="3666829335406793239" datatype="html">
5721 <source>This account does not have channels.</source> 5721 <source>This account does not have channels.</source>
@@ -5753,7 +5753,13 @@ channel with the same name (<x id="PH_2"/>)!</source><target state="new">Do you
5753It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 5753It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
5754channel with the same name (<x id="PH_2"/>)!</target> 5754channel with the same name (<x id="PH_2"/>)!</target>
5755 5755
5756 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5756 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5757 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5758 <context-group purpose="location">
5759 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5760 <context context-type="linenumber">48</context>
5761 </context-group>
5762 </trans-unit>
5757 <trans-unit id="5387007581996837469" datatype="html"> 5763 <trans-unit id="5387007581996837469" datatype="html">
5758 <source>My Channels</source> 5764 <source>My Channels</source>
5759 <target state="new">My Channels</target> 5765 <target state="new">My Channels</target>
@@ -6365,12 +6371,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6365 <source>Your message has been sent.</source> 6371 <source>Your message has been sent.</source>
6366 <target state="new">Your message has been sent.</target> 6372 <target state="new">Your message has been sent.</target>
6367 6373
6368 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6374 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6369 <trans-unit id="2072135752262464360" datatype="html"> 6375 <trans-unit id="2072135752262464360" datatype="html">
6370 <source>You already sent this form recently</source> 6376 <source>You already sent this form recently</source>
6371 <target state="new">You already sent this form recently</target> 6377 <target state="new">You already sent this form recently</target>
6372 6378
6373 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6379 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6374 <trans-unit id="819067926858619041" datatype="html"> 6380 <trans-unit id="819067926858619041" datatype="html">
6375 <source>Account videos</source><target state="new">Account videos</target> 6381 <source>Account videos</source><target state="new">Account videos</target>
6376 6382
@@ -6405,10 +6411,10 @@ channel with the same name (<x id="PH_2"/>)!</target>
6405 <x id="PH"/> direct account followers 6411 <x id="PH"/> direct account followers
6406 </target> 6412 </target>
6407 6413
6408 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html"> 6414 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html">
6409 <source>Report this account</source><target state="new">Report this account</target> 6415 <source>Report this account</source><target state="new">Report this account</target>
6410 6416
6411 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6417 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6412 6418
6413 <trans-unit id="1504521795586863905" datatype="html"> 6419 <trans-unit id="1504521795586863905" datatype="html">
6414 <source>VIDEOS</source><target state="new">VIDEOS</target> 6420 <source>VIDEOS</source><target state="new">VIDEOS</target>
@@ -6420,24 +6426,16 @@ channel with the same name (<x id="PH_2"/>)!</target>
6420 <target state="new">Username copied</target> 6426 <target state="new">Username copied</target>
6421 6427
6422 6428
6423 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html"> 6429 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html">
6424 <source>1 subscriber</source><target state="new">1 subscriber</target> 6430 <source>1 subscriber</source><target state="new">1 subscriber</target>
6425 6431
6426 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html"> 6432 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html">
6427 <source><x id="PH"/> subscribers</source><target state="new"><x id="PH"/> subscribers</target> 6433 <source><x id="PH"/> subscribers</source><target state="new"><x id="PH"/> subscribers</target>
6428 6434
6429 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6435 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6436
6437
6430 6438
6431 <trans-unit id="4682675125751819107" datatype="html">
6432 <source>Instances you follow</source>
6433 <target state="new">Instances you follow</target>
6434
6435 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6436 <trans-unit id="8899833753704589712" datatype="html">
6437 <source>Instances following you</source>
6438 <target state="new">Instances following you</target>
6439
6440 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6441 <trans-unit id="1035838766454786107" datatype="html"> 6439 <trans-unit id="1035838766454786107" datatype="html">
6442 <source>Audio-only</source> 6440 <source>Audio-only</source>
6443 <target state="new">Audio-only</target> 6441 <target state="new">Audio-only</target>
@@ -6485,7 +6483,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6485 <source>Auto (via ffmpeg)</source> 6483 <source>Auto (via ffmpeg)</source>
6486 <target state="new">Auto (via ffmpeg)</target> 6484 <target state="new">Auto (via ffmpeg)</target>
6487 6485
6488 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="931255636742351800" datatype="html"> 6486 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="3642770981085338761" datatype="html">
6487 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6488 <context-group purpose="location">
6489 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6490 <context context-type="linenumber">3</context>
6491 </context-group>
6492 </trans-unit><trans-unit id="931255636742351800" datatype="html">
6489 <source>No limit</source><target state="new">No limit</target> 6493 <source>No limit</source><target state="new">No limit</target>
6490 6494
6491 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html"> 6495 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html">
@@ -6604,17 +6608,33 @@ channel with the same name (<x id="PH_2"/>)!</target>
6604 <source>Domain is required.</source> 6608 <source>Domain is required.</source>
6605 <target state="new">Domain is required.</target> 6609 <target state="new">Domain is required.</target>
6606 6610
6607 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group></trans-unit> 6611 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6608 <trans-unit id="6780793142903080663" datatype="html"> 6612 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6609 <source>Domains entered are invalid.</source> 6613 <context-group purpose="location">
6610 <target state="new">Domains entered are invalid.</target> 6614 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6611 6615 <context context-type="linenumber">93</context>
6612 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6616 </context-group>
6613 <trans-unit id="5886492514458202177" datatype="html"> 6617 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6614 <source>Domains entered contain duplicates.</source> 6618 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6615 <target state="new">Domains entered contain duplicates.</target> 6619 <context-group purpose="location">
6616 6620 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6617 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 6621 <context context-type="linenumber">94</context>
6622 </context-group>
6623 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6624 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6625 <context-group purpose="location">
6626 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6627 <context context-type="linenumber">102</context>
6628 </context-group>
6629 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6630 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6631 <context-group purpose="location">
6632 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6633 <context context-type="linenumber">103</context>
6634 </context-group>
6635 </trans-unit>
6636
6637
6618 <trans-unit id="240806681889331244" datatype="html"> 6638 <trans-unit id="240806681889331244" datatype="html">
6619 <source>Unlimited</source> 6639 <source>Unlimited</source>
6620 <target state="new">Unlimited</target> 6640 <target state="new">Unlimited</target>
@@ -6745,7 +6765,27 @@ channel with the same name (<x id="PH_2"/>)!</target>
6745 <x id="PH"/> removed from instance followers 6765 <x id="PH"/> removed from instance followers
6746 </target> 6766 </target>
6747 6767
6748 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit> 6768 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit><trans-unit id="6018246591673612412" datatype="html">
6769 <source>Follow</source><target state="new">Follow</target>
6770 <context-group purpose="location">
6771 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6772 <context context-type="linenumber">3</context>
6773 </context-group>
6774 <context-group purpose="location">
6775 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6776 <context context-type="linenumber">37</context>
6777 </context-group>
6778 <context-group purpose="location">
6779 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6780 <context context-type="linenumber">18</context>
6781 </context-group>
6782 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6783 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6784 <context-group purpose="location">
6785 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6786 <context context-type="linenumber">11</context>
6787 </context-group>
6788 </trans-unit>
6749 <trans-unit id="2740793005745065895" datatype="html"> 6789 <trans-unit id="2740793005745065895" datatype="html">
6750 <source> 6790 <source>
6751 <x id="PH"/> is not valid 6791 <x id="PH"/> is not valid
@@ -6754,19 +6794,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
6754 <x id="PH"/> is not valid 6794 <x id="PH"/> is not valid
6755 </target> 6795 </target>
6756 6796
6757 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group></trans-unit> 6797 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6758 <trans-unit id="2355066641781598196" datatype="html"> 6798 <trans-unit id="2355066641781598196" datatype="html">
6759 <source>Follow request(s) sent!</source> 6799 <source>Follow request(s) sent!</source>
6760 <target state="new">Follow request(s) sent!</target> 6800 <target state="new">Follow request(s) sent!</target>
6761 6801
6762 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit> 6802 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6803 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6804 <context-group purpose="location">
6805 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6806 <context context-type="linenumber">3</context>
6807 </context-group>
6808 </trans-unit>
6763 <trans-unit id="4245720728052819482" datatype="html"> 6809 <trans-unit id="4245720728052819482" datatype="html">
6764 <source>Do you really want to unfollow <x id="PH"/>?</source> 6810 <source>Do you really want to unfollow <x id="PH"/>?</source>
6765 <target state="new">Do you really want to unfollow 6811 <target state="new">Do you really want to unfollow
6766 <x id="PH"/>? 6812 <x id="PH"/>?
6767 </target> 6813 </target>
6768 6814
6769 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6815 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6770 <trans-unit id="9160510009013134726" datatype="html"> 6816 <trans-unit id="9160510009013134726" datatype="html">
6771 <source>Unfollow</source> 6817 <source>Unfollow</source>
6772 <target state="new">Unfollow</target> 6818 <target state="new">Unfollow</target>
@@ -6778,7 +6824,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
6778 <x id="PH"/> anymore. 6824 <x id="PH"/> anymore.
6779 </target> 6825 </target>
6780 6826
6781 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 6827 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6782 <trans-unit id="2593763089859685916" datatype="html"> 6828 <trans-unit id="2593763089859685916" datatype="html">
6783 <source>enabled</source> 6829 <source>enabled</source>
6784 <target state="new">enabled</target> 6830 <target state="new">enabled</target>
@@ -7236,7 +7282,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7236 7282
7237 7283
7238 7284
7239 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit> 7285 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7240 <trans-unit id="5076187961693950167" datatype="html"> 7286 <trans-unit id="5076187961693950167" datatype="html">
7241 <source>Standard logs</source> 7287 <source>Standard logs</source>
7242 <target state="new">Standard logs</target> 7288 <target state="new">Standard logs</target>
@@ -7274,13 +7320,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7274 <source>Update user password</source> 7320 <source>Update user password</source>
7275 <target state="new">Update user password</target> 7321 <target state="new">Update user password</target>
7276 7322
7277 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit><trans-unit id="177544274549739411" datatype="html"> 7323 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit>
7278 <source>Following list</source><target state="new">Following list</target>
7279
7280 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group></trans-unit><trans-unit id="8092429110007204784" datatype="html">
7281 <source>Followers list</source><target state="new">Followers list</target>
7282
7283 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group></trans-unit>
7284 <trans-unit id="780323526182667308" datatype="html"> 7324 <trans-unit id="780323526182667308" datatype="html">
7285 <source>User <x id="PH"/> updated.</source> 7325 <source>User <x id="PH"/> updated.</source>
7286 <target state="new">User 7326 <target state="new">User
@@ -7311,13 +7351,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7311 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html"> 7351 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html">
7312 <source>Federation</source><target state="new">Federation</target> 7352 <source>Federation</source><target state="new">Federation</target>
7313 7353
7314 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="4682675125751819107" datatype="html"> 7354 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit>
7315 <source>Instances you follow</source><target state="new">Instances you follow</target>
7316
7317 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group></trans-unit><trans-unit id="8899833753704589712" datatype="html">
7318 <source>Instances following you</source><target state="new">Instances following you</target>
7319
7320 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit>
7321 <trans-unit id="3767259920053407667" datatype="html"> 7355 <trans-unit id="3767259920053407667" datatype="html">
7322 <source>Videos will be deleted, comments will be tombstoned.</source> 7356 <source>Videos will be deleted, comments will be tombstoned.</source>
7323 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7357 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7345,7 +7379,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7345 <target state="new">Set Email as Verified</target> 7379 <target state="new">Set Email as Verified</target>
7346 7380
7347 7381
7348 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7382 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7383 <source>Created</source><target state="new">Created</target>
7384 <context-group purpose="location">
7385 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7386 <context context-type="linenumber">115</context>
7387 </context-group>
7388 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7389 <source>Daily quota</source><target state="new">Daily quota</target>
7390 <context-group purpose="location">
7391 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7392 <context context-type="linenumber">120</context>
7393 </context-group>
7394 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7395 <source>Last login</source><target state="new">Last login</target>
7396 <context-group purpose="location">
7397 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7398 <context context-type="linenumber">122</context>
7399 </context-group>
7400 </trans-unit>
7349 <trans-unit id="3403978719736970622" datatype="html"> 7401 <trans-unit id="3403978719736970622" datatype="html">
7350 <source>You cannot ban root.</source> 7402 <source>You cannot ban root.</source>
7351 <target state="new">You cannot ban root.</target> 7403 <target state="new">You cannot ban root.</target>
@@ -7650,12 +7702,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
7650 <x id="PH"/> created. 7702 <x id="PH"/> created.
7651 </target> 7703 </target>
7652 7704
7653 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7705 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7654 <trans-unit id="8723777130353305761" datatype="html"> 7706 <trans-unit id="8723777130353305761" datatype="html">
7655 <source>This name already exists on this instance.</source> 7707 <source>This name already exists on this instance.</source>
7656 <target state="new">This name already exists on this instance.</target> 7708 <target state="new">This name already exists on this instance.</target>
7657 7709
7658 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7710 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7659 <trans-unit id="7589345916094713536" datatype="html"> 7711 <trans-unit id="7589345916094713536" datatype="html">
7660 <source>Video channel <x id="PH"/> updated.</source> 7712 <source>Video channel <x id="PH"/> updated.</source>
7661 <target state="new">Video channel 7713 <target state="new">Video channel
@@ -7672,13 +7724,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7672 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 7724 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7673 7725
7674 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7726 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7675 <trans-unit id="2575302837003821736" datatype="html"> 7727
7676 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7677 <target state="new">Please type the display name of the video channel (
7678 <x id="PH"/>) to confirm
7679 </target>
7680
7681 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7682 <trans-unit id="624066830180032195" datatype="html"> 7728 <trans-unit id="624066830180032195" datatype="html">
7683 <source>Video channel <x id="PH"/> deleted.</source> 7729 <source>Video channel <x id="PH"/> deleted.</source>
7684 <target state="new">Video channel 7730 <target state="new">Video channel
@@ -7821,7 +7867,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
7821 <source>Ownership change request sent.</source> 7867 <source>Ownership change request sent.</source>
7822 <target state="new">Ownership change request sent.</target> 7868 <target state="new">Ownership change request sent.</target>
7823 7869
7824 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 7870 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7699622144571229146" datatype="html">
7871 <source>Sort by</source><target state="new">Sort by</target>
7872 <context-group purpose="location">
7873 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7874 <context context-type="linenumber">26</context>
7875 </context-group>
7876 </trans-unit>
7825 <trans-unit id="3245220240937722814" datatype="html"> 7877 <trans-unit id="3245220240937722814" datatype="html">
7826 <source>My channels</source> 7878 <source>My channels</source>
7827 <target state="new">My channels</target> 7879 <target state="new">My channels</target>
@@ -7916,7 +7968,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7916 <target state="new">Subscribe to the account</target> 7968 <target state="new">Subscribe to the account</target>
7917 7969
7918 7970
7919 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 7971 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
7920 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 7972 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
7921 <context-group purpose="location"> 7973 <context-group purpose="location">
7922 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 7974 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -7963,34 +8015,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
7963 <source>Go to my subscriptions</source> 8015 <source>Go to my subscriptions</source>
7964 <target state="new">Go to my subscriptions</target> 8016 <target state="new">Go to my subscriptions</target>
7965 8017
7966 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 8018 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
7967 <trans-unit id="1136469849928650779" datatype="html"> 8019 <trans-unit id="1136469849928650779" datatype="html">
7968 <source>Go to my videos</source> 8020 <source>Go to my videos</source>
7969 <target state="new">Go to my videos</target> 8021 <target state="new">Go to my videos</target>
7970 8022
7971 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit> 8023 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
7972 <trans-unit id="7836683738999600376" datatype="html"> 8024 <trans-unit id="7836683738999600376" datatype="html">
7973 <source>Go to my imports</source> 8025 <source>Go to my imports</source>
7974 <target state="new">Go to my imports</target> 8026 <target state="new">Go to my imports</target>
7975 8027
7976 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 8028 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
7977 <trans-unit id="7511292153332773503" datatype="html"> 8029 <trans-unit id="7511292153332773503" datatype="html">
7978 <source>Go to my channels</source> 8030 <source>Go to my channels</source>
7979 <target state="new">Go to my channels</target> 8031 <target state="new">Go to my channels</target>
7980 8032
7981 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html"> 8033 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html">
7982 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8034 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7983Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8035Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7984Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8036Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
7985 8037
7986 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group></trans-unit> 8038 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
7987 8039
7988 8040
7989 <trans-unit id="375263728166936544" datatype="html"> 8041 <trans-unit id="375263728166936544" datatype="html">
7990 <source>You need to reconnect.</source> 8042 <source>You need to reconnect.</source>
7991 <target state="new">You need to reconnect.</target> 8043 <target state="new">You need to reconnect.</target>
7992 8044
7993 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group></trans-unit> 8045 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
7994 <trans-unit id="2206638022166154361" datatype="html"> 8046 <trans-unit id="2206638022166154361" datatype="html">
7995 <source>Keyboard Shortcuts:</source> 8047 <source>Keyboard Shortcuts:</source>
7996 <target state="new">Keyboard Shortcuts:</target> 8048 <target state="new">Keyboard Shortcuts:</target>
@@ -8001,6 +8053,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8001 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8053 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8002 <context context-type="linenumber">98</context> 8054 <context context-type="linenumber">98</context>
8003 </context-group> 8055 </context-group>
8056 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8057 <source>In my library</source><target state="new">In my library</target>
8058 <context-group purpose="location">
8059 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8060 <context context-type="linenumber">104</context>
8061 </context-group>
8004 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 8062 </trans-unit><trans-unit id="232050922346936574" datatype="html">
8005 <source>Trending</source><target state="new">Trending</target> 8063 <source>Trending</source><target state="new">Trending</target>
8006 8064
@@ -8024,38 +8082,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8024 <source>Incorrect username or password.</source> 8082 <source>Incorrect username or password.</source>
8025 <target state="new">Incorrect username or password.</target> 8083 <target state="new">Incorrect username or password.</target>
8026 8084
8027 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 8085 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8028 <trans-unit id="6974874606619467663" datatype="html"> 8086 <trans-unit id="6974874606619467663" datatype="html">
8029 <source>Your account is blocked.</source> 8087 <source>Your account is blocked.</source>
8030 <target state="new">Your account is blocked.</target> 8088 <target state="new">Your account is blocked.</target>
8031 8089
8032 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 8090 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8033 8091
8034 <trans-unit id="7939914198003891823" datatype="html"> 8092 <trans-unit id="7939914198003891823" datatype="html">
8035 <source>any language</source> 8093 <source>any language</source>
8036 <target state="new">any language</target> 8094 <target state="new">any language</target>
8037 8095
8038 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8096 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8039 <trans-unit id="5633144232269377096" datatype="html"> 8097 <trans-unit id="5633144232269377096" datatype="html">
8040 <source>hide</source> 8098 <source>hide</source>
8041 <target state="new">hide</target> 8099 <target state="new">hide</target>
8042 8100
8043 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8101 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8044 <trans-unit id="8603861867909474404" datatype="html"> 8102 <trans-unit id="8603861867909474404" datatype="html">
8045 <source>blur</source> 8103 <source>blur</source>
8046 <target state="new">blur</target> 8104 <target state="new">blur</target>
8047 8105
8048 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8106 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8049 <trans-unit id="4534458451100881847" datatype="html"> 8107 <trans-unit id="4534458451100881847" datatype="html">
8050 <source>display</source> 8108 <source>display</source>
8051 <target state="new">display</target> 8109 <target state="new">display</target>
8052 8110
8053 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8111 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8054 <trans-unit id="4467323362722952678" datatype="html"> 8112 <trans-unit id="4467323362722952678" datatype="html">
8055 <source>Unknown</source> 8113 <source>Unknown</source>
8056 <target state="new">Unknown</target> 8114 <target state="new">Unknown</target>
8057 8115
8058 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8116 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8059 <trans-unit id="8781423666414310853" datatype="html"> 8117 <trans-unit id="8781423666414310853" datatype="html">
8060 <source>Your password has been successfully reset!</source> 8118 <source>Your password has been successfully reset!</source>
8061 <target state="new">Your password has been successfully reset!</target> 8119 <target state="new">Your password has been successfully reset!</target>
@@ -9598,17 +9656,17 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9598 <x id="PH"/> minutes. 9656 <x id="PH"/> minutes.
9599 </target> 9657 </target>
9600 9658
9601 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 9659 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9602 <trans-unit id="4965472196059235310" datatype="html"> 9660 <trans-unit id="4965472196059235310" datatype="html">
9603 <source>Too many attempts, please try again later.</source> 9661 <source>Too many attempts, please try again later.</source>
9604 <target state="new">Too many attempts, please try again later.</target> 9662 <target state="new">Too many attempts, please try again later.</target>
9605 9663
9606 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group></trans-unit> 9664 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9607 <trans-unit id="1693549688987384699" datatype="html"> 9665 <trans-unit id="1693549688987384699" datatype="html">
9608 <source>Server error. Please retry later.</source> 9666 <source>Server error. Please retry later.</source>
9609 <target state="new">Server error. Please retry later.</target> 9667 <target state="new">Server error. Please retry later.</target>
9610 9668
9611 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 9669 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9612 <trans-unit id="5927402622550505067" datatype="html"> 9670 <trans-unit id="5927402622550505067" datatype="html">
9613 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9671 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9614 <target state="new">Subscribed to all current channels of 9672 <target state="new">Subscribed to all current channels of
@@ -10111,20 +10169,20 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10111 <source>Your video was uploaded to your account and is private.</source> 10169 <source>Your video was uploaded to your account and is private.</source>
10112 <target state="new">Your video was uploaded to your account and is private.</target> 10170 <target state="new">Your video was uploaded to your account and is private.</target>
10113 10171
10114 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10172 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10115 <trans-unit id="5699822024600815733" datatype="html"> 10173 <trans-unit id="5699822024600815733" datatype="html">
10116 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10174 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10117 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target> 10175 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target>
10118 10176
10119 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10177 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10120 <trans-unit id="1219739004043110649" datatype="html"> 10178 <trans-unit id="1219739004043110649" datatype="html">
10121 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10179 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10122 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target> 10180 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target>
10123 10181
10124 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html"> 10182 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html">
10125 <source>Upload</source><target state="new">Upload</target> 10183 <source>Upload</source><target state="new">Upload</target>
10126 10184
10127 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10185 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10128 <trans-unit id="8278735427925094503" datatype="html"> 10186 <trans-unit id="8278735427925094503" datatype="html">
10129 <source>Upload 10187 <source>Upload
10130 <x id="PH"/> 10188 <x id="PH"/>
@@ -10133,13 +10191,13 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10133 <x id="PH"/> 10191 <x id="PH"/>
10134 </target> 10192 </target>
10135 10193
10136 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10194 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10137 10195
10138 <trans-unit id="5981816353437801748" datatype="html"> 10196 <trans-unit id="5981816353437801748" datatype="html">
10139 <source>Video published.</source> 10197 <source>Video published.</source>
10140 <target state="new">Video published.</target> 10198 <target state="new">Video published.</target>
10141 10199
10142 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10200 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10143 10201
10144 10202
10145 <trans-unit id="764164089183618119" datatype="html"> 10203 <trans-unit id="764164089183618119" datatype="html">
@@ -10203,26 +10261,26 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10203 <trans-unit id="961774488937452220" datatype="html"> 10261 <trans-unit id="961774488937452220" datatype="html">
10204 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10262 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10205 10263
10206 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html"> 10264 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html">
10207 <source>Redirection</source><target state="new">Redirection</target> 10265 <source>Redirection</source><target state="new">Redirection</target>
10208 10266
10209 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10267 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10210 10268
10211 <trans-unit id="8858527736400081688" datatype="html"> 10269 <trans-unit id="8858527736400081688" datatype="html">
10212 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10270 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10213 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target> 10271 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target>
10214 10272
10215 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10273 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10216 <trans-unit id="3937119019020041049" datatype="html"> 10274 <trans-unit id="3937119019020041049" datatype="html">
10217 <source>Mature or explicit content</source> 10275 <source>Mature or explicit content</source>
10218 <target state="new">Mature or explicit content</target> 10276 <target state="new">Mature or explicit content</target>
10219 10277
10220 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10278 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10221 <trans-unit id="1755474755114288376" datatype="html"> 10279 <trans-unit id="1755474755114288376" datatype="html">
10222 <source>Up Next</source> 10280 <source>Up Next</source>
10223 <target state="new">Up Next</target> 10281 <target state="new">Up Next</target>
10224 10282
10225 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html"> 10283 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html">
10226 <source>Cancel</source><target state="new">Cancel</target> 10284 <source>Cancel</source><target state="new">Cancel</target>
10227 10285
10228 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit> 10286 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit>
@@ -10230,62 +10288,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10230 <source>Autoplay is suspended</source> 10288 <source>Autoplay is suspended</source>
10231 <target state="new">Autoplay is suspended</target> 10289 <target state="new">Autoplay is suspended</target>
10232 10290
10233 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10291 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10234 <trans-unit id="7895294730547405228" datatype="html"> 10292 <trans-unit id="7895294730547405228" datatype="html">
10235 <source>Enter/exit fullscreen (requires player focus)</source> 10293 <source>Enter/exit fullscreen (requires player focus)</source>
10236 <target state="new">Enter/exit fullscreen (requires player focus)</target> 10294 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10237 10295
10238 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10296 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10239 <trans-unit id="7618388257165864759" datatype="html"> 10297 <trans-unit id="7618388257165864759" datatype="html">
10240 <source>Play/Pause the video (requires player focus)</source> 10298 <source>Play/Pause the video (requires player focus)</source>
10241 <target state="new">Play/Pause the video (requires player focus)</target> 10299 <target state="new">Play/Pause the video (requires player focus)</target>
10242 10300
10243 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10301 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10244 <trans-unit id="7761890399634216630" datatype="html"> 10302 <trans-unit id="7761890399634216630" datatype="html">
10245 <source>Mute/unmute the video (requires player focus)</source> 10303 <source>Mute/unmute the video (requires player focus)</source>
10246 <target state="new">Mute/unmute the video (requires player focus)</target> 10304 <target state="new">Mute/unmute the video (requires player focus)</target>
10247 10305
10248 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10306 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10249 <trans-unit id="5996585232248234904" datatype="html"> 10307 <trans-unit id="5996585232248234904" datatype="html">
10250 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10308 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10251 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10309 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10252 10310
10253 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10311 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10254 <trans-unit id="3748765405903319998" datatype="html"> 10312 <trans-unit id="3748765405903319998" datatype="html">
10255 <source>Increase the volume (requires player focus)</source> 10313 <source>Increase the volume (requires player focus)</source>
10256 <target state="new">Increase the volume (requires player focus)</target> 10314 <target state="new">Increase the volume (requires player focus)</target>
10257 10315
10258 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10316 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10259 <trans-unit id="5810704036407159982" datatype="html"> 10317 <trans-unit id="5810704036407159982" datatype="html">
10260 <source>Decrease the volume (requires player focus)</source> 10318 <source>Decrease the volume (requires player focus)</source>
10261 <target state="new">Decrease the volume (requires player focus)</target> 10319 <target state="new">Decrease the volume (requires player focus)</target>
10262 10320
10263 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10321 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10264 <trans-unit id="2622048822548065691" datatype="html"> 10322 <trans-unit id="2622048822548065691" datatype="html">
10265 <source>Seek the video forward (requires player focus)</source> 10323 <source>Seek the video forward (requires player focus)</source>
10266 <target state="new">Seek the video forward (requires player focus)</target> 10324 <target state="new">Seek the video forward (requires player focus)</target>
10267 10325
10268 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10326 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10269 <trans-unit id="6540078205109221153" datatype="html"> 10327 <trans-unit id="6540078205109221153" datatype="html">
10270 <source>Seek the video backward (requires player focus)</source> 10328 <source>Seek the video backward (requires player focus)</source>
10271 <target state="new">Seek the video backward (requires player focus)</target> 10329 <target state="new">Seek the video backward (requires player focus)</target>
10272 10330
10273 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10331 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10274 <trans-unit id="1956491957766210808" datatype="html"> 10332 <trans-unit id="1956491957766210808" datatype="html">
10275 <source>Increase playback rate (requires player focus)</source> 10333 <source>Increase playback rate (requires player focus)</source>
10276 <target state="new">Increase playback rate (requires player focus)</target> 10334 <target state="new">Increase playback rate (requires player focus)</target>
10277 10335
10278 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10336 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10279 <trans-unit id="5495529997674803186" datatype="html"> 10337 <trans-unit id="5495529997674803186" datatype="html">
10280 <source>Decrease playback rate (requires player focus)</source> 10338 <source>Decrease playback rate (requires player focus)</source>
10281 <target state="new">Decrease playback rate (requires player focus)</target> 10339 <target state="new">Decrease playback rate (requires player focus)</target>
10282 10340
10283 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10341 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10284 <trans-unit id="3178343147230721210" datatype="html"> 10342 <trans-unit id="3178343147230721210" datatype="html">
10285 <source>Navigate in the video frame by frame (requires player focus)</source> 10343 <source>Navigate in the video frame by frame (requires player focus)</source>
10286 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 10344 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10287 10345
10288 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10346 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10289 <trans-unit id="8025996572234182184" datatype="html"> 10347 <trans-unit id="8025996572234182184" datatype="html">
10290 <source>Like the video</source> 10348 <source>Like the video</source>
10291 <target state="new">Like the video</target> 10349 <target state="new">Like the video</target>
diff --git a/client/src/locale/angular.sl-SI.xlf b/client/src/locale/angular.sl-SI.xlf
index 582be2864..20cab0808 100644
--- a/client/src/locale/angular.sl-SI.xlf
+++ b/client/src/locale/angular.sl-SI.xlf
@@ -280,7 +280,7 @@
280 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 280 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
281 </target> 281 </target>
282 282
283 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 283 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
284 <trans-unit id="1486537403020619891" datatype="html"> 284 <trans-unit id="1486537403020619891" datatype="html">
285 <source>My watch history</source> 285 <source>My watch history</source>
286 <target state="new">My watch history</target> 286 <target state="new">My watch history</target>
@@ -362,12 +362,12 @@
362 362
363 363
364 364
365 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 365 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
366 <trans-unit id="1006562256968398209" datatype="html"> 366 <trans-unit id="1006562256968398209" datatype="html">
367 <source>video</source> 367 <source>video</source>
368 <target state="new">video</target> 368 <target state="new">video</target>
369 369
370 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 370 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
371 <trans-unit id="6438815964972582865" datatype="html"> 371 <trans-unit id="6438815964972582865" datatype="html">
372 <source>The following link contains a private token and should not be shared with anyone.</source> 372 <source>The following link contains a private token and should not be shared with anyone.</source>
373 <target state="new"> The following link contains a private token and should not be shared with anyone. </target> 373 <target state="new"> The following link contains a private token and should not be shared with anyone. </target>
@@ -438,10 +438,10 @@
438 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 438 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
439 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 439 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
440 440
441 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 441 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
442 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 442 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
443 443
444 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 444 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
445 <trans-unit id="5235042777215655908" datatype="html"> 445 <trans-unit id="5235042777215655908" datatype="html">
446 <source>subtitles</source> 446 <source>subtitles</source>
447 <target state="new">subtitles</target> 447 <target state="new">subtitles</target>
@@ -857,10 +857,10 @@
857 <trans-unit id="2602586221576511475" datatype="html"> 857 <trans-unit id="2602586221576511475" datatype="html">
858 <source>Video quota</source> 858 <source>Video quota</source>
859 <target state="new">Video quota</target> 859 <target state="new">Video quota</target>
860 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 860
861 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 861
862 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 862
863 </trans-unit> 863 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
864 <trans-unit id="1502595455339510144" datatype="html"> 864 <trans-unit id="1502595455339510144" datatype="html">
865 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 865 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
866 <target state="new"> 866 <target state="new">
@@ -944,7 +944,31 @@
944 <source>Federation</source> 944 <source>Federation</source>
945 <target state="new">Federation</target> 945 <target state="new">Federation</target>
946 946
947 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 947 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
948 <source>Following</source><target state="new">Following</target>
949 <context-group purpose="location">
950 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
951 <context context-type="linenumber">29</context>
952 </context-group>
953 <context-group purpose="location">
954 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
955 <context context-type="linenumber">31</context>
956 </context-group>
957 <context-group purpose="location">
958 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
959 <context context-type="linenumber">28</context>
960 </context-group>
961 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
962 <source>Followers</source><target state="new">Followers</target>
963 <context-group purpose="location">
964 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
965 <context context-type="linenumber">34</context>
966 </context-group>
967 <context-group purpose="location">
968 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
969 <context context-type="linenumber">37</context>
970 </context-group>
971 </trans-unit>
948 <trans-unit id="3541687134897970106" datatype="html"> 972 <trans-unit id="3541687134897970106" datatype="html">
949 <source>followers</source> 973 <source>followers</source>
950 <target state="new">followers</target> 974 <target state="new">followers</target>
@@ -1018,7 +1042,7 @@
1018 1042
1019 1043
1020 1044
1021 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 1045 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
1022 <trans-unit id="3616223838716839702" datatype="html"> 1046 <trans-unit id="3616223838716839702" datatype="html">
1023 <source>Ban this user</source> 1047 <source>Ban this user</source>
1024 <target state="new">Ban this user</target> 1048 <target state="new">Ban this user</target>
@@ -1091,19 +1115,13 @@
1091 <trans-unit id="7252854992688790751" datatype="html"> 1115 <trans-unit id="7252854992688790751" datatype="html">
1092 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1116 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1093 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1117 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1094 <context-group purpose="location"> 1118
1095 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1119 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1096 <context context-type="linenumber">60,62</context>
1097 </context-group>
1098 </trans-unit>
1099 <trans-unit id="7215649348148521605" datatype="html"> 1120 <trans-unit id="7215649348148521605" datatype="html">
1100 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1121 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1101 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1122 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1102 <context-group purpose="location"> 1123
1103 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1124 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1104 <context context-type="linenumber">65,67</context>
1105 </context-group>
1106 </trans-unit>
1107 <trans-unit id="2392488717875840729" datatype="html"> 1125 <trans-unit id="2392488717875840729" datatype="html">
1108 <source>User</source> 1126 <source>User</source>
1109 <target state="new">User</target> 1127 <target state="new">User</target>
@@ -1114,68 +1132,68 @@
1114 <source>Username or email address</source> 1132 <source>Username or email address</source>
1115 <target>Uporabniško ime ali e-poštni naslov</target> 1133 <target>Uporabniško ime ali e-poštni naslov</target>
1116 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1134 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1135 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1136 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1137 <context-group purpose="location">
1138 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1139 <context context-type="linenumber">33,34</context>
1140 </context-group>
1117 </trans-unit> 1141 </trans-unit>
1118 <trans-unit id="1431416938026210429"> 1142 <trans-unit id="1431416938026210429">
1119 <source>Password</source> 1143 <source>Password</source>
1120 <target>Geslo</target> 1144 <target>Geslo</target>
1121 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1145
1122 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1146
1123 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1147
1124 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1148
1125 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1149
1126 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1150
1127 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1151
1128 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1152
1129 </trans-unit> 1153 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1130 <trans-unit id="8715156686857791956" datatype="html"> 1154 <trans-unit id="8715156686857791956" datatype="html">
1131 <source>Click here to reset your password</source> 1155 <source>Click here to reset your password</source>
1132 <target state="new">Click here to reset your password</target> 1156 <target state="new">Click here to reset your password</target>
1133 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1157
1134 </trans-unit> 1158 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1135 <trans-unit id="892063502898494584" datatype="html"> 1159 <trans-unit id="892063502898494584" datatype="html">
1136 <source>I forgot my password</source> 1160 <source>I forgot my password</source>
1137 <target state="new">I forgot my password</target> 1161 <target state="new">I forgot my password</target>
1138 <context-group purpose="location"> 1162
1139 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1163 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1140 <context context-type="linenumber">47</context>
1141 </context-group>
1142 </trans-unit>
1143 <trans-unit id="2101170466365500913" datatype="html"> 1164 <trans-unit id="2101170466365500913" datatype="html">
1144 <source>Logging into an account lets you publish content</source> 1165 <source>Logging into an account lets you publish content</source>
1145 <target state="new"> Logging into an account lets you publish content </target> 1166 <target state="new"> Logging into an account lets you publish content </target>
1146 <context-group purpose="location"> 1167
1147 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1168 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1148 <context context-type="linenumber">56,57</context>
1149 </context-group>
1150 </trans-unit>
1151 <trans-unit id="2454050363478003966"> 1169 <trans-unit id="2454050363478003966">
1152 <source>Login</source> 1170 <source>Login</source>
1153 <target>Prijava</target> 1171 <target>Prijava</target>
1154 1172
1155 1173
1156 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1174 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1157 <trans-unit id="3183213940445113677" datatype="html"> 1175 <trans-unit id="3183213940445113677" datatype="html">
1158 <source>Or sign in with</source> 1176 <source>Or sign in with</source>
1159 <target state="new">Or sign in with</target> 1177 <target state="new">Or sign in with</target>
1160 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1178
1161 </trans-unit> 1179 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1162 <trans-unit id="3238209155172574367"> 1180 <trans-unit id="3238209155172574367">
1163 <source>Forgot your password</source> 1181 <source>Forgot your password</source>
1164 <target>Ste pozabili geslo?</target> 1182 <target>Ste pozabili geslo?</target>
1165 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1183
1166 </trans-unit> 1184 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1167 <trans-unit id="87327320394367488" datatype="html"> 1185 <trans-unit id="87327320394367488" datatype="html">
1168 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1186 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1169 <target state="new"> 1187 <target state="new">
1170 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1188 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
1171 </target> 1189 </target>
1172 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1190
1173 </trans-unit> 1191 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1174 <trans-unit id="3188014010833256853" datatype="html"> 1192 <trans-unit id="3188014010833256853" datatype="html">
1175 <source>Enter your email address and we will send you a link to reset your password.</source> 1193 <source>Enter your email address and we will send you a link to reset your password.</source>
1176 <target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1194 <target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1177 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1195
1178 </trans-unit> 1196 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1179 <trans-unit id="1190256911880544559" datatype="html"> 1197 <trans-unit id="1190256911880544559" datatype="html">
1180 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1198 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1181The link will expire within 1 hour.</source> 1199The link will expire within 1 hour.</source>
@@ -1186,26 +1204,26 @@ The link will expire within 1 hour.</target>
1186 <trans-unit id="4768749765465246664"> 1204 <trans-unit id="4768749765465246664">
1187 <source>Email</source> 1205 <source>Email</source>
1188 <target>E-poštni naslov</target> 1206 <target>E-poštni naslov</target>
1189 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1207
1190 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1208
1191 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1209
1192 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1210
1193 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1211
1194 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1212
1195 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1213
1196 </trans-unit> 1214 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1197 <trans-unit id="3967269098753656610"> 1215 <trans-unit id="3967269098753656610">
1198 <source>Email address</source> 1216 <source>Email address</source>
1199 <target>E-poštni naslov</target> 1217 <target>E-poštni naslov</target>
1200 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1218
1201 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1219
1202 </trans-unit> 1220 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1203 <trans-unit id="7808756054397155068" datatype="html"> 1221 <trans-unit id="7808756054397155068" datatype="html">
1204 <source>Reset</source> 1222 <source>Reset</source>
1205 <target state="new">Reset</target> 1223 <target state="new">Reset</target>
1206 <note priority="1" from="description">Password reset button</note> 1224 <note priority="1" from="description">Password reset button</note>
1207 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1225
1208 </trans-unit> 1226 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1209 <trans-unit id="4319634264526091601" datatype="html"> 1227 <trans-unit id="4319634264526091601" datatype="html">
1210 <source>on this instance</source> 1228 <source>on this instance</source>
1211 <target state="new">on this instance</target> 1229 <target state="new">on this instance</target>
@@ -1579,7 +1597,7 @@ The link will expire within 1 hour.</target>
1579 <target>Ustvari račun</target> 1597 <target>Ustvari račun</target>
1580 1598
1581 1599
1582 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1600 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1583 1601
1584 <trans-unit id="3058024914967508975" datatype="html"> 1602 <trans-unit id="3058024914967508975" datatype="html">
1585 <source>My videos</source> 1603 <source>My videos</source>
@@ -1636,7 +1654,7 @@ The link will expire within 1 hour.</target>
1636 <source>VIDEOS</source> 1654 <source>VIDEOS</source>
1637 <target state="new">VIDEOS</target> 1655 <target state="new">VIDEOS</target>
1638 1656
1639 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 1657 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1640 <trans-unit id="667372110624203230" datatype="html"> 1658 <trans-unit id="667372110624203230" datatype="html">
1641 <source>Import jobs concurrency</source> 1659 <source>Import jobs concurrency</source>
1642 <target state="new">Import jobs concurrency</target> 1660 <target state="new">Import jobs concurrency</target>
@@ -1715,7 +1733,7 @@ The link will expire within 1 hour.</target>
1715 <source>I'm a teapot</source> 1733 <source>I'm a teapot</source>
1716 <target state="new">I'm a teapot</target> 1734 <target state="new">I'm a teapot</target>
1717 1735
1718 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit> 1736 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1719 <trans-unit id="1597262876035959248" datatype="html"> 1737 <trans-unit id="1597262876035959248" datatype="html">
1720 <source>That's an error.</source> 1738 <source>That's an error.</source>
1721 <target state="new">That's an error.</target> 1739 <target state="new">That's an error.</target>
@@ -1808,8 +1826,8 @@ The link will expire within 1 hour.</target>
1808 <trans-unit id="2971365540217107489" datatype="html"> 1826 <trans-unit id="2971365540217107489" datatype="html">
1809 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1827 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1810 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1828 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1811 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1829
1812 </trans-unit> 1830 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1813 1831
1814 <trans-unit id="5131854469652959713" datatype="html"> 1832 <trans-unit id="5131854469652959713" datatype="html">
1815 <source>GLOBAL SEARCH</source> 1833 <source>GLOBAL SEARCH</source>
@@ -2555,7 +2573,7 @@ The link will expire within 1 hour.</target>
2555 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2573 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2556 <source>Upload on hold</source><target state="new">Upload on hold</target> 2574 <source>Upload on hold</source><target state="new">Upload on hold</target>
2557 2575
2558 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2576 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2559 <trans-unit id="285180972645018518" datatype="html"> 2577 <trans-unit id="285180972645018518" datatype="html">
2560 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2578 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2561 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2579 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3262,11 +3280,7 @@ The link will expire within 1 hour.</target>
3262 <target state="new">ID</target> 3280 <target state="new">ID</target>
3263 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3281 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3264 </trans-unit> 3282 </trans-unit>
3265 <trans-unit id="2265605798180116441" datatype="html"> 3283
3266 <source>Follower handle</source>
3267 <target state="new">Follower handle</target>
3268 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3269 </trans-unit>
3270 <trans-unit id="5911214550882917183" datatype="html"> 3284 <trans-unit id="5911214550882917183" datatype="html">
3271 <source>State</source> 3285 <source>State</source>
3272 <target state="new">State</target> 3286 <target state="new">State</target>
@@ -3341,11 +3355,7 @@ The link will expire within 1 hour.</target>
3341 </target> 3355 </target>
3342 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3356 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3343 </trans-unit> 3357 </trans-unit>
3344 <trans-unit id="6641024648411549335" datatype="html"> 3358
3345 <source>Host</source>
3346 <target state="new">Host</target>
3347 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3348 </trans-unit>
3349 <trans-unit id="6571718060636962350" datatype="html"> 3359 <trans-unit id="6571718060636962350" datatype="html">
3350 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3360 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3351 <target state="new">Redundancy allowed 3361 <target state="new">Redundancy allowed
@@ -3358,7 +3368,7 @@ The link will expire within 1 hour.</target>
3358 <source>Unfollow</source> 3368 <source>Unfollow</source>
3359 <target state="new">Unfollow</target> 3369 <target state="new">Unfollow</target>
3360 3370
3361 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3371 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3362 <trans-unit id="8246779176913476983" datatype="html"> 3372 <trans-unit id="8246779176913476983" datatype="html">
3363 <source>Open instance in a new tab</source> 3373 <source>Open instance in a new tab</source>
3364 <target state="new">Open instance in a new tab</target> 3374 <target state="new">Open instance in a new tab</target>
@@ -3369,13 +3379,13 @@ The link will expire within 1 hour.</target>
3369 <trans-unit id="9132918641931433659" datatype="html"> 3379 <trans-unit id="9132918641931433659" datatype="html">
3370 <source>No host found matching current filters.</source> 3380 <source>No host found matching current filters.</source>
3371 <target state="new">No host found matching current filters.</target> 3381 <target state="new">No host found matching current filters.</target>
3372 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3382
3373 </trans-unit> 3383 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3374 <trans-unit id="7274241885665071790" datatype="html"> 3384 <trans-unit id="7274241885665071790" datatype="html">
3375 <source>Your instance is not following anyone.</source> 3385 <source>Your instance is not following anyone.</source>
3376 <target state="new">Your instance is not following anyone.</target> 3386 <target state="new">Your instance is not following anyone.</target>
3377 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3387
3378 </trans-unit> 3388 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3379 <trans-unit id="4774348799569692380" datatype="html"> 3389 <trans-unit id="4774348799569692380" datatype="html">
3380 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3390 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3381 <target state="new">Showing 3391 <target state="new">Showing
@@ -3385,16 +3395,8 @@ The link will expire within 1 hour.</target>
3385 </target> 3395 </target>
3386 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3396 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3387 </trans-unit> 3397 </trans-unit>
3388 <trans-unit id="6275803119759621687" datatype="html"> 3398
3389 <source>Follow domains</source> 3399
3390 <target state="new">Follow domains</target>
3391 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3392 </trans-unit>
3393 <trans-unit id="1268699198448750610" datatype="html">
3394 <source>Follow instances</source>
3395 <target state="new">Follow instances</target>
3396 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3397 </trans-unit>
3398 <trans-unit id="9216117865911519658" datatype="html"> 3400 <trans-unit id="9216117865911519658" datatype="html">
3399 <source>Action</source> 3401 <source>Action</source>
3400 <target state="new">Action</target> 3402 <target state="new">Action</target>
@@ -3443,11 +3445,11 @@ The link will expire within 1 hour.</target>
3443 <trans-unit id="5248717555542428023" datatype="html"> 3445 <trans-unit id="5248717555542428023" datatype="html">
3444 <source>Username</source> 3446 <source>Username</source>
3445 <target state="new">Username</target> 3447 <target state="new">Username</target>
3446 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3448
3447 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3449
3448 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3450
3449 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3451
3450 </trans-unit> 3452 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3451 <trans-unit id="5428411040014095392" datatype="html"> 3453 <trans-unit id="5428411040014095392" datatype="html">
3452 <source>e.g. jane_doe</source> 3454 <source>e.g. jane_doe</source>
3453 <target state="new">e.g. jane_doe</target> 3455 <target state="new">e.g. jane_doe</target>
@@ -3477,9 +3479,9 @@ The link will expire within 1 hour.</target>
3477 <trans-unit id="4145496584631696119" datatype="html"> 3479 <trans-unit id="4145496584631696119" datatype="html">
3478 <source>Role</source> 3480 <source>Role</source>
3479 <target state="new">Role</target> 3481 <target state="new">Role</target>
3480 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3482
3481 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3483
3482 </trans-unit> 3484 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3483 <trans-unit id="7046347992315328430" datatype="html"> 3485 <trans-unit id="7046347992315328430" datatype="html">
3484 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3486 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3485 <target state="new"> 3487 <target state="new">
@@ -3504,15 +3506,9 @@ The link will expire within 1 hour.</target>
3504 <trans-unit id="2622255144026150901" datatype="html"> 3506 <trans-unit id="2622255144026150901" datatype="html">
3505 <source>Auth plugin</source> 3507 <source>Auth plugin</source>
3506 <target state="new">Auth plugin</target> 3508 <target state="new">Auth plugin</target>
3507 <context-group purpose="location"> 3509
3508 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3510
3509 <context context-type="linenumber">188</context> 3511 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3510 </context-group>
3511 <context-group purpose="location">
3512 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3513 <context context-type="linenumber">188</context>
3514 </context-group>
3515 </trans-unit>
3516 <trans-unit id="588099657508661970" datatype="html"> 3512 <trans-unit id="588099657508661970" datatype="html">
3517 <source>None (local authentication)</source> 3513 <source>None (local authentication)</source>
3518 <target state="new">None (local authentication)</target> 3514 <target state="new">None (local authentication)</target>
@@ -3766,6 +3762,12 @@ The link will expire within 1 hour.</target>
3766 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3762 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3767 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3763 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3768 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3764 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3765 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3766 <source>Follower</source><target state="new">Follower</target>
3767 <context-group purpose="location">
3768 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3769 <context context-type="linenumber">24</context>
3770 </context-group>
3769 </trans-unit> 3771 </trans-unit>
3770 <trans-unit id="4691552465058437520" datatype="html"> 3772 <trans-unit id="4691552465058437520" datatype="html">
3771 <source>Commented video</source> 3773 <source>Commented video</source>
@@ -4104,8 +4106,8 @@ The link will expire within 1 hour.</target>
4104 <target state="new"> 4106 <target state="new">
4105 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 4107 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
4106 </target> 4108 </target>
4107 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 4109
4108 </trans-unit> 4110 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
4109 <trans-unit id="4058814854824495833" datatype="html"> 4111 <trans-unit id="4058814854824495833" datatype="html">
4110 <source>Mute domains</source> 4112 <source>Mute domains</source>
4111 <target state="new">Mute domains</target> 4113 <target state="new">Mute domains</target>
@@ -6102,11 +6104,8 @@ color: red;
6102 <trans-unit id="5512878593724620692" datatype="html"> 6104 <trans-unit id="5512878593724620692" datatype="html">
6103 <source>CHANNELS</source> 6105 <source>CHANNELS</source>
6104 <target state="new">CHANNELS</target> 6106 <target state="new">CHANNELS</target>
6105 <context-group purpose="location"> 6107
6106 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 6108 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
6107 <context context-type="linenumber">82</context>
6108 </context-group>
6109 </trans-unit>
6110 <trans-unit id="3666829335406793239" datatype="html"> 6109 <trans-unit id="3666829335406793239" datatype="html">
6111 <source>This account does not have channels.</source> 6110 <source>This account does not have channels.</source>
6112 <target state="new">This account does not have channels.</target> 6111 <target state="new">This account does not have channels.</target>
@@ -6151,6 +6150,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6151It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 6150It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
6152channel with the same name (<x id="PH_2"/>)!</target> 6151channel with the same name (<x id="PH_2"/>)!</target>
6153 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 6152 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
6153 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
6154 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
6155 <context-group purpose="location">
6156 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
6157 <context context-type="linenumber">48</context>
6158 </context-group>
6154 </trans-unit> 6159 </trans-unit>
6155 <trans-unit id="5387007581996837469" datatype="html"> 6160 <trans-unit id="5387007581996837469" datatype="html">
6156 <source>My Channels</source> 6161 <source>My Channels</source>
@@ -6749,12 +6754,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6749 <source>Your message has been sent.</source> 6754 <source>Your message has been sent.</source>
6750 <target state="new">Your message has been sent.</target> 6755 <target state="new">Your message has been sent.</target>
6751 6756
6752 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6757 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6753 <trans-unit id="2072135752262464360" datatype="html"> 6758 <trans-unit id="2072135752262464360" datatype="html">
6754 <source>You already sent this form recently</source> 6759 <source>You already sent this form recently</source>
6755 <target state="new">You already sent this form recently</target> 6760 <target state="new">You already sent this form recently</target>
6756 6761
6757 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6762 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6758 <trans-unit id="819067926858619041" datatype="html"> 6763 <trans-unit id="819067926858619041" datatype="html">
6759 <source>Account videos</source> 6764 <source>Account videos</source>
6760 <target state="new">Account videos</target> 6765 <target state="new">Account videos</target>
@@ -6800,13 +6805,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6800 <target state="new"> 6805 <target state="new">
6801 <x id="PH"/> direct account followers 6806 <x id="PH"/> direct account followers
6802 </target> 6807 </target>
6803 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6808
6804 </trans-unit> 6809 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6805 <trans-unit id="6250999352462648289" datatype="html"> 6810 <trans-unit id="6250999352462648289" datatype="html">
6806 <source>Report this account</source> 6811 <source>Report this account</source>
6807 <target state="new">Report this account</target> 6812 <target state="new">Report this account</target>
6808 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6813
6809 </trans-unit> 6814 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6810 <trans-unit id="1504521795586863905" datatype="html"> 6815 <trans-unit id="1504521795586863905" datatype="html">
6811 <source>VIDEOS</source> 6816 <source>VIDEOS</source>
6812 <target state="new">VIDEOS</target> 6817 <target state="new">VIDEOS</target>
@@ -6816,29 +6821,21 @@ channel with the same name (<x id="PH_2"/>)!</target>
6816 <trans-unit id="25349740244798533" datatype="html"> 6821 <trans-unit id="25349740244798533" datatype="html">
6817 <source>Username copied</source> 6822 <source>Username copied</source>
6818 <target state="new">Username copied</target> 6823 <target state="new">Username copied</target>
6819 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6824
6820 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6825
6821 </trans-unit> 6826 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6822 <trans-unit id="9221735175659318025" datatype="html"> 6827 <trans-unit id="9221735175659318025" datatype="html">
6823 <source>1 subscriber</source> 6828 <source>1 subscriber</source>
6824 <target state="new">1 subscriber</target> 6829 <target state="new">1 subscriber</target>
6825 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6830
6826 </trans-unit> 6831 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6827 <trans-unit id="4097331874769079975" datatype="html"> 6832 <trans-unit id="4097331874769079975" datatype="html">
6828 <source><x id="PH"/> subscribers</source> 6833 <source><x id="PH"/> subscribers</source>
6829 <target state="new"><x id="PH"/> subscribers</target> 6834 <target state="new"><x id="PH"/> subscribers</target>
6830 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group>
6831 </trans-unit>
6832 <trans-unit id="4682675125751819107" datatype="html">
6833 <source>Instances you follow</source>
6834 <target state="new">Instances you follow</target>
6835
6836 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6837 <trans-unit id="8899833753704589712" datatype="html">
6838 <source>Instances following you</source>
6839 <target state="new">Instances following you</target>
6840 6835
6841 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit> 6836 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6837
6838
6842 <trans-unit id="1035838766454786107" datatype="html"> 6839 <trans-unit id="1035838766454786107" datatype="html">
6843 <source>Audio-only</source> 6840 <source>Audio-only</source>
6844 <target state="new">Audio-only</target> 6841 <target state="new">Audio-only</target>
@@ -6888,6 +6885,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6888 <source>Auto (via ffmpeg)</source> 6885 <source>Auto (via ffmpeg)</source>
6889 <target state="new">Auto (via ffmpeg)</target> 6886 <target state="new">Auto (via ffmpeg)</target>
6890 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6887 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6888 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6889 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6890 <context-group purpose="location">
6891 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6892 <context context-type="linenumber">3</context>
6893 </context-group>
6891 </trans-unit> 6894 </trans-unit>
6892 <trans-unit id="931255636742351800" datatype="html"> 6895 <trans-unit id="931255636742351800" datatype="html">
6893 <source>No limit</source> 6896 <source>No limit</source>
@@ -7030,18 +7033,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
7030 <trans-unit id="2127446333083057097" datatype="html"> 7033 <trans-unit id="2127446333083057097" datatype="html">
7031 <source>Domain is required.</source> 7034 <source>Domain is required.</source>
7032 <target state="new">Domain is required.</target> 7035 <target state="new">Domain is required.</target>
7033 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 7036
7034 </trans-unit> 7037 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
7035 <trans-unit id="6780793142903080663" datatype="html"> 7038 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
7036 <source>Domains entered are invalid.</source> 7039 <context-group purpose="location">
7037 <target state="new">Domains entered are invalid.</target> 7040 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7038 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 7041 <context context-type="linenumber">93</context>
7039 </trans-unit> 7042 </context-group>
7040 <trans-unit id="5886492514458202177" datatype="html"> 7043 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
7041 <source>Domains entered contain duplicates.</source> 7044 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
7042 <target state="new">Domains entered contain duplicates.</target> 7045 <context-group purpose="location">
7043 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 7046 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7047 <context context-type="linenumber">94</context>
7048 </context-group>
7049 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
7050 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
7051 <context-group purpose="location">
7052 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7053 <context context-type="linenumber">102</context>
7054 </context-group>
7055 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
7056 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
7057 <context-group purpose="location">
7058 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7059 <context context-type="linenumber">103</context>
7060 </context-group>
7044 </trans-unit> 7061 </trans-unit>
7062
7063
7045 <trans-unit id="240806681889331244" datatype="html"> 7064 <trans-unit id="240806681889331244" datatype="html">
7046 <source>Unlimited</source> 7065 <source>Unlimited</source>
7047 <target state="new">Unlimited</target> 7066 <target state="new">Unlimited</target>
@@ -7201,26 +7220,52 @@ channel with the same name (<x id="PH_2"/>)!</target>
7201 <x id="PH"/> removed from instance followers 7220 <x id="PH"/> removed from instance followers
7202 </target> 7221 </target>
7203 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 7222 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
7223 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
7224 <source>Follow</source><target state="new">Follow</target>
7225 <context-group purpose="location">
7226 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7227 <context context-type="linenumber">3</context>
7228 </context-group>
7229 <context-group purpose="location">
7230 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7231 <context context-type="linenumber">37</context>
7232 </context-group>
7233 <context-group purpose="location">
7234 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7235 <context context-type="linenumber">18</context>
7236 </context-group>
7237 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
7238 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
7239 <context-group purpose="location">
7240 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7241 <context context-type="linenumber">11</context>
7242 </context-group>
7204 </trans-unit> 7243 </trans-unit>
7205 <trans-unit id="2740793005745065895" datatype="html"> 7244 <trans-unit id="2740793005745065895" datatype="html">
7206 <source><x id="PH"/> is not valid </source> 7245 <source><x id="PH"/> is not valid </source>
7207 <target state="new"> 7246 <target state="new">
7208 <x id="PH"/> is not valid 7247 <x id="PH"/> is not valid
7209 </target> 7248 </target>
7210 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 7249
7211 </trans-unit> 7250 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
7212 <trans-unit id="2355066641781598196" datatype="html"> 7251 <trans-unit id="2355066641781598196" datatype="html">
7213 <source>Follow request(s) sent!</source> 7252 <source>Follow request(s) sent!</source>
7214 <target state="new">Follow request(s) sent!</target> 7253 <target state="new">Follow request(s) sent!</target>
7215 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 7254
7255 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
7256 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
7257 <context-group purpose="location">
7258 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7259 <context context-type="linenumber">3</context>
7260 </context-group>
7216 </trans-unit> 7261 </trans-unit>
7217 <trans-unit id="4245720728052819482" datatype="html"> 7262 <trans-unit id="4245720728052819482" datatype="html">
7218 <source>Do you really want to unfollow <x id="PH"/>?</source> 7263 <source>Do you really want to unfollow <x id="PH"/>?</source>
7219 <target state="new">Do you really want to unfollow 7264 <target state="new">Do you really want to unfollow
7220 <x id="PH"/>? 7265 <x id="PH"/>?
7221 </target> 7266 </target>
7222 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 7267
7223 </trans-unit> 7268 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
7224 <trans-unit id="9160510009013134726" datatype="html"> 7269 <trans-unit id="9160510009013134726" datatype="html">
7225 <source>Unfollow</source> 7270 <source>Unfollow</source>
7226 <target state="new">Unfollow</target> 7271 <target state="new">Unfollow</target>
@@ -7231,8 +7276,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7231 <target state="new">You are not following 7276 <target state="new">You are not following
7232 <x id="PH"/> anymore. 7277 <x id="PH"/> anymore.
7233 </target> 7278 </target>
7234 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 7279
7235 </trans-unit> 7280 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
7236 <trans-unit id="2593763089859685916" datatype="html"> 7281 <trans-unit id="2593763089859685916" datatype="html">
7237 <source>enabled</source> 7282 <source>enabled</source>
7238 <target state="new">enabled</target> 7283 <target state="new">enabled</target>
@@ -7723,9 +7768,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7723 <trans-unit id="1519954996184640001" datatype="html"> 7768 <trans-unit id="1519954996184640001" datatype="html">
7724 <source>Error</source> 7769 <source>Error</source>
7725 <target state="new">Error</target> 7770 <target state="new">Error</target>
7726 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7771
7727 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7772
7728 </trans-unit> 7773 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7729 <trans-unit id="5076187961693950167" datatype="html"> 7774 <trans-unit id="5076187961693950167" datatype="html">
7730 <source>Standard logs</source> 7775 <source>Standard logs</source>
7731 <target state="new">Standard logs</target> 7776 <target state="new">Standard logs</target>
@@ -7770,16 +7815,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7770 <target state="new">Update user password</target> 7815 <target state="new">Update user password</target>
7771 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7816 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7772 </trans-unit> 7817 </trans-unit>
7773 <trans-unit id="177544274549739411" datatype="html"> 7818
7774 <source>Following list</source> 7819
7775 <target state="new">Following list</target>
7776 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7777 </trans-unit>
7778 <trans-unit id="8092429110007204784" datatype="html">
7779 <source>Followers list</source>
7780 <target state="new">Followers list</target>
7781 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7782 </trans-unit>
7783 <trans-unit id="780323526182667308" datatype="html"> 7820 <trans-unit id="780323526182667308" datatype="html">
7784 <source>User <x id="PH"/> updated.</source> 7821 <source>User <x id="PH"/> updated.</source>
7785 <target state="new">User 7822 <target state="new">User
@@ -7819,16 +7856,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7819 <target state="new">Federation</target> 7856 <target state="new">Federation</target>
7820 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7857 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7821 </trans-unit> 7858 </trans-unit>
7822 <trans-unit id="4682675125751819107" datatype="html"> 7859
7823 <source>Instances you follow</source> 7860
7824 <target state="new">Instances you follow</target>
7825 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7826 </trans-unit>
7827 <trans-unit id="8899833753704589712" datatype="html">
7828 <source>Instances following you</source>
7829 <target state="new">Instances following you</target>
7830 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7831 </trans-unit>
7832 <trans-unit id="3767259920053407667" datatype="html"> 7861 <trans-unit id="3767259920053407667" datatype="html">
7833 <source>Videos will be deleted, comments will be tombstoned.</source> 7862 <source>Videos will be deleted, comments will be tombstoned.</source>
7834 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7863 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7859,7 +7888,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7859 <target state="new">Set Email as Verified</target> 7888 <target state="new">Set Email as Verified</target>
7860 7889
7861 7890
7862 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7891 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7892 <source>Created</source><target state="new">Created</target>
7893 <context-group purpose="location">
7894 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7895 <context context-type="linenumber">115</context>
7896 </context-group>
7897 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7898 <source>Daily quota</source><target state="new">Daily quota</target>
7899 <context-group purpose="location">
7900 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7901 <context context-type="linenumber">120</context>
7902 </context-group>
7903 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7904 <source>Last login</source><target state="new">Last login</target>
7905 <context-group purpose="location">
7906 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7907 <context context-type="linenumber">122</context>
7908 </context-group>
7909 </trans-unit>
7863 <trans-unit id="3403978719736970622" datatype="html"> 7910 <trans-unit id="3403978719736970622" datatype="html">
7864 <source>You cannot ban root.</source> 7911 <source>You cannot ban root.</source>
7865 <target state="new">You cannot ban root.</target> 7912 <target state="new">You cannot ban root.</target>
@@ -8172,13 +8219,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
8172 <target state="new">Video channel 8219 <target state="new">Video channel
8173 <x id="PH"/> created. 8220 <x id="PH"/> created.
8174 </target> 8221 </target>
8175 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 8222
8176 </trans-unit> 8223 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
8177 <trans-unit id="8723777130353305761" datatype="html"> 8224 <trans-unit id="8723777130353305761" datatype="html">
8178 <source>This name already exists on this instance.</source> 8225 <source>This name already exists on this instance.</source>
8179 <target state="new">This name already exists on this instance.</target> 8226 <target state="new">This name already exists on this instance.</target>
8180 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 8227
8181 </trans-unit> 8228 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
8182 <trans-unit id="7589345916094713536" datatype="html"> 8229 <trans-unit id="7589345916094713536" datatype="html">
8183 <source>Video channel <x id="PH"/> updated.</source> 8230 <source>Video channel <x id="PH"/> updated.</source>
8184 <target state="new">Video channel 8231 <target state="new">Video channel
@@ -8201,13 +8248,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8201 <target state="new">Banner deleted.</target> 8248 <target state="new">Banner deleted.</target>
8202 8249
8203 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 8250 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
8204 <trans-unit id="2575302837003821736" datatype="html"> 8251
8205 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
8206 <target state="new">Please type the display name of the video channel (
8207 <x id="PH"/>) to confirm
8208 </target>
8209 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
8210 </trans-unit>
8211 <trans-unit id="624066830180032195" datatype="html"> 8252 <trans-unit id="624066830180032195" datatype="html">
8212 <source>Video channel <x id="PH"/> deleted.</source> 8253 <source>Video channel <x id="PH"/> deleted.</source>
8213 <target state="new">Video channel 8254 <target state="new">Video channel
@@ -8370,6 +8411,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8370 <source>Ownership change request sent.</source> 8411 <source>Ownership change request sent.</source>
8371 <target state="new">Ownership change request sent.</target> 8412 <target state="new">Ownership change request sent.</target>
8372 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8413 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8414 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8415 <source>Sort by</source><target state="new">Sort by</target>
8416 <context-group purpose="location">
8417 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8418 <context context-type="linenumber">26</context>
8419 </context-group>
8373 </trans-unit> 8420 </trans-unit>
8374 <trans-unit id="3245220240937722814" datatype="html"> 8421 <trans-unit id="3245220240937722814" datatype="html">
8375 <source>My channels</source> 8422 <source>My channels</source>
@@ -8472,7 +8519,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8472 <target state="new">Subscribe to the account</target> 8519 <target state="new">Subscribe to the account</target>
8473 8520
8474 8521
8475 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8522 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8476 <trans-unit id="3131904093925601441" datatype="html"> 8523 <trans-unit id="3131904093925601441" datatype="html">
8477 <source>PLAYLISTS</source> 8524 <source>PLAYLISTS</source>
8478 <target state="new">PLAYLISTS</target> 8525 <target state="new">PLAYLISTS</target>
@@ -8519,35 +8566,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8519 <trans-unit id="3779524668013120370" datatype="html"> 8566 <trans-unit id="3779524668013120370" datatype="html">
8520 <source>Go to my subscriptions</source> 8567 <source>Go to my subscriptions</source>
8521 <target state="new">Go to my subscriptions</target> 8568 <target state="new">Go to my subscriptions</target>
8522 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8569
8523 </trans-unit> 8570 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8524 <trans-unit id="1136469849928650779" datatype="html"> 8571 <trans-unit id="1136469849928650779" datatype="html">
8525 <source>Go to my videos</source> 8572 <source>Go to my videos</source>
8526 <target state="new">Go to my videos</target> 8573 <target state="new">Go to my videos</target>
8527 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8574
8528 </trans-unit> 8575 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8529 <trans-unit id="7836683738999600376" datatype="html"> 8576 <trans-unit id="7836683738999600376" datatype="html">
8530 <source>Go to my imports</source> 8577 <source>Go to my imports</source>
8531 <target state="new">Go to my imports</target> 8578 <target state="new">Go to my imports</target>
8532 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8579
8533 </trans-unit> 8580 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8534 <trans-unit id="7511292153332773503" datatype="html"> 8581 <trans-unit id="7511292153332773503" datatype="html">
8535 <source>Go to my channels</source> 8582 <source>Go to my channels</source>
8536 <target state="new">Go to my channels</target> 8583 <target state="new">Go to my channels</target>
8537 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8584
8538 </trans-unit> 8585 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8539 <trans-unit id="2013324644839511073" datatype="html"> 8586 <trans-unit id="2013324644839511073" datatype="html">
8540 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8587 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8541Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8588Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8542 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8589 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8543Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8590Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8544 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8591
8545 </trans-unit> 8592 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8546 <trans-unit id="375263728166936544" datatype="html"> 8593 <trans-unit id="375263728166936544" datatype="html">
8547 <source>You need to reconnect.</source> 8594 <source>You need to reconnect.</source>
8548 <target state="new">You need to reconnect.</target> 8595 <target state="new">You need to reconnect.</target>
8549 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8596
8550 </trans-unit> 8597 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8551 <trans-unit id="2206638022166154361" datatype="html"> 8598 <trans-unit id="2206638022166154361" datatype="html">
8552 <source>Keyboard Shortcuts:</source> 8599 <source>Keyboard Shortcuts:</source>
8553 <target state="new">Keyboard Shortcuts:</target> 8600 <target state="new">Keyboard Shortcuts:</target>
@@ -8558,6 +8605,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8558 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8605 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8559 <context context-type="linenumber">98</context> 8606 <context context-type="linenumber">98</context>
8560 </context-group> 8607 </context-group>
8608 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8609 <source>In my library</source><target state="new">In my library</target>
8610 <context-group purpose="location">
8611 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8612 <context context-type="linenumber">104</context>
8613 </context-group>
8561 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 8614 </trans-unit><trans-unit id="232050922346936574" datatype="html">
8562 <source>Trending</source><target state="new">Trending</target> 8615 <source>Trending</source><target state="new">Trending</target>
8563 8616
@@ -8580,39 +8633,39 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8580 <trans-unit id="1266887509445371246" datatype="html"> 8633 <trans-unit id="1266887509445371246" datatype="html">
8581 <source>Incorrect username or password.</source> 8634 <source>Incorrect username or password.</source>
8582 <target state="new">Incorrect username or password.</target> 8635 <target state="new">Incorrect username or password.</target>
8583 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8636
8584 </trans-unit> 8637 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8585 <trans-unit id="6974874606619467663" datatype="html"> 8638 <trans-unit id="6974874606619467663" datatype="html">
8586 <source>Your account is blocked.</source> 8639 <source>Your account is blocked.</source>
8587 <target state="new">Your account is blocked.</target> 8640 <target state="new">Your account is blocked.</target>
8588 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8641
8589 </trans-unit> 8642 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8590 <trans-unit id="7939914198003891823" datatype="html"> 8643 <trans-unit id="7939914198003891823" datatype="html">
8591 <source>any language</source> 8644 <source>any language</source>
8592 <target state="new">any language</target> 8645 <target state="new">any language</target>
8593 8646
8594 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8647 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8595 8648
8596 <trans-unit id="5633144232269377096" datatype="html"> 8649 <trans-unit id="5633144232269377096" datatype="html">
8597 <source>hide</source> 8650 <source>hide</source>
8598 <target state="new">hide</target> 8651 <target state="new">hide</target>
8599 8652
8600 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8653 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8601 <trans-unit id="8603861867909474404" datatype="html"> 8654 <trans-unit id="8603861867909474404" datatype="html">
8602 <source>blur</source> 8655 <source>blur</source>
8603 <target state="new">blur</target> 8656 <target state="new">blur</target>
8604 8657
8605 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8658 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8606 <trans-unit id="4534458451100881847" datatype="html"> 8659 <trans-unit id="4534458451100881847" datatype="html">
8607 <source>display</source> 8660 <source>display</source>
8608 <target state="new">display</target> 8661 <target state="new">display</target>
8609 8662
8610 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8663 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8611 <trans-unit id="4467323362722952678" datatype="html"> 8664 <trans-unit id="4467323362722952678" datatype="html">
8612 <source>Unknown</source> 8665 <source>Unknown</source>
8613 <target state="new">Unknown</target> 8666 <target state="new">Unknown</target>
8614 8667
8615 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8668 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8616 <trans-unit id="8781423666414310853" datatype="html"> 8669 <trans-unit id="8781423666414310853" datatype="html">
8617 <source>Your password has been successfully reset!</source> 8670 <source>Your password has been successfully reset!</source>
8618 <target state="new">Your password has been successfully reset!</target> 8671 <target state="new">Your password has been successfully reset!</target>
@@ -10209,18 +10262,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10209 <target state="new">Too many attempts, please try again after 10262 <target state="new">Too many attempts, please try again after
10210 <x id="PH"/> minutes. 10263 <x id="PH"/> minutes.
10211 </target> 10264 </target>
10212 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 10265
10213 </trans-unit> 10266 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
10214 <trans-unit id="4965472196059235310" datatype="html"> 10267 <trans-unit id="4965472196059235310" datatype="html">
10215 <source>Too many attempts, please try again later.</source> 10268 <source>Too many attempts, please try again later.</source>
10216 <target state="new">Too many attempts, please try again later.</target> 10269 <target state="new">Too many attempts, please try again later.</target>
10217 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 10270
10218 </trans-unit> 10271 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
10219 <trans-unit id="1693549688987384699" datatype="html"> 10272 <trans-unit id="1693549688987384699" datatype="html">
10220 <source>Server error. Please retry later.</source> 10273 <source>Server error. Please retry later.</source>
10221 <target state="new">Server error. Please retry later.</target> 10274 <target state="new">Server error. Please retry later.</target>
10222 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 10275
10223 </trans-unit> 10276 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
10224 <trans-unit id="5927402622550505067" datatype="html"> 10277 <trans-unit id="5927402622550505067" datatype="html">
10225 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 10278 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
10226 <target state="new">Subscribed to all current channels of 10279 <target state="new">Subscribed to all current channels of
@@ -10818,35 +10871,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10818 <source>Your video was uploaded to your account and is private.</source> 10871 <source>Your video was uploaded to your account and is private.</source>
10819 <target state="new">Your video was uploaded to your account and is private.</target> 10872 <target state="new">Your video was uploaded to your account and is private.</target>
10820 10873
10821 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10874 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10822 <trans-unit id="5699822024600815733" datatype="html"> 10875 <trans-unit id="5699822024600815733" datatype="html">
10823 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10876 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10824 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target> 10877 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target>
10825 10878
10826 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10879 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10827 <trans-unit id="1219739004043110649" datatype="html"> 10880 <trans-unit id="1219739004043110649" datatype="html">
10828 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10881 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10829 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target> 10882 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target>
10830 10883
10831 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10884 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10832 <trans-unit id="6932865105766151309" datatype="html"> 10885 <trans-unit id="6932865105766151309" datatype="html">
10833 <source>Upload</source> 10886 <source>Upload</source>
10834 <target state="new">Upload</target> 10887 <target state="new">Upload</target>
10835 10888
10836 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10889 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10837 <trans-unit id="8278735427925094503" datatype="html"> 10890 <trans-unit id="8278735427925094503" datatype="html">
10838 <source>Upload <x id="PH"/> </source> 10891 <source>Upload <x id="PH"/> </source>
10839 <target state="new">Upload 10892 <target state="new">Upload
10840 <x id="PH"/> 10893 <x id="PH"/>
10841 </target> 10894 </target>
10842 10895
10843 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10896 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10844 10897
10845 <trans-unit id="5981816353437801748" datatype="html"> 10898 <trans-unit id="5981816353437801748" datatype="html">
10846 <source>Video published.</source> 10899 <source>Video published.</source>
10847 <target state="new">Video published.</target> 10900 <target state="new">Video published.</target>
10848 10901
10849 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10902 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10850 10903
10851 10904
10852 <trans-unit id="764164089183618119" datatype="html"> 10905 <trans-unit id="764164089183618119" datatype="html">
@@ -10916,27 +10969,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10916 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10969 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10917 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10970 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10918 10971
10919 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10972 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10920 <trans-unit id="5761611056224181752" datatype="html"> 10973 <trans-unit id="5761611056224181752" datatype="html">
10921 <source>Redirection</source> 10974 <source>Redirection</source>
10922 <target state="new">Redirection</target> 10975 <target state="new">Redirection</target>
10923 10976
10924 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10977 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10925 <trans-unit id="8858527736400081688" datatype="html"> 10978 <trans-unit id="8858527736400081688" datatype="html">
10926 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10979 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10927 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target> 10980 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target>
10928 10981
10929 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10982 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10930 <trans-unit id="3937119019020041049" datatype="html"> 10983 <trans-unit id="3937119019020041049" datatype="html">
10931 <source>Mature or explicit content</source> 10984 <source>Mature or explicit content</source>
10932 <target state="new">Mature or explicit content</target> 10985 <target state="new">Mature or explicit content</target>
10933 10986
10934 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10987 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10935 <trans-unit id="1755474755114288376" datatype="html"> 10988 <trans-unit id="1755474755114288376" datatype="html">
10936 <source>Up Next</source> 10989 <source>Up Next</source>
10937 <target state="new">Up Next</target> 10990 <target state="new">Up Next</target>
10938 10991
10939 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10992 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10940 <trans-unit id="2159130950882492111" datatype="html"> 10993 <trans-unit id="2159130950882492111" datatype="html">
10941 <source>Cancel</source> 10994 <source>Cancel</source>
10942 <target state="new">Cancel</target> 10995 <target state="new">Cancel</target>
@@ -10946,62 +10999,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10946 <source>Autoplay is suspended</source> 10999 <source>Autoplay is suspended</source>
10947 <target state="new">Autoplay is suspended</target> 11000 <target state="new">Autoplay is suspended</target>
10948 11001
10949 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 11002 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10950 <trans-unit id="7895294730547405228" datatype="html"> 11003 <trans-unit id="7895294730547405228" datatype="html">
10951 <source>Enter/exit fullscreen (requires player focus)</source> 11004 <source>Enter/exit fullscreen (requires player focus)</source>
10952 <target state="new">Enter/exit fullscreen (requires player focus)</target> 11005 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10953 11006
10954 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 11007 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10955 <trans-unit id="7618388257165864759" datatype="html"> 11008 <trans-unit id="7618388257165864759" datatype="html">
10956 <source>Play/Pause the video (requires player focus)</source> 11009 <source>Play/Pause the video (requires player focus)</source>
10957 <target state="new">Play/Pause the video (requires player focus)</target> 11010 <target state="new">Play/Pause the video (requires player focus)</target>
10958 11011
10959 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 11012 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10960 <trans-unit id="7761890399634216630" datatype="html"> 11013 <trans-unit id="7761890399634216630" datatype="html">
10961 <source>Mute/unmute the video (requires player focus)</source> 11014 <source>Mute/unmute the video (requires player focus)</source>
10962 <target state="new">Mute/unmute the video (requires player focus)</target> 11015 <target state="new">Mute/unmute the video (requires player focus)</target>
10963 11016
10964 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 11017 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10965 <trans-unit id="5996585232248234904" datatype="html"> 11018 <trans-unit id="5996585232248234904" datatype="html">
10966 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 11019 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10967 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 11020 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10968 11021
10969 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 11022 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10970 <trans-unit id="3748765405903319998" datatype="html"> 11023 <trans-unit id="3748765405903319998" datatype="html">
10971 <source>Increase the volume (requires player focus)</source> 11024 <source>Increase the volume (requires player focus)</source>
10972 <target state="new">Increase the volume (requires player focus)</target> 11025 <target state="new">Increase the volume (requires player focus)</target>
10973 11026
10974 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 11027 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10975 <trans-unit id="5810704036407159982" datatype="html"> 11028 <trans-unit id="5810704036407159982" datatype="html">
10976 <source>Decrease the volume (requires player focus)</source> 11029 <source>Decrease the volume (requires player focus)</source>
10977 <target state="new">Decrease the volume (requires player focus)</target> 11030 <target state="new">Decrease the volume (requires player focus)</target>
10978 11031
10979 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 11032 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10980 <trans-unit id="2622048822548065691" datatype="html"> 11033 <trans-unit id="2622048822548065691" datatype="html">
10981 <source>Seek the video forward (requires player focus)</source> 11034 <source>Seek the video forward (requires player focus)</source>
10982 <target state="new">Seek the video forward (requires player focus)</target> 11035 <target state="new">Seek the video forward (requires player focus)</target>
10983 11036
10984 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 11037 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10985 <trans-unit id="6540078205109221153" datatype="html"> 11038 <trans-unit id="6540078205109221153" datatype="html">
10986 <source>Seek the video backward (requires player focus)</source> 11039 <source>Seek the video backward (requires player focus)</source>
10987 <target state="new">Seek the video backward (requires player focus)</target> 11040 <target state="new">Seek the video backward (requires player focus)</target>
10988 11041
10989 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 11042 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10990 <trans-unit id="1956491957766210808" datatype="html"> 11043 <trans-unit id="1956491957766210808" datatype="html">
10991 <source>Increase playback rate (requires player focus)</source> 11044 <source>Increase playback rate (requires player focus)</source>
10992 <target state="new">Increase playback rate (requires player focus)</target> 11045 <target state="new">Increase playback rate (requires player focus)</target>
10993 11046
10994 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 11047 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10995 <trans-unit id="5495529997674803186" datatype="html"> 11048 <trans-unit id="5495529997674803186" datatype="html">
10996 <source>Decrease playback rate (requires player focus)</source> 11049 <source>Decrease playback rate (requires player focus)</source>
10997 <target state="new">Decrease playback rate (requires player focus)</target> 11050 <target state="new">Decrease playback rate (requires player focus)</target>
10998 11051
10999 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 11052 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
11000 <trans-unit id="3178343147230721210" datatype="html"> 11053 <trans-unit id="3178343147230721210" datatype="html">
11001 <source>Navigate in the video frame by frame (requires player focus)</source> 11054 <source>Navigate in the video frame by frame (requires player focus)</source>
11002 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 11055 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
11003 11056
11004 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 11057 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
11005 <trans-unit id="8025996572234182184" datatype="html"> 11058 <trans-unit id="8025996572234182184" datatype="html">
11006 <source>Like the video</source> 11059 <source>Like the video</source>
11007 <target state="new">Like the video</target> 11060 <target state="new">Like the video</target>
diff --git a/client/src/locale/angular.sv-SE.xlf b/client/src/locale/angular.sv-SE.xlf
index f36bd5c46..1c3671468 100644
--- a/client/src/locale/angular.sv-SE.xlf
+++ b/client/src/locale/angular.sv-SE.xlf
@@ -162,19 +162,19 @@
162 <target state="translated"> 162 <target state="translated">
163 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 163 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
164 </target> 164 </target>
165 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group> 165
166 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group> 166
167 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group> 167
168 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group> 168
169 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group> 169
170 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 170
171 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group> 171
172 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group> 172
173 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group> 173
174 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group> 174
175 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group> 175
176 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group> 176
177 </trans-unit> 177 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
178 <trans-unit id="1486537403020619891" datatype="html"> 178 <trans-unit id="1486537403020619891" datatype="html">
179 <source>My watch history</source> 179 <source>My watch history</source>
180 <target state="translated">Min visningshistorik</target> 180 <target state="translated">Min visningshistorik</target>
@@ -243,22 +243,22 @@
243 <trans-unit id="5674286808255988565"> 243 <trans-unit id="5674286808255988565">
244 <source>Create</source> 244 <source>Create</source>
245 <target>Skapa</target> 245 <target>Skapa</target>
246 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 246
247 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 247
248 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 248
249 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 249
250 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 250
251 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 251
252 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 252
253 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 253
254 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 254
255 </trans-unit> 255 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
256 <trans-unit id="1006562256968398209" datatype="html"> 256 <trans-unit id="1006562256968398209" datatype="html">
257 <source>video</source> 257 <source>video</source>
258 <target state="translated">video</target> 258 <target state="translated">video</target>
259 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group> 259
260 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group> 260
261 </trans-unit> 261 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
262 <trans-unit id="6438815964972582865" datatype="html"> 262 <trans-unit id="6438815964972582865" datatype="html">
263 <source>The following link contains a private token and should not be shared with anyone.</source> 263 <source>The following link contains a private token and should not be shared with anyone.</source>
264 <target state="translated">Följande länk innehåller en personlig nyckel och bör ej delas med någon annan.</target> 264 <target state="translated">Följande länk innehåller en personlig nyckel och bör ej delas med någon annan.</target>
@@ -330,13 +330,13 @@
330 <trans-unit id="6995024616159044376" datatype="html"> 330 <trans-unit id="6995024616159044376" datatype="html">
331 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 331 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
332 <target state="translated">Din videokvot kommer överskridas av den här videon (videostorlek: <x id="PH" equiv-text="videoSizeBytes"/>, använt: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, kvot: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 332 <target state="translated">Din videokvot kommer överskridas av den här videon (videostorlek: <x id="PH" equiv-text="videoSizeBytes"/>, använt: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, kvot: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
333 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 333
334 </trans-unit> 334 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
335 <trans-unit id="7873395933409147217" datatype="html"> 335 <trans-unit id="7873395933409147217" datatype="html">
336 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 336 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
337 <target state="translated">Din dagliga videokvot kommer överskridas av den här videon (videostorlek: <x id="PH" equiv-text="videoSizeBytes"/>, använt: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, kvot:<x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 337 <target state="translated">Din dagliga videokvot kommer överskridas av den här videon (videostorlek: <x id="PH" equiv-text="videoSizeBytes"/>, använt: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, kvot:<x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
338 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 338
339 </trans-unit> 339 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
340 <trans-unit id="5235042777215655908" datatype="html"> 340 <trans-unit id="5235042777215655908" datatype="html">
341 <source>subtitles</source> 341 <source>subtitles</source>
342 <target state="translated">undertexter</target> 342 <target state="translated">undertexter</target>
@@ -776,10 +776,10 @@
776 <trans-unit id="2602586221576511475"> 776 <trans-unit id="2602586221576511475">
777 <source>Video quota</source> 777 <source>Video quota</source>
778 <target>Videokvot</target> 778 <target>Videokvot</target>
779 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 779
780 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 780
781 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 781
782 </trans-unit> 782 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
783 <trans-unit id="1502595455339510144"> 783 <trans-unit id="1502595455339510144">
784 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 784 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
785 <target>Obegränsat <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per dag)<x id="CLOSE_TAG_NG_CONTAINER"/></target> 785 <target>Obegränsat <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per dag)<x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -859,6 +859,30 @@
859 <target>Federation</target> 859 <target>Federation</target>
860 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 860 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
861 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 861 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
862 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
863 <source>Following</source><target state="new">Following</target>
864 <context-group purpose="location">
865 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
866 <context context-type="linenumber">29</context>
867 </context-group>
868 <context-group purpose="location">
869 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
870 <context context-type="linenumber">31</context>
871 </context-group>
872 <context-group purpose="location">
873 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
874 <context context-type="linenumber">28</context>
875 </context-group>
876 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
877 <source>Followers</source><target state="new">Followers</target>
878 <context-group purpose="location">
879 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
880 <context context-type="linenumber">34</context>
881 </context-group>
882 <context-group purpose="location">
883 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
884 <context context-type="linenumber">37</context>
885 </context-group>
862 </trans-unit> 886 </trans-unit>
863 <trans-unit id="3541687134897970106"> 887 <trans-unit id="3541687134897970106">
864 <source>followers</source> 888 <source>followers</source>
@@ -922,25 +946,25 @@
922 <trans-unit id="2159130950882492111"> 946 <trans-unit id="2159130950882492111">
923 <source>Cancel</source> 947 <source>Cancel</source>
924 <target>Avbryt</target> 948 <target>Avbryt</target>
925 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group> 949
926 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group> 950
927 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group> 951
928 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group> 952
929 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group> 953
930 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group> 954
931 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group> 955
932 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group> 956
933 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group> 957
934 <context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group> 958
935 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group> 959
936 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group> 960
937 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 961
938 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 962
939 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group> 963
940 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group> 964
941 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group> 965
942 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group> 966
943 </trans-unit> 967 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
944 <trans-unit id="3616223838716839702"> 968 <trans-unit id="3616223838716839702">
945 <source>Ban this user</source> 969 <source>Ban this user</source>
946 <target>Blockera den här användaren</target> 970 <target>Blockera den här användaren</target>
@@ -1006,19 +1030,13 @@
1006 <trans-unit id="7252854992688790751" datatype="html"> 1030 <trans-unit id="7252854992688790751" datatype="html">
1007 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1031 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1008 <target state="translated">Den här instansen tillåter kontoregistrering. Se till att läsa <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>villkoren<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>villkoren<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> innan du skapar ett konto. Du kan också söka efter en annan instans som passar dina behov bättre på <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1032 <target state="translated">Den här instansen tillåter kontoregistrering. Se till att läsa <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>villkoren<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>villkoren<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> innan du skapar ett konto. Du kan också söka efter en annan instans som passar dina behov bättre på <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1009 <context-group purpose="location"> 1033
1010 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1034 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1011 <context context-type="linenumber">60,62</context>
1012 </context-group>
1013 </trans-unit>
1014 <trans-unit id="7215649348148521605" datatype="html"> 1035 <trans-unit id="7215649348148521605" datatype="html">
1015 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1036 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1016 <target state="translated">Den här instansen tillåter inte kontoregistrering för närvarande, men du kan läsa <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>villkoren<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> för mer information eller hitta en annan instans som ger dig möjligheten att skaffa ett konto och ladda upp dina videor där. Hitta din instans av dem alla på <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1037 <target state="translated">Den här instansen tillåter inte kontoregistrering för närvarande, men du kan läsa <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>villkoren<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> för mer information eller hitta en annan instans som ger dig möjligheten att skaffa ett konto och ladda upp dina videor där. Hitta din instans av dem alla på <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1017 <context-group purpose="location"> 1038
1018 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1019 <context context-type="linenumber">65,67</context>
1020 </context-group>
1021 </trans-unit>
1022 <trans-unit id="2392488717875840729"> 1040 <trans-unit id="2392488717875840729">
1023 <source>User</source> 1041 <source>User</source>
1024 <target>Användare</target> 1042 <target>Användare</target>
@@ -1029,69 +1047,69 @@
1029 <source>Username or email address</source> 1047 <source>Username or email address</source>
1030 <target>Användarnamn eller e-postadress</target> 1048 <target>Användarnamn eller e-postadress</target>
1031 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1049 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1050 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1051 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1052 <context-group purpose="location">
1053 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1054 <context context-type="linenumber">33,34</context>
1055 </context-group>
1032 </trans-unit> 1056 </trans-unit>
1033 <trans-unit id="1431416938026210429"> 1057 <trans-unit id="1431416938026210429">
1034 <source>Password</source> 1058 <source>Password</source>
1035 <target>Lösenord</target> 1059 <target>Lösenord</target>
1036 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1060
1037 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1061
1038 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1062
1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1063
1040 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1064
1041 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1065
1042 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1066
1043 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1067
1044 </trans-unit> 1068 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1045 <trans-unit id="8715156686857791956" datatype="html"> 1069 <trans-unit id="8715156686857791956" datatype="html">
1046 <source>Click here to reset your password</source> 1070 <source>Click here to reset your password</source>
1047 <target state="translated">Klicka här för att återställa ditt lösenord</target> 1071 <target state="translated">Klicka här för att återställa ditt lösenord</target>
1048 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1072
1049 </trans-unit> 1073 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1050 <trans-unit id="892063502898494584" datatype="html"> 1074 <trans-unit id="892063502898494584" datatype="html">
1051 <source>I forgot my password</source> 1075 <source>I forgot my password</source>
1052 <target state="translated">Jag har glömt mitt lösenord</target> 1076 <target state="translated">Jag har glömt mitt lösenord</target>
1053 <context-group purpose="location"> 1077
1054 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1078 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1055 <context context-type="linenumber">47</context>
1056 </context-group>
1057 </trans-unit>
1058 <trans-unit id="2101170466365500913" datatype="html"> 1079 <trans-unit id="2101170466365500913" datatype="html">
1059 <source>Logging into an account lets you publish content</source> 1080 <source>Logging into an account lets you publish content</source>
1060 <target state="translated">Du måste logga in för att kunna publicera material</target> 1081 <target state="translated">Du måste logga in för att kunna publicera material</target>
1061 <context-group purpose="location"> 1082
1062 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1083 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1063 <context context-type="linenumber">56,57</context>
1064 </context-group>
1065 </trans-unit>
1066 <trans-unit id="2454050363478003966"> 1084 <trans-unit id="2454050363478003966">
1067 <source>Login</source> 1085 <source>Login</source>
1068 <target>Logga in</target> 1086 <target>Logga in</target>
1069 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1087
1070 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1088
1071 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1089
1072 </trans-unit> 1090 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1073 <trans-unit id="3183213940445113677" datatype="html"> 1091 <trans-unit id="3183213940445113677" datatype="html">
1074 <source>Or sign in with</source> 1092 <source>Or sign in with</source>
1075 <target state="translated">Eller logga in med</target> 1093 <target state="translated">Eller logga in med</target>
1076 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1094
1077 </trans-unit> 1095 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1078 <trans-unit id="3238209155172574367"> 1096 <trans-unit id="3238209155172574367">
1079 <source>Forgot your password</source> 1097 <source>Forgot your password</source>
1080 <target>Glömt ditt lösenord</target> 1098 <target>Glömt ditt lösenord</target>
1081 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1099
1082 </trans-unit> 1100 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1083 <trans-unit id="87327320394367488"> 1101 <trans-unit id="87327320394367488">
1084 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1102 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1085 <target> 1103 <target>
1086 Du kan inte återställa ditt lösenord eftersom din instans administratör inte har konfigurerat PeerTubes e-postsystem. 1104 Du kan inte återställa ditt lösenord eftersom din instans administratör inte har konfigurerat PeerTubes e-postsystem.
1087 </target> 1105 </target>
1088 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1106
1089 </trans-unit> 1107 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1090 <trans-unit id="3188014010833256853" datatype="html"> 1108 <trans-unit id="3188014010833256853" datatype="html">
1091 <source>Enter your email address and we will send you a link to reset your password.</source> 1109 <source>Enter your email address and we will send you a link to reset your password.</source>
1092 <target state="translated">Ange din e-postadress så skickar vi dig en länk för att återställa till lösenord.</target> 1110 <target state="translated">Ange din e-postadress så skickar vi dig en länk för att återställa till lösenord.</target>
1093 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1111
1094 </trans-unit> 1112 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1095 <trans-unit id="1190256911880544559" datatype="html"> 1113 <trans-unit id="1190256911880544559" datatype="html">
1096 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1114 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1097The link will expire within 1 hour.</source> 1115The link will expire within 1 hour.</source>
@@ -1101,26 +1119,26 @@ The link will expire within 1 hour.</source>
1101 <trans-unit id="4768749765465246664"> 1119 <trans-unit id="4768749765465246664">
1102 <source>Email</source> 1120 <source>Email</source>
1103 <target>E-post</target> 1121 <target>E-post</target>
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1122
1105 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1123
1106 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1124
1107 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1125
1108 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1126
1109 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1127
1110 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1128
1111 </trans-unit> 1129 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1112 <trans-unit id="3967269098753656610"> 1130 <trans-unit id="3967269098753656610">
1113 <source>Email address</source> 1131 <source>Email address</source>
1114 <target>E-postadress</target> 1132 <target>E-postadress</target>
1115 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1133
1116 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1134
1117 </trans-unit> 1135 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1118 <trans-unit id="7808756054397155068" datatype="html"> 1136 <trans-unit id="7808756054397155068" datatype="html">
1119 <source>Reset</source> 1137 <source>Reset</source>
1120 <target state="translated">Återställ</target> 1138 <target state="translated">Återställ</target>
1121 <note priority="1" from="description">Password reset button</note> 1139 <note priority="1" from="description">Password reset button</note>
1122 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1140
1123 </trans-unit> 1141 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1124 <trans-unit id="4319634264526091601" datatype="html"> 1142 <trans-unit id="4319634264526091601" datatype="html">
1125 <source>on this instance</source> 1143 <source>on this instance</source>
1126 <target state="translated">på den här instansen</target> 1144 <target state="translated">på den här instansen</target>
@@ -1455,9 +1473,9 @@ The link will expire within 1 hour.</source>
1455 <trans-unit id="2308975396733519902"> 1473 <trans-unit id="2308975396733519902">
1456 <source>Create an account</source> 1474 <source>Create an account</source>
1457 <target>Skapa ett konto</target> 1475 <target>Skapa ett konto</target>
1458 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1476
1459 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1477
1460 </trans-unit> 1478 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1461 <trans-unit id="3058024914967508975" datatype="html"> 1479 <trans-unit id="3058024914967508975" datatype="html">
1462 <source>My videos</source> 1480 <source>My videos</source>
1463 <target state="translated">Mina videor</target> 1481 <target state="translated">Mina videor</target>
@@ -1524,10 +1542,10 @@ The link will expire within 1 hour.</source>
1524 <trans-unit id="1504521795586863905" datatype="html"> 1542 <trans-unit id="1504521795586863905" datatype="html">
1525 <source>VIDEOS</source> 1543 <source>VIDEOS</source>
1526 <target state="translated">VIDEOR</target> 1544 <target state="translated">VIDEOR</target>
1527 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1545
1528 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1546
1529 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1547
1530 </trans-unit> 1548 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1531 <trans-unit id="667372110624203230" datatype="html"> 1549 <trans-unit id="667372110624203230" datatype="html">
1532 <source>Import jobs concurrency</source> 1550 <source>Import jobs concurrency</source>
1533 <target state="translated">Samtidiga importjobb</target> 1551 <target state="translated">Samtidiga importjobb</target>
@@ -1606,8 +1624,8 @@ The link will expire within 1 hour.</source>
1606 <trans-unit id="4424964105331349857" datatype="html"> 1624 <trans-unit id="4424964105331349857" datatype="html">
1607 <source>I'm a teapot</source> 1625 <source>I'm a teapot</source>
1608 <target state="translated">Jag är en tekanna</target> 1626 <target state="translated">Jag är en tekanna</target>
1609 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1627
1610 </trans-unit> 1628 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1611 <trans-unit id="1597262876035959248" datatype="html"> 1629 <trans-unit id="1597262876035959248" datatype="html">
1612 <source>That's an error.</source> 1630 <source>That's an error.</source>
1613 <target state="translated">Detta är ett fel.</target> 1631 <target state="translated">Detta är ett fel.</target>
@@ -1700,8 +1718,8 @@ The link will expire within 1 hour.</source>
1700 <trans-unit id="2971365540217107489" datatype="html"> 1718 <trans-unit id="2971365540217107489" datatype="html">
1701 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1719 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1702 <target state="translated">Filen är för stor för servern. Kontakta din administratör om du vill höja gränsen.</target> 1720 <target state="translated">Filen är för stor för servern. Kontakta din administratör om du vill höja gränsen.</target>
1703 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1721
1704 </trans-unit> 1722 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1705 <trans-unit id="5131854469652959713" datatype="html"> 1723 <trans-unit id="5131854469652959713" datatype="html">
1706 <source>GLOBAL SEARCH</source> 1724 <source>GLOBAL SEARCH</source>
1707 <target state="translated">GLOBAL SÖKNING</target> 1725 <target state="translated">GLOBAL SÖKNING</target>
@@ -2445,8 +2463,8 @@ The link will expire within 1 hour.</source>
2445 <trans-unit id="6161604372916832458" datatype="html"> 2463 <trans-unit id="6161604372916832458" datatype="html">
2446 <source>Upload on hold</source> 2464 <source>Upload on hold</source>
2447 <target state="translated">Uppladdning pausad</target> 2465 <target state="translated">Uppladdning pausad</target>
2448 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2466
2449 </trans-unit> 2467 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2450 <trans-unit id="285180972645018518" datatype="html"> 2468 <trans-unit id="285180972645018518" datatype="html">
2451 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2469 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2452 <target state="translated">Uppladdning är inte aktiverat från ditt konto. Om du vill lägga upp videor, måste en administratör låsa upp din videokvot.</target> 2470 <target state="translated">Uppladdning är inte aktiverat från ditt konto. Om du vill lägga upp videor, måste en administratör låsa upp din videokvot.</target>
@@ -3133,11 +3151,7 @@ The link will expire within 1 hour.</source>
3133 <target>ID</target> 3151 <target>ID</target>
3134 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3152 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3135 </trans-unit> 3153 </trans-unit>
3136 <trans-unit id="2265605798180116441"> 3154
3137 <source>Follower handle</source>
3138 <target>Hantera följare</target>
3139 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3140 </trans-unit>
3141 <trans-unit id="5911214550882917183"> 3155 <trans-unit id="5911214550882917183">
3142 <source>State</source> 3156 <source>State</source>
3143 <target>Status</target> 3157 <target>Status</target>
@@ -3205,11 +3219,7 @@ The link will expire within 1 hour.</source>
3205 </target> 3219 </target>
3206 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3220 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3207 </trans-unit> 3221 </trans-unit>
3208 <trans-unit id="6641024648411549335"> 3222
3209 <source>Host</source>
3210 <target>Värd</target>
3211 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3212 </trans-unit>
3213 <trans-unit id="6571718060636962350" datatype="html"> 3223 <trans-unit id="6571718060636962350" datatype="html">
3214 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3224 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3215 <target state="translated">Redundans tillåten <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 3225 <target state="translated">Redundans tillåten <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -3218,9 +3228,9 @@ The link will expire within 1 hour.</source>
3218 <trans-unit id="9160510009013134726" datatype="html"> 3228 <trans-unit id="9160510009013134726" datatype="html">
3219 <source>Unfollow</source> 3229 <source>Unfollow</source>
3220 <target state="translated">Sluta följa</target> 3230 <target state="translated">Sluta följa</target>
3221 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3231
3222 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3232
3223 </trans-unit> 3233 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3224 <trans-unit id="8246779176913476983" datatype="html"> 3234 <trans-unit id="8246779176913476983" datatype="html">
3225 <source>Open instance in a new tab</source> 3235 <source>Open instance in a new tab</source>
3226 <target state="translated">Öppna instansen i en ny flik</target> 3236 <target state="translated">Öppna instansen i en ny flik</target>
@@ -3231,28 +3241,20 @@ The link will expire within 1 hour.</source>
3231 <trans-unit id="9132918641931433659" datatype="html"> 3241 <trans-unit id="9132918641931433659" datatype="html">
3232 <source>No host found matching current filters.</source> 3242 <source>No host found matching current filters.</source>
3233 <target state="translated">Inga värdar matchar de nuvarande filtren.</target> 3243 <target state="translated">Inga värdar matchar de nuvarande filtren.</target>
3234 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3244
3235 </trans-unit> 3245 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3236 <trans-unit id="7274241885665071790" datatype="html"> 3246 <trans-unit id="7274241885665071790" datatype="html">
3237 <source>Your instance is not following anyone.</source> 3247 <source>Your instance is not following anyone.</source>
3238 <target state="translated">Din instans följer inte någon annan.</target> 3248 <target state="translated">Din instans följer inte någon annan.</target>
3239 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3249
3240 </trans-unit> 3250 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3241 <trans-unit id="4774348799569692380" datatype="html"> 3251 <trans-unit id="4774348799569692380" datatype="html">
3242 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3252 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3243 <target state="translated">Visar värd <x id="INTERPOLATION"/> till <x id="INTERPOLATION_1"/> av <x id="INTERPOLATION_2"/></target> 3253 <target state="translated">Visar värd <x id="INTERPOLATION"/> till <x id="INTERPOLATION_1"/> av <x id="INTERPOLATION_2"/></target>
3244 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3254 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3245 </trans-unit> 3255 </trans-unit>
3246 <trans-unit id="6275803119759621687" datatype="html"> 3256
3247 <source>Follow domains</source> 3257
3248 <target state="translated">Följ domäner</target>
3249 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3250 </trans-unit>
3251 <trans-unit id="1268699198448750610" datatype="html">
3252 <source>Follow instances</source>
3253 <target state="translated">Följ instanser</target>
3254 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3255 </trans-unit>
3256 <trans-unit id="9216117865911519658" datatype="html"> 3258 <trans-unit id="9216117865911519658" datatype="html">
3257 <source>Action</source> 3259 <source>Action</source>
3258 <target state="translated">Åtgärd</target> 3260 <target state="translated">Åtgärd</target>
@@ -3302,11 +3304,11 @@ The link will expire within 1 hour.</source>
3302 <trans-unit id="5248717555542428023"> 3304 <trans-unit id="5248717555542428023">
3303 <source>Username</source> 3305 <source>Username</source>
3304 <target>Användarnamn</target> 3306 <target>Användarnamn</target>
3305 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3307
3306 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3308
3307 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3309
3308 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3310
3309 </trans-unit> 3311 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3310 <trans-unit id="5428411040014095392" datatype="html"> 3312 <trans-unit id="5428411040014095392" datatype="html">
3311 <source>e.g. jane_doe</source> 3313 <source>e.g. jane_doe</source>
3312 <target state="translated">t.ex. jane_doe</target> 3314 <target state="translated">t.ex. jane_doe</target>
@@ -3334,9 +3336,9 @@ The link will expire within 1 hour.</source>
3334 <trans-unit id="4145496584631696119"> 3336 <trans-unit id="4145496584631696119">
3335 <source>Role</source> 3337 <source>Role</source>
3336 <target>Roll</target> 3338 <target>Roll</target>
3337 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3339
3338 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3340
3339 </trans-unit> 3341 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3340 <trans-unit id="7046347992315328430" datatype="html"> 3342 <trans-unit id="7046347992315328430" datatype="html">
3341 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3343 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3342 <target state="translated">Omkodning har aktiverats. Videokvoten omfattar endast <x id="START_TAG_STRONG"/>originalfilens<x id="CLOSE_TAG_STRONG"/> storlek. <x id="LINE_BREAK"/> Den här användaren kan ladda upp ungefär <x id="INTERPOLATION"/>. </target> 3344 <target state="translated">Omkodning har aktiverats. Videokvoten omfattar endast <x id="START_TAG_STRONG"/>originalfilens<x id="CLOSE_TAG_STRONG"/> storlek. <x id="LINE_BREAK"/> Den här användaren kan ladda upp ungefär <x id="INTERPOLATION"/>. </target>
@@ -3353,15 +3355,9 @@ The link will expire within 1 hour.</source>
3353 <trans-unit id="2622255144026150901" datatype="html"> 3355 <trans-unit id="2622255144026150901" datatype="html">
3354 <source>Auth plugin</source> 3356 <source>Auth plugin</source>
3355 <target state="translated">Tillägg för autentisering</target> 3357 <target state="translated">Tillägg för autentisering</target>
3356 <context-group purpose="location"> 3358
3357 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3359
3358 <context context-type="linenumber">188</context> 3360 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3359 </context-group>
3360 <context-group purpose="location">
3361 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3362 <context context-type="linenumber">188</context>
3363 </context-group>
3364 </trans-unit>
3365 <trans-unit id="588099657508661970" datatype="html"> 3361 <trans-unit id="588099657508661970" datatype="html">
3366 <source>None (local authentication)</source> 3362 <source>None (local authentication)</source>
3367 <target state="translated">Ingen (lokal autentisering)</target> 3363 <target state="translated">Ingen (lokal autentisering)</target>
@@ -3612,6 +3608,12 @@ The link will expire within 1 hour.</source>
3612 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3608 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3613 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3609 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3614 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3610 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3611 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3612 <source>Follower</source><target state="new">Follower</target>
3613 <context-group purpose="location">
3614 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3615 <context context-type="linenumber">24</context>
3616 </context-group>
3615 </trans-unit> 3617 </trans-unit>
3616 <trans-unit id="4691552465058437520" datatype="html"> 3618 <trans-unit id="4691552465058437520" datatype="html">
3617 <source>Commented video</source> 3619 <source>Commented video</source>
@@ -3913,8 +3915,8 @@ The link will expire within 1 hour.</source>
3913 <trans-unit id="4917252294930256268" datatype="html"> 3915 <trans-unit id="4917252294930256268" datatype="html">
3914 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3916 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3915 <target state="translated">Det verkar som att din server inte använder HTTPS. Webbserver måste ha TLS aktiverat för att följa andra servrar.</target> 3917 <target state="translated">Det verkar som att din server inte använder HTTPS. Webbserver måste ha TLS aktiverat för att följa andra servrar.</target>
3916 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 3918
3917 </trans-unit> 3919 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3918 <trans-unit id="4058814854824495833" datatype="html"> 3920 <trans-unit id="4058814854824495833" datatype="html">
3919 <source>Mute domains</source> 3921 <source>Mute domains</source>
3920 <target state="translated">Ignorera instanser</target> 3922 <target state="translated">Ignorera instanser</target>
@@ -5868,11 +5870,8 @@ color: red;
5868 <trans-unit id="5512878593724620692" datatype="html"> 5870 <trans-unit id="5512878593724620692" datatype="html">
5869 <source>CHANNELS</source> 5871 <source>CHANNELS</source>
5870 <target state="translated">KANALER</target> 5872 <target state="translated">KANALER</target>
5871 <context-group purpose="location"> 5873
5872 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5874 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5873 <context context-type="linenumber">82</context>
5874 </context-group>
5875 </trans-unit>
5876 <trans-unit id="3666829335406793239"> 5875 <trans-unit id="3666829335406793239">
5877 <source>This account does not have channels.</source> 5876 <source>This account does not have channels.</source>
5878 <target>Det här kontot har inga kanaler.</target> 5877 <target>Det här kontot har inga kanaler.</target>
@@ -5911,6 +5910,12 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
5911channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 5910channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
5912 <target state="translated">Vill du verkligen radera <x id="PH" equiv-text="videoChannel.displayName"/>? Det kommer att radera <x id="PH_1" equiv-text="videoChannel.videosCount"/> videor uppladdade till kanalen, och du kan inte skapa en kanal med samma namn (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target> 5911 <target state="translated">Vill du verkligen radera <x id="PH" equiv-text="videoChannel.displayName"/>? Det kommer att radera <x id="PH_1" equiv-text="videoChannel.videosCount"/> videor uppladdade till kanalen, och du kan inte skapa en kanal med samma namn (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target>
5913 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 5912 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
5913 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
5914 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5915 <context-group purpose="location">
5916 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5917 <context context-type="linenumber">48</context>
5918 </context-group>
5914 </trans-unit> 5919 </trans-unit>
5915 <trans-unit id="5387007581996837469" datatype="html"> 5920 <trans-unit id="5387007581996837469" datatype="html">
5916 <source>My Channels</source> 5921 <source>My Channels</source>
@@ -6430,13 +6435,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6430 <trans-unit id="6979021199788941693"> 6435 <trans-unit id="6979021199788941693">
6431 <source>Your message has been sent.</source> 6436 <source>Your message has been sent.</source>
6432 <target>Ditt meddelande har skickats.</target> 6437 <target>Ditt meddelande har skickats.</target>
6433 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group> 6438
6434 </trans-unit> 6439 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6435 <trans-unit id="2072135752262464360"> 6440 <trans-unit id="2072135752262464360">
6436 <source>You already sent this form recently</source> 6441 <source>You already sent this form recently</source>
6437 <target>Du har redan skickat detta formulär nyligen</target> 6442 <target>Du har redan skickat detta formulär nyligen</target>
6438 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group> 6443
6439 </trans-unit> 6444 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6440 <trans-unit id="819067926858619041" datatype="html"> 6445 <trans-unit id="819067926858619041" datatype="html">
6441 <source>Account videos</source> 6446 <source>Account videos</source>
6442 <target state="translated">Kontots videor</target> 6447 <target state="translated">Kontots videor</target>
@@ -6481,13 +6486,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6481 <target state="translated">Kontot har 6486 <target state="translated">Kontot har
6482 <x id="PH"/> direktföljare 6487 <x id="PH"/> direktföljare
6483 </target> 6488 </target>
6484 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6489
6485 </trans-unit> 6490 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6486 <trans-unit id="6250999352462648289" datatype="html"> 6491 <trans-unit id="6250999352462648289" datatype="html">
6487 <source>Report this account</source> 6492 <source>Report this account</source>
6488 <target state="translated">Anmäl det här kontot</target> 6493 <target state="translated">Anmäl det här kontot</target>
6489 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6494
6490 </trans-unit> 6495 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6491 <trans-unit id="1504521795586863905" datatype="html"> 6496 <trans-unit id="1504521795586863905" datatype="html">
6492 <source>VIDEOS</source> 6497 <source>VIDEOS</source>
6493 <target state="translated">VIDEOR</target> 6498 <target state="translated">VIDEOR</target>
@@ -6497,31 +6502,21 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6497 <trans-unit id="25349740244798533"> 6502 <trans-unit id="25349740244798533">
6498 <source>Username copied</source> 6503 <source>Username copied</source>
6499 <target>Användarnamn kopierat</target> 6504 <target>Användarnamn kopierat</target>
6500 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6505
6501 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6506
6502 </trans-unit> 6507 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6503 <trans-unit id="9221735175659318025" datatype="html"> 6508 <trans-unit id="9221735175659318025" datatype="html">
6504 <source>1 subscriber</source> 6509 <source>1 subscriber</source>
6505 <target state="translated">1 prenumerant</target> 6510 <target state="translated">1 prenumerant</target>
6506 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6511
6507 </trans-unit> 6512 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6508 <trans-unit id="4097331874769079975" datatype="html"> 6513 <trans-unit id="4097331874769079975" datatype="html">
6509 <source><x id="PH"/> subscribers</source> 6514 <source><x id="PH"/> subscribers</source>
6510 <target state="translated"><x id="PH"/> prenumeranter</target> 6515 <target state="translated"><x id="PH"/> prenumeranter</target>
6511 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6516
6512 </trans-unit> 6517 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6513 <trans-unit id="4682675125751819107" datatype="html"> 6518
6514 <source>Instances you follow</source> 6519
6515 <target state="translated">Instanser du följer</target>
6516 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6517 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6518 </trans-unit>
6519 <trans-unit id="8899833753704589712" datatype="html">
6520 <source>Instances following you</source>
6521 <target state="translated">Instanser som följer dig</target>
6522 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6523 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6524 </trans-unit>
6525 <trans-unit id="1035838766454786107" datatype="html"> 6520 <trans-unit id="1035838766454786107" datatype="html">
6526 <source>Audio-only</source> 6521 <source>Audio-only</source>
6527 <target state="translated">Endast ljud</target> 6522 <target state="translated">Endast ljud</target>
@@ -6571,6 +6566,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6571 <source>Auto (via ffmpeg)</source> 6566 <source>Auto (via ffmpeg)</source>
6572 <target>Automatiskt (via ffmpeg)</target> 6567 <target>Automatiskt (via ffmpeg)</target>
6573 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6568 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6569 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6570 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6571 <context-group purpose="location">
6572 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6573 <context context-type="linenumber">3</context>
6574 </context-group>
6574 </trans-unit> 6575 </trans-unit>
6575 <trans-unit id="931255636742351800" datatype="html"> 6576 <trans-unit id="931255636742351800" datatype="html">
6576 <source>No limit</source> 6577 <source>No limit</source>
@@ -6719,18 +6720,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6719 <trans-unit id="2127446333083057097" datatype="html"> 6720 <trans-unit id="2127446333083057097" datatype="html">
6720 <source>Domain is required.</source> 6721 <source>Domain is required.</source>
6721 <target state="translated">Domän måste uppges.</target> 6722 <target state="translated">Domän måste uppges.</target>
6722 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6723
6723 </trans-unit> 6724 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6724 <trans-unit id="6780793142903080663" datatype="html"> 6725 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6725 <source>Domains entered are invalid.</source> 6726 <context-group purpose="location">
6726 <target state="translated">Angivna domäner är ogiltiga.</target> 6727 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6727 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6728 <context context-type="linenumber">93</context>
6728 </trans-unit> 6729 </context-group>
6729 <trans-unit id="5886492514458202177" datatype="html"> 6730 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6730 <source>Domains entered contain duplicates.</source> 6731 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6731 <target state="translated">Domänerna innehåller dubbletter.</target> 6732 <context-group purpose="location">
6732 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6733 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6734 <context context-type="linenumber">94</context>
6735 </context-group>
6736 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6737 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6738 <context-group purpose="location">
6739 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6740 <context context-type="linenumber">102</context>
6741 </context-group>
6742 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6743 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6744 <context-group purpose="location">
6745 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6746 <context context-type="linenumber">103</context>
6747 </context-group>
6733 </trans-unit> 6748 </trans-unit>
6749
6750
6734 <trans-unit id="240806681889331244"> 6751 <trans-unit id="240806681889331244">
6735 <source>Unlimited</source> 6752 <source>Unlimited</source>
6736 <target>Obegränsat</target> 6753 <target>Obegränsat</target>
@@ -6890,24 +6907,50 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6890 <x id="PH"/> har tagits bort från listan över instansföljare 6907 <x id="PH"/> har tagits bort från listan över instansföljare
6891 </target> 6908 </target>
6892 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6909 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6910 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6911 <source>Follow</source><target state="new">Follow</target>
6912 <context-group purpose="location">
6913 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6914 <context context-type="linenumber">3</context>
6915 </context-group>
6916 <context-group purpose="location">
6917 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6918 <context context-type="linenumber">37</context>
6919 </context-group>
6920 <context-group purpose="location">
6921 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6922 <context context-type="linenumber">18</context>
6923 </context-group>
6924 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6925 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6926 <context-group purpose="location">
6927 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6928 <context context-type="linenumber">11</context>
6929 </context-group>
6893 </trans-unit> 6930 </trans-unit>
6894 <trans-unit id="2740793005745065895"> 6931 <trans-unit id="2740793005745065895">
6895 <source><x id="PH"/> is not valid </source> 6932 <source><x id="PH"/> is not valid </source>
6896 <target> 6933 <target>
6897 <x id="PH"/> är inte giltig 6934 <x id="PH"/> är inte giltig
6898 </target> 6935 </target>
6899 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6936
6900 </trans-unit> 6937 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6901 <trans-unit id="2355066641781598196"> 6938 <trans-unit id="2355066641781598196">
6902 <source>Follow request(s) sent!</source> 6939 <source>Follow request(s) sent!</source>
6903 <target>Följningsförfrågan / förfrågningar skickad!</target> 6940 <target>Följningsförfrågan / förfrågningar skickad!</target>
6904 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6941
6942 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6943 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6944 <context-group purpose="location">
6945 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6946 <context context-type="linenumber">3</context>
6947 </context-group>
6905 </trans-unit> 6948 </trans-unit>
6906 <trans-unit id="4245720728052819482"> 6949 <trans-unit id="4245720728052819482">
6907 <source>Do you really want to unfollow <x id="PH"/>?</source> 6950 <source>Do you really want to unfollow <x id="PH"/>?</source>
6908 <target>Vill du verkligen sluta följa <x id="PH"/>?</target> 6951 <target>Vill du verkligen sluta följa <x id="PH"/>?</target>
6909 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6952
6910 </trans-unit> 6953 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6911 <trans-unit id="9160510009013134726"> 6954 <trans-unit id="9160510009013134726">
6912 <source>Unfollow</source> 6955 <source>Unfollow</source>
6913 <target>Sluta följa</target> 6956 <target>Sluta följa</target>
@@ -6916,8 +6959,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6916 <trans-unit id="3935234189109112926"> 6959 <trans-unit id="3935234189109112926">
6917 <source>You are not following <x id="PH"/> anymore.</source> 6960 <source>You are not following <x id="PH"/> anymore.</source>
6918 <target>Du följer inte <x id="PH"/> längre.</target> 6961 <target>Du följer inte <x id="PH"/> längre.</target>
6919 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6962
6920 </trans-unit> 6963 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6921 <trans-unit id="2593763089859685916"> 6964 <trans-unit id="2593763089859685916">
6922 <source>enabled</source> 6965 <source>enabled</source>
6923 <target>aktiverad</target> 6966 <target>aktiverad</target>
@@ -7389,9 +7432,9 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7389 <trans-unit id="1519954996184640001"> 7432 <trans-unit id="1519954996184640001">
7390 <source>Error</source> 7433 <source>Error</source>
7391 <target>Fel</target> 7434 <target>Fel</target>
7392 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7435
7393 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7436
7394 </trans-unit> 7437 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7395 <trans-unit id="5076187961693950167" datatype="html"> 7438 <trans-unit id="5076187961693950167" datatype="html">
7396 <source>Standard logs</source> 7439 <source>Standard logs</source>
7397 <target state="translated">Standardloggar</target> 7440 <target state="translated">Standardloggar</target>
@@ -7432,16 +7475,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7432 <target>Uppdatera användarens lösenord</target> 7475 <target>Uppdatera användarens lösenord</target>
7433 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7476 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7434 </trans-unit> 7477 </trans-unit>
7435 <trans-unit id="177544274549739411" datatype="html"> 7478
7436 <source>Following list</source> 7479
7437 <target state="translated">Lista över följda instanser</target>
7438 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7439 </trans-unit>
7440 <trans-unit id="8092429110007204784" datatype="html">
7441 <source>Followers list</source>
7442 <target state="translated">Lista över följare</target>
7443 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7444 </trans-unit>
7445 <trans-unit id="780323526182667308" datatype="html"> 7480 <trans-unit id="780323526182667308" datatype="html">
7446 <source>User <x id="PH"/> updated.</source> 7481 <source>User <x id="PH"/> updated.</source>
7447 <target state="translated">Användaren <x id="PH"/> uppdaterad.</target> 7482 <target state="translated">Användaren <x id="PH"/> uppdaterad.</target>
@@ -7477,16 +7512,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7477 <target state="translated">Federation</target> 7512 <target state="translated">Federation</target>
7478 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7513 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7479 </trans-unit> 7514 </trans-unit>
7480 <trans-unit id="4682675125751819107" datatype="html"> 7515
7481 <source>Instances you follow</source> 7516
7482 <target state="translated">Instanser du följer</target>
7483 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7484 </trans-unit>
7485 <trans-unit id="8899833753704589712" datatype="html">
7486 <source>Instances following you</source>
7487 <target state="translated">Instanser som följer dig</target>
7488 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7489 </trans-unit>
7490 <trans-unit id="3767259920053407667" datatype="html"> 7517 <trans-unit id="3767259920053407667" datatype="html">
7491 <source>Videos will be deleted, comments will be tombstoned.</source> 7518 <source>Videos will be deleted, comments will be tombstoned.</source>
7492 <target state="translated">Videorna kommer raderas och kommentarerna arkiverade.</target> 7519 <target state="translated">Videorna kommer raderas och kommentarerna arkiverade.</target>
@@ -7517,6 +7544,24 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7517 <target>Markera e-post som verifierad</target> 7544 <target>Markera e-post som verifierad</target>
7518 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7545 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7519 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7546 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7547 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7548 <source>Created</source><target state="new">Created</target>
7549 <context-group purpose="location">
7550 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7551 <context context-type="linenumber">115</context>
7552 </context-group>
7553 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7554 <source>Daily quota</source><target state="new">Daily quota</target>
7555 <context-group purpose="location">
7556 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7557 <context context-type="linenumber">120</context>
7558 </context-group>
7559 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7560 <source>Last login</source><target state="new">Last login</target>
7561 <context-group purpose="location">
7562 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7563 <context context-type="linenumber">122</context>
7564 </context-group>
7520 </trans-unit> 7565 </trans-unit>
7521 <trans-unit id="3403978719736970622"> 7566 <trans-unit id="3403978719736970622">
7522 <source>You cannot ban root.</source> 7567 <source>You cannot ban root.</source>
@@ -7821,13 +7866,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7821 <trans-unit id="1137937154872046253"> 7866 <trans-unit id="1137937154872046253">
7822 <source>Video channel <x id="PH"/> created.</source> 7867 <source>Video channel <x id="PH"/> created.</source>
7823 <target>Kanalen <x id="PH"/> har skapats.</target> 7868 <target>Kanalen <x id="PH"/> har skapats.</target>
7824 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 7869
7825 </trans-unit> 7870 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7826 <trans-unit id="8723777130353305761"> 7871 <trans-unit id="8723777130353305761">
7827 <source>This name already exists on this instance.</source> 7872 <source>This name already exists on this instance.</source>
7828 <target>Namnet finns redan på den här instansen.</target> 7873 <target>Namnet finns redan på den här instansen.</target>
7829 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 7874
7830 </trans-unit> 7875 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7831 <trans-unit id="7589345916094713536"> 7876 <trans-unit id="7589345916094713536">
7832 <source>Video channel <x id="PH"/> updated.</source> 7877 <source>Video channel <x id="PH"/> updated.</source>
7833 <target>Kanalen <x id="PH"/> har uppdaterats.</target> 7878 <target>Kanalen <x id="PH"/> har uppdaterats.</target>
@@ -7848,11 +7893,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7848 <target state="translated">Baneret har raderats.</target> 7893 <target state="translated">Baneret har raderats.</target>
7849 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 7894 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
7850 </trans-unit> 7895 </trans-unit>
7851 <trans-unit id="2575302837003821736"> 7896
7852 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7853 <target>Uppge kanalens visningsnamn (<x id="PH"/>) för att bekräfta</target>
7854 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
7855 </trans-unit>
7856 <trans-unit id="624066830180032195"> 7897 <trans-unit id="624066830180032195">
7857 <source>Video channel <x id="PH"/> deleted.</source> 7898 <source>Video channel <x id="PH"/> deleted.</source>
7858 <target>Kanalen <x id="PH"/> har raderats.</target> 7899 <target>Kanalen <x id="PH"/> har raderats.</target>
@@ -8004,6 +8045,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8004 <source>Ownership change request sent.</source> 8045 <source>Ownership change request sent.</source>
8005 <target>Förfrågan om byte av ägarskap har skickats.</target> 8046 <target>Förfrågan om byte av ägarskap har skickats.</target>
8006 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8047 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8048 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8049 <source>Sort by</source><target state="new">Sort by</target>
8050 <context-group purpose="location">
8051 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8052 <context context-type="linenumber">26</context>
8053 </context-group>
8007 </trans-unit> 8054 </trans-unit>
8008 <trans-unit id="3245220240937722814"> 8055 <trans-unit id="3245220240937722814">
8009 <source>My channels</source> 8056 <source>My channels</source>
@@ -8102,7 +8149,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8102 <target>Prenumerera på kontot</target> 8149 <target>Prenumerera på kontot</target>
8103 8150
8104 8151
8105 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8152 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8106 <trans-unit id="3131904093925601441" datatype="html"> 8153 <trans-unit id="3131904093925601441" datatype="html">
8107 <source>PLAYLISTS</source> 8154 <source>PLAYLISTS</source>
8108 <target state="translated">SPELLISTOR</target> 8155 <target state="translated">SPELLISTOR</target>
@@ -8149,34 +8196,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8149 <trans-unit id="3779524668013120370"> 8196 <trans-unit id="3779524668013120370">
8150 <source>Go to my subscriptions</source> 8197 <source>Go to my subscriptions</source>
8151 <target>Gå till mina prenumerationer</target> 8198 <target>Gå till mina prenumerationer</target>
8152 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8199
8153 </trans-unit> 8200 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8154 <trans-unit id="1136469849928650779"> 8201 <trans-unit id="1136469849928650779">
8155 <source>Go to my videos</source> 8202 <source>Go to my videos</source>
8156 <target>Gå till mina videor</target> 8203 <target>Gå till mina videor</target>
8157 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8204
8158 </trans-unit> 8205 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8159 <trans-unit id="7836683738999600376"> 8206 <trans-unit id="7836683738999600376">
8160 <source>Go to my imports</source> 8207 <source>Go to my imports</source>
8161 <target>Gå till mina importeringar</target> 8208 <target>Gå till mina importeringar</target>
8162 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8209
8163 </trans-unit> 8210 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8164 <trans-unit id="7511292153332773503"> 8211 <trans-unit id="7511292153332773503">
8165 <source>Go to my channels</source> 8212 <source>Go to my channels</source>
8166 <target>Gå till mina kanaler</target> 8213 <target>Gå till mina kanaler</target>
8167 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8214
8168 </trans-unit> 8215 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8169 <trans-unit id="2013324644839511073" datatype="html"> 8216 <trans-unit id="2013324644839511073" datatype="html">
8170 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8217 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8171Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8218Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8172 <target state="translated">Kan inte hämta OAuth Client-uppgifter: <x id="PH" equiv-text="error.text"/>. Försäkra dig om att du har konfigurerat PeerTube korrekt (i config-katalogen), speciellt ”webserver”-sektionen.</target> 8219 <target state="translated">Kan inte hämta OAuth Client-uppgifter: <x id="PH" equiv-text="error.text"/>. Försäkra dig om att du har konfigurerat PeerTube korrekt (i config-katalogen), speciellt ”webserver”-sektionen.</target>
8173 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8220
8174 </trans-unit> 8221 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8175 <trans-unit id="375263728166936544"> 8222 <trans-unit id="375263728166936544">
8176 <source>You need to reconnect.</source> 8223 <source>You need to reconnect.</source>
8177 <target>Du måste återansluta.</target> 8224 <target>Du måste återansluta.</target>
8178 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8225
8179 </trans-unit> 8226 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8180 <trans-unit id="2206638022166154361"> 8227 <trans-unit id="2206638022166154361">
8181 <source>Keyboard Shortcuts:</source> 8228 <source>Keyboard Shortcuts:</source>
8182 <target>Kortkommandon:</target> 8229 <target>Kortkommandon:</target>
@@ -8189,6 +8236,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8189 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8236 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8190 <context context-type="linenumber">98</context> 8237 <context context-type="linenumber">98</context>
8191 </context-group> 8238 </context-group>
8239 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8240 <source>In my library</source><target state="new">In my library</target>
8241 <context-group purpose="location">
8242 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8243 <context context-type="linenumber">104</context>
8244 </context-group>
8192 </trans-unit> 8245 </trans-unit>
8193 <trans-unit id="232050922346936574" datatype="html"> 8246 <trans-unit id="232050922346936574" datatype="html">
8194 <source>Trending</source> 8247 <source>Trending</source>
@@ -8217,38 +8270,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8217 <trans-unit id="1266887509445371246"> 8270 <trans-unit id="1266887509445371246">
8218 <source>Incorrect username or password.</source> 8271 <source>Incorrect username or password.</source>
8219 <target>Felaktigt användarnamn eller lösenord.</target> 8272 <target>Felaktigt användarnamn eller lösenord.</target>
8220 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8273
8221 </trans-unit> 8274 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8222 <trans-unit id="6974874606619467663" datatype="html"> 8275 <trans-unit id="6974874606619467663" datatype="html">
8223 <source>Your account is blocked.</source> 8276 <source>Your account is blocked.</source>
8224 <target state="translated">Ditt konto har blockerats.</target> 8277 <target state="translated">Ditt konto har blockerats.</target>
8225 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8278
8226 </trans-unit> 8279 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8227 <trans-unit id="7939914198003891823" datatype="html"> 8280 <trans-unit id="7939914198003891823" datatype="html">
8228 <source>any language</source> 8281 <source>any language</source>
8229 <target state="translated">vilket språk som helst</target> 8282 <target state="translated">vilket språk som helst</target>
8230 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8283
8231 </trans-unit> 8284 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8232 <trans-unit id="5633144232269377096" datatype="html"> 8285 <trans-unit id="5633144232269377096" datatype="html">
8233 <source>hide</source> 8286 <source>hide</source>
8234 <target state="translated">dölj</target> 8287 <target state="translated">dölj</target>
8235 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8288
8236 </trans-unit> 8289 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8237 <trans-unit id="8603861867909474404" datatype="html"> 8290 <trans-unit id="8603861867909474404" datatype="html">
8238 <source>blur</source> 8291 <source>blur</source>
8239 <target state="translated">gör suddig</target> 8292 <target state="translated">gör suddig</target>
8240 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8293
8241 </trans-unit> 8294 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8242 <trans-unit id="4534458451100881847" datatype="html"> 8295 <trans-unit id="4534458451100881847" datatype="html">
8243 <source>display</source> 8296 <source>display</source>
8244 <target state="translated">visa</target> 8297 <target state="translated">visa</target>
8245 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8298
8246 </trans-unit> 8299 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8247 <trans-unit id="4467323362722952678" datatype="html"> 8300 <trans-unit id="4467323362722952678" datatype="html">
8248 <source>Unknown</source> 8301 <source>Unknown</source>
8249 <target state="translated">Okänd</target> 8302 <target state="translated">Okänd</target>
8250 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8303
8251 </trans-unit> 8304 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8252 <trans-unit id="8781423666414310853"> 8305 <trans-unit id="8781423666414310853">
8253 <source>Your password has been successfully reset!</source> 8306 <source>Your password has been successfully reset!</source>
8254 <target>Ditt lösenord har återställts!</target> 8307 <target>Ditt lösenord har återställts!</target>
@@ -9834,18 +9887,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9834 <trans-unit id="968295009933361070"> 9887 <trans-unit id="968295009933361070">
9835 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9888 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9836 <target>För många försök, vänligen försök igen om <x id="PH"/> minuter.</target> 9889 <target>För många försök, vänligen försök igen om <x id="PH"/> minuter.</target>
9837 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9890
9838 </trans-unit> 9891 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9839 <trans-unit id="4965472196059235310"> 9892 <trans-unit id="4965472196059235310">
9840 <source>Too many attempts, please try again later.</source> 9893 <source>Too many attempts, please try again later.</source>
9841 <target>För många försök, vänligen försök igen senare.</target> 9894 <target>För många försök, vänligen försök igen senare.</target>
9842 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9895
9843 </trans-unit> 9896 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9844 <trans-unit id="1693549688987384699"> 9897 <trans-unit id="1693549688987384699">
9845 <source>Server error. Please retry later.</source> 9898 <source>Server error. Please retry later.</source>
9846 <target>Serverfel, försök gärna igen om en stund.</target> 9899 <target>Serverfel, försök gärna igen om en stund.</target>
9847 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9900
9848 </trans-unit> 9901 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9849 <trans-unit id="5927402622550505067" datatype="html"> 9902 <trans-unit id="5927402622550505067" datatype="html">
9850 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9903 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9851 <target state="translated">Prenumererar på samtliga kanaler tillhörande <x id="PH"/>. Du kommer underrättas om alla nya videor.</target> 9904 <target state="translated">Prenumererar på samtliga kanaler tillhörande <x id="PH"/>. Du kommer underrättas om alla nya videor.</target>
@@ -10438,35 +10491,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10438 <trans-unit id="3284171506518522275"> 10491 <trans-unit id="3284171506518522275">
10439 <source>Your video was uploaded to your account and is private.</source> 10492 <source>Your video was uploaded to your account and is private.</source>
10440 <target>Din video har laddats upp till ditt konto och är privat.</target> 10493 <target>Din video har laddats upp till ditt konto och är privat.</target>
10441 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10494
10442 </trans-unit> 10495 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10443 <trans-unit id="5699822024600815733"> 10496 <trans-unit id="5699822024600815733">
10444 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10497 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10445 <target>Men associerad data (taggar, beskrivning …) kommer försvinna, är du säker på att du vill lämna sidan?</target> 10498 <target>Men associerad data (taggar, beskrivning …) kommer försvinna, är du säker på att du vill lämna sidan?</target>
10446 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10499
10447 </trans-unit> 10500 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10448 <trans-unit id="1219739004043110649"> 10501 <trans-unit id="1219739004043110649">
10449 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10502 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10450 <target>Din video har inte laddats upp än, vill du lämna sidan?</target> 10503 <target>Din video har inte laddats upp än, vill du lämna sidan?</target>
10451 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10504
10452 </trans-unit> 10505 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10453 <trans-unit id="6932865105766151309" datatype="html"> 10506 <trans-unit id="6932865105766151309" datatype="html">
10454 <source>Upload</source> 10507 <source>Upload</source>
10455 <target state="translated">Ladda upp</target> 10508 <target state="translated">Ladda upp</target>
10456 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10509
10457 </trans-unit> 10510 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10458 <trans-unit id="8278735427925094503"> 10511 <trans-unit id="8278735427925094503">
10459 <source>Upload <x id="PH"/> </source> 10512 <source>Upload <x id="PH"/> </source>
10460 <target>Ladda upp 10513 <target>Ladda upp
10461 <x id="PH"/> 10514 <x id="PH"/>
10462 </target> 10515 </target>
10463 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10516
10464 </trans-unit> 10517 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10465 <trans-unit id="5981816353437801748"> 10518 <trans-unit id="5981816353437801748">
10466 <source>Video published.</source> 10519 <source>Video published.</source>
10467 <target>Videon har publicerats.</target> 10520 <target>Videon har publicerats.</target>
10468 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10521
10469 </trans-unit> 10522 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10470 <trans-unit id="764164089183618119"> 10523 <trans-unit id="764164089183618119">
10471 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10524 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10472 <target>Du har gjort ändringar som inte sparats! Om du lämnar nu kommer de förkastas.</target> 10525 <target>Du har gjort ändringar som inte sparats! Om du lämnar nu kommer de förkastas.</target>
@@ -10513,28 +10566,28 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10513 <trans-unit id="961774488937452220" datatype="html"> 10566 <trans-unit id="961774488937452220" datatype="html">
10514 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10567 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10515 <target state="translated">Den här videon finns inte på din instans. Vill du bli hänvisad till ursprungsinstansen &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10568 <target state="translated">Den här videon finns inte på din instans. Vill du bli hänvisad till ursprungsinstansen &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10516 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group> 10569
10517 </trans-unit> 10570 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10518 <trans-unit id="5761611056224181752" datatype="html"> 10571 <trans-unit id="5761611056224181752" datatype="html">
10519 <source>Redirection</source> 10572 <source>Redirection</source>
10520 <target state="translated">Omdirigering</target> 10573 <target state="translated">Omdirigering</target>
10521 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group> 10574
10522 </trans-unit> 10575 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10523 <trans-unit id="8858527736400081688"> 10576 <trans-unit id="8858527736400081688">
10524 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10577 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10525 <target>Den här videon innehåller oförbehållsamt innehåll eller innehåll skapat för vuxna. Är du säker på att du vill se den?</target> 10578 <target>Den här videon innehåller oförbehållsamt innehåll eller innehåll skapat för vuxna. Är du säker på att du vill se den?</target>
10526 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group> 10579
10527 </trans-unit> 10580 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10528 <trans-unit id="3937119019020041049"> 10581 <trans-unit id="3937119019020041049">
10529 <source>Mature or explicit content</source> 10582 <source>Mature or explicit content</source>
10530 <target>Oförbehållsamt innehåll eller innehåll skapat för vuxna</target> 10583 <target>Oförbehållsamt innehåll eller innehåll skapat för vuxna</target>
10531 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group> 10584
10532 </trans-unit> 10585 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10533 <trans-unit id="1755474755114288376" datatype="html"> 10586 <trans-unit id="1755474755114288376" datatype="html">
10534 <source>Up Next</source> 10587 <source>Up Next</source>
10535 <target state="translated">Kommer härnäst</target> 10588 <target state="translated">Kommer härnäst</target>
10536 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group> 10589
10537 </trans-unit> 10590 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10538 <trans-unit id="2159130950882492111" datatype="html"> 10591 <trans-unit id="2159130950882492111" datatype="html">
10539 <source>Cancel</source> 10592 <source>Cancel</source>
10540 <target state="translated">Avbryt</target> 10593 <target state="translated">Avbryt</target>
@@ -10543,63 +10596,63 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10543 <trans-unit id="3354816756665089864" datatype="html"> 10596 <trans-unit id="3354816756665089864" datatype="html">
10544 <source>Autoplay is suspended</source> 10597 <source>Autoplay is suspended</source>
10545 <target state="translated">Automatisk uppspelning är upphävd</target> 10598 <target state="translated">Automatisk uppspelning är upphävd</target>
10546 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group> 10599
10547 </trans-unit> 10600 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10548 <trans-unit id="7895294730547405228" datatype="html"> 10601 <trans-unit id="7895294730547405228" datatype="html">
10549 <source>Enter/exit fullscreen (requires player focus)</source> 10602 <source>Enter/exit fullscreen (requires player focus)</source>
10550 <target state="translated">Slå av eller på helskärmsläget (spelaren måste vara markerad)</target> 10603 <target state="translated">Slå av eller på helskärmsläget (spelaren måste vara markerad)</target>
10551 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group> 10604
10552 </trans-unit> 10605 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10553 <trans-unit id="7618388257165864759" datatype="html"> 10606 <trans-unit id="7618388257165864759" datatype="html">
10554 <source>Play/Pause the video (requires player focus)</source> 10607 <source>Play/Pause the video (requires player focus)</source>
10555 <target state="translated">Spela eller pausa videon (spelaren måste vara markerad)</target> 10608 <target state="translated">Spela eller pausa videon (spelaren måste vara markerad)</target>
10556 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group> 10609
10557 </trans-unit> 10610 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10558 <trans-unit id="7761890399634216630" datatype="html"> 10611 <trans-unit id="7761890399634216630" datatype="html">
10559 <source>Mute/unmute the video (requires player focus)</source> 10612 <source>Mute/unmute the video (requires player focus)</source>
10560 <target state="translated">Slå av eller på ljudet (spelaren måste vara markerad)</target> 10613 <target state="translated">Slå av eller på ljudet (spelaren måste vara markerad)</target>
10561 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group> 10614
10562 </trans-unit> 10615 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10563 <trans-unit id="5996585232248234904" datatype="html"> 10616 <trans-unit id="5996585232248234904" datatype="html">
10564 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10617 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10565 <target state="translated">Hoppa till en position i videon: 0 motsvarar 0 % av den totala speltiden och 9 motsvarar 90 % (spelaren måste vara markerad)</target> 10618 <target state="translated">Hoppa till en position i videon: 0 motsvarar 0 % av den totala speltiden och 9 motsvarar 90 % (spelaren måste vara markerad)</target>
10566 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group> 10619
10567 </trans-unit> 10620 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10568 <trans-unit id="3748765405903319998" datatype="html"> 10621 <trans-unit id="3748765405903319998" datatype="html">
10569 <source>Increase the volume (requires player focus)</source> 10622 <source>Increase the volume (requires player focus)</source>
10570 <target state="translated">Öka volymen (spelaren måste vara markerad)</target> 10623 <target state="translated">Öka volymen (spelaren måste vara markerad)</target>
10571 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group> 10624
10572 </trans-unit> 10625 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10573 <trans-unit id="5810704036407159982" datatype="html"> 10626 <trans-unit id="5810704036407159982" datatype="html">
10574 <source>Decrease the volume (requires player focus)</source> 10627 <source>Decrease the volume (requires player focus)</source>
10575 <target state="translated">Minska volymen (spelaren måste vara markerad)</target> 10628 <target state="translated">Minska volymen (spelaren måste vara markerad)</target>
10576 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group> 10629
10577 </trans-unit> 10630 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10578 <trans-unit id="2622048822548065691" datatype="html"> 10631 <trans-unit id="2622048822548065691" datatype="html">
10579 <source>Seek the video forward (requires player focus)</source> 10632 <source>Seek the video forward (requires player focus)</source>
10580 <target state="translated">Spola videon framåt (spelaren måste vara markerad)</target> 10633 <target state="translated">Spola videon framåt (spelaren måste vara markerad)</target>
10581 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group> 10634
10582 </trans-unit> 10635 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10583 <trans-unit id="6540078205109221153" datatype="html"> 10636 <trans-unit id="6540078205109221153" datatype="html">
10584 <source>Seek the video backward (requires player focus)</source> 10637 <source>Seek the video backward (requires player focus)</source>
10585 <target state="translated">Spola videon bakåt (spelaren måste vara markerad)</target> 10638 <target state="translated">Spola videon bakåt (spelaren måste vara markerad)</target>
10586 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group> 10639
10587 </trans-unit> 10640 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10588 <trans-unit id="1956491957766210808" datatype="html"> 10641 <trans-unit id="1956491957766210808" datatype="html">
10589 <source>Increase playback rate (requires player focus)</source> 10642 <source>Increase playback rate (requires player focus)</source>
10590 <target state="translated">Öka uppspelningshastigheten (spelaren måste vara markerad)</target> 10643 <target state="translated">Öka uppspelningshastigheten (spelaren måste vara markerad)</target>
10591 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group> 10644
10592 </trans-unit> 10645 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10593 <trans-unit id="5495529997674803186" datatype="html"> 10646 <trans-unit id="5495529997674803186" datatype="html">
10594 <source>Decrease playback rate (requires player focus)</source> 10647 <source>Decrease playback rate (requires player focus)</source>
10595 <target state="translated">Minska uppspelningshastigheten (spelaren måste vara markerad)</target> 10648 <target state="translated">Minska uppspelningshastigheten (spelaren måste vara markerad)</target>
10596 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group> 10649
10597 </trans-unit> 10650 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10598 <trans-unit id="3178343147230721210" datatype="html"> 10651 <trans-unit id="3178343147230721210" datatype="html">
10599 <source>Navigate in the video frame by frame (requires player focus)</source> 10652 <source>Navigate in the video frame by frame (requires player focus)</source>
10600 <target state="translated">Föregående eller nästkommande bildruta (spelaren måste vara markerad)</target> 10653 <target state="translated">Föregående eller nästkommande bildruta (spelaren måste vara markerad)</target>
10601 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group> 10654
10602 </trans-unit> 10655 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10603 <trans-unit id="8025996572234182184"> 10656 <trans-unit id="8025996572234182184">
10604 <source>Like the video</source> 10657 <source>Like the video</source>
10605 <target>Gilla videon</target> 10658 <target>Gilla videon</target>
diff --git a/client/src/locale/angular.ta.xlf b/client/src/locale/angular.ta.xlf
index 4dd2b73cd..e7757cc6b 100644
--- a/client/src/locale/angular.ta.xlf
+++ b/client/src/locale/angular.ta.xlf
@@ -307,7 +307,7 @@
307 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 307 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
308 </target> 308 </target>
309 309
310 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html"> 310 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit><trans-unit id="1486537403020619891" datatype="html">
311 <source>My watch history</source><target state="new">My watch history</target> 311 <source>My watch history</source><target state="new">My watch history</target>
312 312
313 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 313 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-history/my-history.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
@@ -388,12 +388,12 @@
388 388
389 389
390 390
391 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 391 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
392 <trans-unit id="1006562256968398209" datatype="html"> 392 <trans-unit id="1006562256968398209" datatype="html">
393 <source>video</source> 393 <source>video</source>
394 <target state="new">video</target> 394 <target state="new">video</target>
395 395
396 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 396 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
397 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 397 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
398 398
399 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 399 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -455,10 +455,10 @@
455 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 455 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
456 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 456 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
457 457
458 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 458 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
459 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 459 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
460 460
461 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html"> 461 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit><trans-unit id="5235042777215655908" datatype="html">
462 <source>subtitles</source><target state="new">subtitles</target> 462 <source>subtitles</source><target state="new">subtitles</target>
463 463
464 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 464 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
@@ -847,7 +847,7 @@
847 847
848 848
849 849
850 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group></trans-unit> 850 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
851 <trans-unit id="1502595455339510144" datatype="html"> 851 <trans-unit id="1502595455339510144" datatype="html">
852 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 852 <source> Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
853 <target state="new"> 853 <target state="new">
@@ -930,7 +930,31 @@
930 <source>Federation</source> 930 <source>Federation</source>
931 <target state="new">Federation</target> 931 <target state="new">Federation</target>
932 932
933 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 933 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
934 <source>Following</source><target state="new">Following</target>
935 <context-group purpose="location">
936 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
937 <context context-type="linenumber">29</context>
938 </context-group>
939 <context-group purpose="location">
940 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
941 <context context-type="linenumber">31</context>
942 </context-group>
943 <context-group purpose="location">
944 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
945 <context context-type="linenumber">28</context>
946 </context-group>
947 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
948 <source>Followers</source><target state="new">Followers</target>
949 <context-group purpose="location">
950 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
951 <context context-type="linenumber">34</context>
952 </context-group>
953 <context-group purpose="location">
954 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
955 <context context-type="linenumber">37</context>
956 </context-group>
957 </trans-unit>
934 <trans-unit id="3541687134897970106" datatype="html"> 958 <trans-unit id="3541687134897970106" datatype="html">
935 <source>followers</source> 959 <source>followers</source>
936 <target state="new">followers</target> 960 <target state="new">followers</target>
@@ -994,7 +1018,7 @@
994 1018
995 1019
996 1020
997 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 1021 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
998 <trans-unit id="3616223838716839702"> 1022 <trans-unit id="3616223838716839702">
999 <source>Ban this user</source> 1023 <source>Ban this user</source>
1000 <target>இந்த பயணரை ரத்து செய்</target> 1024 <target>இந்த பயணரை ரத்து செய்</target>
@@ -1062,17 +1086,11 @@
1062 1086
1063 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html"> 1087 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html</context><context context-type="linenumber">16</context></context-group></trans-unit><trans-unit id="7252854992688790751" datatype="html">
1064 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1088 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1065 <context-group purpose="location"> 1089
1066 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1090 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7215649348148521605" datatype="html">
1067 <context context-type="linenumber">60,62</context>
1068 </context-group>
1069 </trans-unit><trans-unit id="7215649348148521605" datatype="html">
1070 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1091 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source><target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1071 <context-group purpose="location"> 1092
1072 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1093 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1073 <context context-type="linenumber">65,67</context>
1074 </context-group>
1075 </trans-unit>
1076 <trans-unit id="2392488717875840729"> 1094 <trans-unit id="2392488717875840729">
1077 <source>User</source> 1095 <source>User</source>
1078 <target>பயணர்</target> 1096 <target>பயணர்</target>
@@ -1083,7 +1101,13 @@
1083 <source>Username or email address</source> 1101 <source>Username or email address</source>
1084 <target state="new">Username or email address</target> 1102 <target state="new">Username or email address</target>
1085 1103
1086 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit> 1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="1758058452376026925" datatype="html">
1105 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1106 <context-group purpose="location">
1107 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1108 <context context-type="linenumber">33,34</context>
1109 </context-group>
1110 </trans-unit>
1087 1111
1088 <trans-unit id="1431416938026210429"> 1112 <trans-unit id="1431416938026210429">
1089 <source>Password</source> 1113 <source>Password</source>
@@ -1096,40 +1120,34 @@
1096 1120
1097 1121
1098 1122
1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group></trans-unit> 1123 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1100 <trans-unit id="8715156686857791956" datatype="html"> 1124 <trans-unit id="8715156686857791956" datatype="html">
1101 <source>Click here to reset your password</source> 1125 <source>Click here to reset your password</source>
1102 <target state="new">Click here to reset your password</target> 1126 <target state="new">Click here to reset your password</target>
1103 1127
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html"> 1128 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="892063502898494584" datatype="html">
1105 <source>I forgot my password</source><target state="new">I forgot my password</target> 1129 <source>I forgot my password</source><target state="new">I forgot my password</target>
1106 <context-group purpose="location"> 1130
1107 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1131 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit><trans-unit id="2101170466365500913" datatype="html">
1108 <context context-type="linenumber">47</context>
1109 </context-group>
1110 </trans-unit><trans-unit id="2101170466365500913" datatype="html">
1111 <source> Logging into an account lets you publish content </source><target state="new"> Logging into an account lets you publish content </target> 1132 <source> Logging into an account lets you publish content </source><target state="new"> Logging into an account lets you publish content </target>
1112 <context-group purpose="location"> 1133
1113 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1134 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1114 <context context-type="linenumber">56,57</context>
1115 </context-group>
1116 </trans-unit>
1117 <trans-unit id="2454050363478003966"> 1135 <trans-unit id="2454050363478003966">
1118 <source>Login</source> 1136 <source>Login</source>
1119 <target>உள்நுழை</target> 1137 <target>உள்நுழை</target>
1120 1138
1121 1139
1122 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1140 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1123 <trans-unit id="3183213940445113677" datatype="html"> 1141 <trans-unit id="3183213940445113677" datatype="html">
1124 <source>Or sign in with</source> 1142 <source>Or sign in with</source>
1125 <target state="new">Or sign in with</target> 1143 <target state="new">Or sign in with</target>
1126 1144
1127 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit> 1145 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1128 <trans-unit id="3238209155172574367"> 1146 <trans-unit id="3238209155172574367">
1129 <source>Forgot your password</source> 1147 <source>Forgot your password</source>
1130 <target>கடவுச்சொல் மறந்துவிட்டது</target> 1148 <target>கடவுச்சொல் மறந்துவிட்டது</target>
1131 1149
1132 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group></trans-unit> 1150 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1133 <trans-unit id="87327320394367488" datatype="html"> 1151 <trans-unit id="87327320394367488" datatype="html">
1134 <source> 1152 <source>
1135 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1153 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
@@ -1138,10 +1156,10 @@
1138 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. 1156 We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.
1139 </target> 1157 </target>
1140 1158
1141 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html"> 1159 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="3188014010833256853" datatype="html">
1142 <source> Enter your email address and we will send you a link to reset your password. </source><target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1160 <source> Enter your email address and we will send you a link to reset your password. </source><target state="new"> Enter your email address and we will send you a link to reset your password. </target>
1143 1161
1144 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html"> 1162 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit><trans-unit id="1190256911880544559" datatype="html">
1145 <source>An email with the reset password instructions will be sent to <x id="PH"/>. 1163 <source>An email with the reset password instructions will be sent to <x id="PH"/>.
1146The link will expire within 1 hour.</source><target state="new">An email with the reset password instructions will be sent to <x id="PH"/>. 1164The link will expire within 1 hour.</source><target state="new">An email with the reset password instructions will be sent to <x id="PH"/>.
1147The link will expire within 1 hour.</target> 1165The link will expire within 1 hour.</target>
@@ -1157,17 +1175,17 @@ The link will expire within 1 hour.</target>
1157 1175
1158 1176
1159 1177
1160 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group></trans-unit> 1178 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1161 <trans-unit id="3967269098753656610"> 1179 <trans-unit id="3967269098753656610">
1162 <source>Email address</source> 1180 <source>Email address</source>
1163 <target>மின்னஞ்சல்</target> 1181 <target>மின்னஞ்சல்</target>
1164 1182
1165 1183
1166 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html"> 1184 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit><trans-unit id="7808756054397155068" datatype="html">
1167 <source>Reset</source><target state="new">Reset</target> 1185 <source>Reset</source><target state="new">Reset</target>
1168 1186
1169 <note priority="1" from="description">Password reset button</note> 1187 <note priority="1" from="description">Password reset button</note>
1170 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group></trans-unit> 1188 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1171 1189
1172 1190
1173 <trans-unit id="4319634264526091601" datatype="html"> 1191 <trans-unit id="4319634264526091601" datatype="html">
@@ -1537,7 +1555,7 @@ The link will expire within 1 hour.</target>
1537 <source>Create an account</source> 1555 <source>Create an account</source>
1538 <target>கணக்கை உருவாக்கு</target> 1556 <target>கணக்கை உருவாக்கு</target>
1539 1557
1540 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1558 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1541 1559
1542 <trans-unit id="3058024914967508975" datatype="html"> 1560 <trans-unit id="3058024914967508975" datatype="html">
1543 <source>My videos</source><target state="new">My videos</target> 1561 <source>My videos</source><target state="new">My videos</target>
@@ -1583,7 +1601,7 @@ The link will expire within 1 hour.</target>
1583 <target state="new">VIDEOS</target> 1601 <target state="new">VIDEOS</target>
1584 1602
1585 1603
1586 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html"> 1604 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html">
1587 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target> 1605 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target>
1588 1606
1589 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html"> 1607 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html">
@@ -1651,7 +1669,7 @@ The link will expire within 1 hour.</target>
1651 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html"> 1669 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/notification.component.html</context><context context-type="linenumber">49</context></context-group></trans-unit><trans-unit id="4424964105331349857" datatype="html">
1652 <source>I'm a teapot</source><target state="new">I'm a teapot</target> 1670 <source>I'm a teapot</source><target state="new">I'm a teapot</target>
1653 1671
1654 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html"> 1672 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit><trans-unit id="1597262876035959248" datatype="html">
1655 <source>That's an error.</source><target state="new">That's an error.</target> 1673 <source>That's an error.</source><target state="new">That's an error.</target>
1656 <context-group purpose="location"> 1674 <context-group purpose="location">
1657 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context> 1675 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context>
@@ -1717,7 +1735,7 @@ The link will expire within 1 hour.</target>
1717 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html"> 1735 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.html</context><context context-type="linenumber">42</context></context-group></trans-unit><trans-unit id="2971365540217107489" datatype="html">
1718 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1736 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source><target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1719 1737
1720 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group></trans-unit> 1738 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1721 1739
1722 <trans-unit id="5131854469652959713" datatype="html"> 1740 <trans-unit id="5131854469652959713" datatype="html">
1723 <source>GLOBAL SEARCH</source> 1741 <source>GLOBAL SEARCH</source>
@@ -2425,7 +2443,7 @@ The link will expire within 1 hour.</target>
2425 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2443 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2426 <source>Upload on hold</source><target state="new">Upload on hold</target> 2444 <source>Upload on hold</source><target state="new">Upload on hold</target>
2427 2445
2428 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2446 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2429 <trans-unit id="285180972645018518" datatype="html"> 2447 <trans-unit id="285180972645018518" datatype="html">
2430 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2448 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2431 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2449 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3097,11 +3115,7 @@ The link will expire within 1 hour.</target>
3097 <target>ID</target> 3115 <target>ID</target>
3098 3116
3099 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit> 3117 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group></trans-unit>
3100 <trans-unit id="2265605798180116441" datatype="html"> 3118
3101 <source>Follower handle</source>
3102 <target state="new">Follower handle</target>
3103
3104 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
3105 <trans-unit id="5911214550882917183" datatype="html"> 3119 <trans-unit id="5911214550882917183" datatype="html">
3106 <source>State</source> 3120 <source>State</source>
3107 <target state="new">State</target> 3121 <target state="new">State</target>
@@ -3183,11 +3197,7 @@ The link will expire within 1 hour.</target>
3183 </target> 3197 </target>
3184 3198
3185 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit> 3199 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
3186 <trans-unit id="6641024648411549335" datatype="html"> 3200
3187 <source>Host</source>
3188 <target state="new">Host</target>
3189
3190 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3191 <trans-unit id="6571718060636962350" datatype="html"> 3201 <trans-unit id="6571718060636962350" datatype="html">
3192 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3202 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3193 <target state="new">Redundancy allowed 3203 <target state="new">Redundancy allowed
@@ -3198,7 +3208,7 @@ The link will expire within 1 hour.</target>
3198 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html"> 3208 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="9160510009013134726" datatype="html">
3199 <source>Unfollow</source><target state="new">Unfollow</target> 3209 <source>Unfollow</source><target state="new">Unfollow</target>
3200 3210
3201 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3211 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3202 <trans-unit id="8246779176913476983" datatype="html"> 3212 <trans-unit id="8246779176913476983" datatype="html">
3203 <source>Open instance in a new tab</source> 3213 <source>Open instance in a new tab</source>
3204 <target state="new">Open instance in a new tab</target> 3214 <target state="new">Open instance in a new tab</target>
@@ -3210,12 +3220,12 @@ The link will expire within 1 hour.</target>
3210 <source>No host found matching current filters.</source> 3220 <source>No host found matching current filters.</source>
3211 <target state="new">No host found matching current filters.</target> 3221 <target state="new">No host found matching current filters.</target>
3212 3222
3213 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3223 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3214 <trans-unit id="7274241885665071790" datatype="html"> 3224 <trans-unit id="7274241885665071790" datatype="html">
3215 <source>Your instance is not following anyone.</source> 3225 <source>Your instance is not following anyone.</source>
3216 <target state="new">Your instance is not following anyone.</target> 3226 <target state="new">Your instance is not following anyone.</target>
3217 3227
3218 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3228 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3219 <trans-unit id="4774348799569692380" datatype="html"> 3229 <trans-unit id="4774348799569692380" datatype="html">
3220 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3230 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3221 <target state="new">Showing 3231 <target state="new">Showing
@@ -3225,14 +3235,7 @@ The link will expire within 1 hour.</target>
3225 </target> 3235 </target>
3226 3236
3227 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3237 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3228 <trans-unit id="6275803119759621687" datatype="html"> 3238 <trans-unit id="9216117865911519658" datatype="html">
3229 <source>Follow domains</source>
3230 <target state="new">Follow domains</target>
3231
3232 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit><trans-unit id="1268699198448750610" datatype="html">
3233 <source>Follow instances</source><target state="new">Follow instances</target>
3234
3235 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3236 <source>Action</source><target state="new">Action</target> 3239 <source>Action</source><target state="new">Action</target>
3237 3240
3238 3241
@@ -3282,7 +3285,7 @@ The link will expire within 1 hour.</target>
3282 3285
3283 3286
3284 3287
3285 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html"> 3288 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit><trans-unit id="5428411040014095392" datatype="html">
3286 <source>e.g. jane_doe</source><target state="new">e.g. jane_doe</target> 3289 <source>e.g. jane_doe</source><target state="new">e.g. jane_doe</target>
3287 3290
3288 <note priority="1" from="description">Username choice placeholder in the registration form</note> 3291 <note priority="1" from="description">Username choice placeholder in the registration form</note>
@@ -3314,7 +3317,7 @@ The link will expire within 1 hour.</target>
3314 <target state="new">Role</target> 3317 <target state="new">Role</target>
3315 3318
3316 3319
3317 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group></trans-unit> 3320 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3318 <trans-unit id="7046347992315328430" datatype="html"> 3321 <trans-unit id="7046347992315328430" datatype="html">
3319 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3322 <source> Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3320 <target state="new"> 3323 <target state="new">
@@ -3337,15 +3340,9 @@ The link will expire within 1 hour.</target>
3337 3340
3338 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html"> 3341 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html">
3339 <source>Auth plugin</source><target state="new">Auth plugin</target> 3342 <source>Auth plugin</source><target state="new">Auth plugin</target>
3340 <context-group purpose="location"> 3343
3341 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3344
3342 <context context-type="linenumber">188</context> 3345 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit><trans-unit id="588099657508661970" datatype="html">
3343 </context-group>
3344 <context-group purpose="location">
3345 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3346 <context context-type="linenumber">188</context>
3347 </context-group>
3348 </trans-unit><trans-unit id="588099657508661970" datatype="html">
3349 <source>None (local authentication)</source><target state="new">None (local authentication)</target> 3346 <source>None (local authentication)</source><target state="new">None (local authentication)</target>
3350 <context-group purpose="location"> 3347 <context-group purpose="location">
3351 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3348 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
@@ -3572,7 +3569,13 @@ The link will expire within 1 hour.</target>
3572 3569
3573 3570
3574 3571
3575 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="4691552465058437520" datatype="html"> 3572 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3573 <source>Follower</source><target state="new">Follower</target>
3574 <context-group purpose="location">
3575 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3576 <context context-type="linenumber">24</context>
3577 </context-group>
3578 </trans-unit><trans-unit id="4691552465058437520" datatype="html">
3576 <source>Commented video</source><target state="new">Commented video</target> 3579 <source>Commented video</source><target state="new">Commented video</target>
3577 3580
3578 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html"> 3581 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">82</context></context-group></trans-unit><trans-unit id="7266085473379376028" datatype="html">
@@ -3896,7 +3899,7 @@ The link will expire within 1 hour.</target>
3896 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 3899 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
3897 </target> 3900 </target>
3898 3901
3899 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3902 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3900 <trans-unit id="4058814854824495833" datatype="html"> 3903 <trans-unit id="4058814854824495833" datatype="html">
3901 <source>Mute domains</source> 3904 <source>Mute domains</source>
3902 <target state="new">Mute domains</target> 3905 <target state="new">Mute domains</target>
@@ -5711,11 +5714,8 @@ color: red;
5711 5714
5712 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5715 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5713 <source>CHANNELS</source><target state="new">CHANNELS</target> 5716 <source>CHANNELS</source><target state="new">CHANNELS</target>
5714 <context-group purpose="location"> 5717
5715 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5718 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5716 <context context-type="linenumber">82</context>
5717 </context-group>
5718 </trans-unit>
5719 5719
5720 <trans-unit id="3666829335406793239" datatype="html"> 5720 <trans-unit id="3666829335406793239" datatype="html">
5721 <source>This account does not have channels.</source> 5721 <source>This account does not have channels.</source>
@@ -5753,7 +5753,13 @@ channel with the same name (<x id="PH_2"/>)!</source><target state="new">Do you
5753It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 5753It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
5754channel with the same name (<x id="PH_2"/>)!</target> 5754channel with the same name (<x id="PH_2"/>)!</target>
5755 5755
5756 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5756 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5757 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5758 <context-group purpose="location">
5759 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5760 <context context-type="linenumber">48</context>
5761 </context-group>
5762 </trans-unit>
5757 <trans-unit id="5387007581996837469" datatype="html"> 5763 <trans-unit id="5387007581996837469" datatype="html">
5758 <source>My Channels</source> 5764 <source>My Channels</source>
5759 <target state="new">My Channels</target> 5765 <target state="new">My Channels</target>
@@ -6365,12 +6371,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6365 <source>Your message has been sent.</source> 6371 <source>Your message has been sent.</source>
6366 <target state="new">Your message has been sent.</target> 6372 <target state="new">Your message has been sent.</target>
6367 6373
6368 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6374 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6369 <trans-unit id="2072135752262464360" datatype="html"> 6375 <trans-unit id="2072135752262464360" datatype="html">
6370 <source>You already sent this form recently</source> 6376 <source>You already sent this form recently</source>
6371 <target state="new">You already sent this form recently</target> 6377 <target state="new">You already sent this form recently</target>
6372 6378
6373 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6379 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6374 <trans-unit id="819067926858619041" datatype="html"> 6380 <trans-unit id="819067926858619041" datatype="html">
6375 <source>Account videos</source><target state="new">Account videos</target> 6381 <source>Account videos</source><target state="new">Account videos</target>
6376 6382
@@ -6405,10 +6411,10 @@ channel with the same name (<x id="PH_2"/>)!</target>
6405 <x id="PH"/> direct account followers 6411 <x id="PH"/> direct account followers
6406 </target> 6412 </target>
6407 6413
6408 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html"> 6414 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit><trans-unit id="6250999352462648289" datatype="html">
6409 <source>Report this account</source><target state="new">Report this account</target> 6415 <source>Report this account</source><target state="new">Report this account</target>
6410 6416
6411 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6417 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6412 6418
6413 <trans-unit id="1504521795586863905" datatype="html"> 6419 <trans-unit id="1504521795586863905" datatype="html">
6414 <source>VIDEOS</source><target state="new">VIDEOS</target> 6420 <source>VIDEOS</source><target state="new">VIDEOS</target>
@@ -6420,24 +6426,16 @@ channel with the same name (<x id="PH_2"/>)!</target>
6420 <target state="new">Username copied</target> 6426 <target state="new">Username copied</target>
6421 6427
6422 6428
6423 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html"> 6429 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit><trans-unit id="9221735175659318025" datatype="html">
6424 <source>1 subscriber</source><target state="new">1 subscriber</target> 6430 <source>1 subscriber</source><target state="new">1 subscriber</target>
6425 6431
6426 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html"> 6432 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit><trans-unit id="4097331874769079975" datatype="html">
6427 <source><x id="PH"/> subscribers</source><target state="new"><x id="PH"/> subscribers</target> 6433 <source><x id="PH"/> subscribers</source><target state="new"><x id="PH"/> subscribers</target>
6428 6434
6429 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6435 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6436
6437
6430 6438
6431 <trans-unit id="4682675125751819107" datatype="html">
6432 <source>Instances you follow</source>
6433 <target state="new">Instances you follow</target>
6434
6435 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6436 <trans-unit id="8899833753704589712" datatype="html">
6437 <source>Instances following you</source>
6438 <target state="new">Instances following you</target>
6439
6440 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6441 <trans-unit id="1035838766454786107" datatype="html"> 6439 <trans-unit id="1035838766454786107" datatype="html">
6442 <source>Audio-only</source> 6440 <source>Audio-only</source>
6443 <target state="new">Audio-only</target> 6441 <target state="new">Audio-only</target>
@@ -6485,7 +6483,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6485 <source>Auto (via ffmpeg)</source> 6483 <source>Auto (via ffmpeg)</source>
6486 <target state="new">Auto (via ffmpeg)</target> 6484 <target state="new">Auto (via ffmpeg)</target>
6487 6485
6488 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="931255636742351800" datatype="html"> 6486 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="3642770981085338761" datatype="html">
6487 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6488 <context-group purpose="location">
6489 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6490 <context context-type="linenumber">3</context>
6491 </context-group>
6492 </trans-unit><trans-unit id="931255636742351800" datatype="html">
6489 <source>No limit</source><target state="new">No limit</target> 6493 <source>No limit</source><target state="new">No limit</target>
6490 6494
6491 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html"> 6495 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit><trans-unit id="5250062810079582285" datatype="html">
@@ -6604,17 +6608,33 @@ channel with the same name (<x id="PH_2"/>)!</target>
6604 <source>Domain is required.</source> 6608 <source>Domain is required.</source>
6605 <target state="new">Domain is required.</target> 6609 <target state="new">Domain is required.</target>
6606 6610
6607 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group></trans-unit> 6611 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6608 <trans-unit id="6780793142903080663" datatype="html"> 6612 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6609 <source>Domains entered are invalid.</source> 6613 <context-group purpose="location">
6610 <target state="new">Domains entered are invalid.</target> 6614 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6611 6615 <context context-type="linenumber">93</context>
6612 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6616 </context-group>
6613 <trans-unit id="5886492514458202177" datatype="html"> 6617 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6614 <source>Domains entered contain duplicates.</source> 6618 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6615 <target state="new">Domains entered contain duplicates.</target> 6619 <context-group purpose="location">
6616 6620 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6617 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 6621 <context context-type="linenumber">94</context>
6622 </context-group>
6623 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6624 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6625 <context-group purpose="location">
6626 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6627 <context context-type="linenumber">102</context>
6628 </context-group>
6629 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6630 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6631 <context-group purpose="location">
6632 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6633 <context context-type="linenumber">103</context>
6634 </context-group>
6635 </trans-unit>
6636
6637
6618 <trans-unit id="240806681889331244" datatype="html"> 6638 <trans-unit id="240806681889331244" datatype="html">
6619 <source>Unlimited</source> 6639 <source>Unlimited</source>
6620 <target state="new">Unlimited</target> 6640 <target state="new">Unlimited</target>
@@ -6745,7 +6765,27 @@ channel with the same name (<x id="PH_2"/>)!</target>
6745 <x id="PH"/> removed from instance followers 6765 <x id="PH"/> removed from instance followers
6746 </target> 6766 </target>
6747 6767
6748 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit> 6768 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit><trans-unit id="6018246591673612412" datatype="html">
6769 <source>Follow</source><target state="new">Follow</target>
6770 <context-group purpose="location">
6771 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6772 <context context-type="linenumber">3</context>
6773 </context-group>
6774 <context-group purpose="location">
6775 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6776 <context context-type="linenumber">37</context>
6777 </context-group>
6778 <context-group purpose="location">
6779 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6780 <context context-type="linenumber">18</context>
6781 </context-group>
6782 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6783 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6784 <context-group purpose="location">
6785 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6786 <context context-type="linenumber">11</context>
6787 </context-group>
6788 </trans-unit>
6749 <trans-unit id="2740793005745065895" datatype="html"> 6789 <trans-unit id="2740793005745065895" datatype="html">
6750 <source> 6790 <source>
6751 <x id="PH"/> is not valid 6791 <x id="PH"/> is not valid
@@ -6754,19 +6794,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
6754 <x id="PH"/> is not valid 6794 <x id="PH"/> is not valid
6755 </target> 6795 </target>
6756 6796
6757 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group></trans-unit> 6797 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6758 <trans-unit id="2355066641781598196" datatype="html"> 6798 <trans-unit id="2355066641781598196" datatype="html">
6759 <source>Follow request(s) sent!</source> 6799 <source>Follow request(s) sent!</source>
6760 <target state="new">Follow request(s) sent!</target> 6800 <target state="new">Follow request(s) sent!</target>
6761 6801
6762 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit> 6802 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6803 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6804 <context-group purpose="location">
6805 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6806 <context context-type="linenumber">3</context>
6807 </context-group>
6808 </trans-unit>
6763 <trans-unit id="4245720728052819482" datatype="html"> 6809 <trans-unit id="4245720728052819482" datatype="html">
6764 <source>Do you really want to unfollow <x id="PH"/>?</source> 6810 <source>Do you really want to unfollow <x id="PH"/>?</source>
6765 <target state="new">Do you really want to unfollow 6811 <target state="new">Do you really want to unfollow
6766 <x id="PH"/>? 6812 <x id="PH"/>?
6767 </target> 6813 </target>
6768 6814
6769 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group></trans-unit> 6815 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6770 <trans-unit id="9160510009013134726" datatype="html"> 6816 <trans-unit id="9160510009013134726" datatype="html">
6771 <source>Unfollow</source> 6817 <source>Unfollow</source>
6772 <target state="new">Unfollow</target> 6818 <target state="new">Unfollow</target>
@@ -6778,7 +6824,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
6778 <x id="PH"/> anymore. 6824 <x id="PH"/> anymore.
6779 </target> 6825 </target>
6780 6826
6781 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 6827 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6782 <trans-unit id="2593763089859685916" datatype="html"> 6828 <trans-unit id="2593763089859685916" datatype="html">
6783 <source>enabled</source> 6829 <source>enabled</source>
6784 <target state="new">enabled</target> 6830 <target state="new">enabled</target>
@@ -7236,7 +7282,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7236 7282
7237 7283
7238 7284
7239 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit> 7285 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7240 <trans-unit id="5076187961693950167" datatype="html"> 7286 <trans-unit id="5076187961693950167" datatype="html">
7241 <source>Standard logs</source> 7287 <source>Standard logs</source>
7242 <target state="new">Standard logs</target> 7288 <target state="new">Standard logs</target>
@@ -7274,13 +7320,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7274 <source>Update user password</source> 7320 <source>Update user password</source>
7275 <target state="new">Update user password</target> 7321 <target state="new">Update user password</target>
7276 7322
7277 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit><trans-unit id="177544274549739411" datatype="html"> 7323 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group></trans-unit>
7278 <source>Following list</source><target state="new">Following list</target>
7279
7280 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group></trans-unit><trans-unit id="8092429110007204784" datatype="html">
7281 <source>Followers list</source><target state="new">Followers list</target>
7282
7283 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group></trans-unit>
7284 <trans-unit id="780323526182667308" datatype="html"> 7324 <trans-unit id="780323526182667308" datatype="html">
7285 <source>User <x id="PH"/> updated.</source> 7325 <source>User <x id="PH"/> updated.</source>
7286 <target state="new">User 7326 <target state="new">User
@@ -7311,13 +7351,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7311 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html"> 7351 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/users.routes.ts</context><context context-type="linenumber">45</context></context-group></trans-unit><trans-unit id="8564701209009684429" datatype="html">
7312 <source>Federation</source><target state="new">Federation</target> 7352 <source>Federation</source><target state="new">Federation</target>
7313 7353
7314 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit><trans-unit id="4682675125751819107" datatype="html"> 7354 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit>
7315 <source>Instances you follow</source><target state="new">Instances you follow</target>
7316
7317 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group></trans-unit><trans-unit id="8899833753704589712" datatype="html">
7318 <source>Instances following you</source><target state="new">Instances following you</target>
7319
7320 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group></trans-unit>
7321 <trans-unit id="3767259920053407667" datatype="html"> 7355 <trans-unit id="3767259920053407667" datatype="html">
7322 <source>Videos will be deleted, comments will be tombstoned.</source> 7356 <source>Videos will be deleted, comments will be tombstoned.</source>
7323 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7357 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7345,7 +7379,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7345 <target state="new">Set Email as Verified</target> 7379 <target state="new">Set Email as Verified</target>
7346 7380
7347 7381
7348 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7382 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7383 <source>Created</source><target state="new">Created</target>
7384 <context-group purpose="location">
7385 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7386 <context context-type="linenumber">115</context>
7387 </context-group>
7388 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7389 <source>Daily quota</source><target state="new">Daily quota</target>
7390 <context-group purpose="location">
7391 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7392 <context context-type="linenumber">120</context>
7393 </context-group>
7394 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7395 <source>Last login</source><target state="new">Last login</target>
7396 <context-group purpose="location">
7397 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7398 <context context-type="linenumber">122</context>
7399 </context-group>
7400 </trans-unit>
7349 <trans-unit id="3403978719736970622" datatype="html"> 7401 <trans-unit id="3403978719736970622" datatype="html">
7350 <source>You cannot ban root.</source> 7402 <source>You cannot ban root.</source>
7351 <target state="new">You cannot ban root.</target> 7403 <target state="new">You cannot ban root.</target>
@@ -7650,12 +7702,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
7650 <x id="PH"/> created. 7702 <x id="PH"/> created.
7651 </target> 7703 </target>
7652 7704
7653 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7705 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7654 <trans-unit id="8723777130353305761" datatype="html"> 7706 <trans-unit id="8723777130353305761" datatype="html">
7655 <source>This name already exists on this instance.</source> 7707 <source>This name already exists on this instance.</source>
7656 <target state="new">This name already exists on this instance.</target> 7708 <target state="new">This name already exists on this instance.</target>
7657 7709
7658 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7710 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7659 <trans-unit id="7589345916094713536" datatype="html"> 7711 <trans-unit id="7589345916094713536" datatype="html">
7660 <source>Video channel <x id="PH"/> updated.</source> 7712 <source>Video channel <x id="PH"/> updated.</source>
7661 <target state="new">Video channel 7713 <target state="new">Video channel
@@ -7672,13 +7724,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7672 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 7724 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7673 7725
7674 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7726 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7675 <trans-unit id="2575302837003821736" datatype="html"> 7727
7676 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7677 <target state="new">Please type the display name of the video channel (
7678 <x id="PH"/>) to confirm
7679 </target>
7680
7681 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7682 <trans-unit id="624066830180032195" datatype="html"> 7728 <trans-unit id="624066830180032195" datatype="html">
7683 <source>Video channel <x id="PH"/> deleted.</source> 7729 <source>Video channel <x id="PH"/> deleted.</source>
7684 <target state="new">Video channel 7730 <target state="new">Video channel
@@ -7821,7 +7867,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
7821 <source>Ownership change request sent.</source> 7867 <source>Ownership change request sent.</source>
7822 <target state="new">Ownership change request sent.</target> 7868 <target state="new">Ownership change request sent.</target>
7823 7869
7824 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 7870 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group></trans-unit><trans-unit id="7699622144571229146" datatype="html">
7871 <source>Sort by</source><target state="new">Sort by</target>
7872 <context-group purpose="location">
7873 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7874 <context context-type="linenumber">26</context>
7875 </context-group>
7876 </trans-unit>
7825 <trans-unit id="3245220240937722814" datatype="html"> 7877 <trans-unit id="3245220240937722814" datatype="html">
7826 <source>My channels</source> 7878 <source>My channels</source>
7827 <target state="new">My channels</target> 7879 <target state="new">My channels</target>
@@ -7916,7 +7968,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7916 <target state="new">Subscribe to the account</target> 7968 <target state="new">Subscribe to the account</target>
7917 7969
7918 7970
7919 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 7971 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
7920 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 7972 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
7921 <context-group purpose="location"> 7973 <context-group purpose="location">
7922 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 7974 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -7963,34 +8015,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
7963 <source>Go to my subscriptions</source> 8015 <source>Go to my subscriptions</source>
7964 <target state="new">Go to my subscriptions</target> 8016 <target state="new">Go to my subscriptions</target>
7965 8017
7966 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group></trans-unit> 8018 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
7967 <trans-unit id="1136469849928650779" datatype="html"> 8019 <trans-unit id="1136469849928650779" datatype="html">
7968 <source>Go to my videos</source> 8020 <source>Go to my videos</source>
7969 <target state="new">Go to my videos</target> 8021 <target state="new">Go to my videos</target>
7970 8022
7971 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit> 8023 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
7972 <trans-unit id="7836683738999600376" datatype="html"> 8024 <trans-unit id="7836683738999600376" datatype="html">
7973 <source>Go to my imports</source> 8025 <source>Go to my imports</source>
7974 <target state="new">Go to my imports</target> 8026 <target state="new">Go to my imports</target>
7975 8027
7976 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 8028 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
7977 <trans-unit id="7511292153332773503" datatype="html"> 8029 <trans-unit id="7511292153332773503" datatype="html">
7978 <source>Go to my channels</source> 8030 <source>Go to my channels</source>
7979 <target state="new">Go to my channels</target> 8031 <target state="new">Go to my channels</target>
7980 8032
7981 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html"> 8033 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit><trans-unit id="2013324644839511073" datatype="html">
7982 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8034 <source>Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7983Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8035Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source><target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
7984Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8036Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
7985 8037
7986 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group></trans-unit> 8038 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
7987 8039
7988 8040
7989 <trans-unit id="375263728166936544" datatype="html"> 8041 <trans-unit id="375263728166936544" datatype="html">
7990 <source>You need to reconnect.</source> 8042 <source>You need to reconnect.</source>
7991 <target state="new">You need to reconnect.</target> 8043 <target state="new">You need to reconnect.</target>
7992 8044
7993 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group></trans-unit> 8045 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
7994 <trans-unit id="2206638022166154361" datatype="html"> 8046 <trans-unit id="2206638022166154361" datatype="html">
7995 <source>Keyboard Shortcuts:</source> 8047 <source>Keyboard Shortcuts:</source>
7996 <target state="new">Keyboard Shortcuts:</target> 8048 <target state="new">Keyboard Shortcuts:</target>
@@ -8001,6 +8053,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8001 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8053 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8002 <context context-type="linenumber">98</context> 8054 <context context-type="linenumber">98</context>
8003 </context-group> 8055 </context-group>
8056 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8057 <source>In my library</source><target state="new">In my library</target>
8058 <context-group purpose="location">
8059 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8060 <context context-type="linenumber">104</context>
8061 </context-group>
8004 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 8062 </trans-unit><trans-unit id="232050922346936574" datatype="html">
8005 <source>Trending</source><target state="new">Trending</target> 8063 <source>Trending</source><target state="new">Trending</target>
8006 8064
@@ -8024,38 +8082,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8024 <source>Incorrect username or password.</source> 8082 <source>Incorrect username or password.</source>
8025 <target state="new">Incorrect username or password.</target> 8083 <target state="new">Incorrect username or password.</target>
8026 8084
8027 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 8085 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8028 <trans-unit id="6974874606619467663" datatype="html"> 8086 <trans-unit id="6974874606619467663" datatype="html">
8029 <source>Your account is blocked.</source> 8087 <source>Your account is blocked.</source>
8030 <target state="new">Your account is blocked.</target> 8088 <target state="new">Your account is blocked.</target>
8031 8089
8032 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 8090 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8033 8091
8034 <trans-unit id="7939914198003891823" datatype="html"> 8092 <trans-unit id="7939914198003891823" datatype="html">
8035 <source>any language</source> 8093 <source>any language</source>
8036 <target state="new">any language</target> 8094 <target state="new">any language</target>
8037 8095
8038 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8096 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8039 <trans-unit id="5633144232269377096" datatype="html"> 8097 <trans-unit id="5633144232269377096" datatype="html">
8040 <source>hide</source> 8098 <source>hide</source>
8041 <target state="new">hide</target> 8099 <target state="new">hide</target>
8042 8100
8043 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8101 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8044 <trans-unit id="8603861867909474404" datatype="html"> 8102 <trans-unit id="8603861867909474404" datatype="html">
8045 <source>blur</source> 8103 <source>blur</source>
8046 <target state="new">blur</target> 8104 <target state="new">blur</target>
8047 8105
8048 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8106 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8049 <trans-unit id="4534458451100881847" datatype="html"> 8107 <trans-unit id="4534458451100881847" datatype="html">
8050 <source>display</source> 8108 <source>display</source>
8051 <target state="new">display</target> 8109 <target state="new">display</target>
8052 8110
8053 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8111 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8054 <trans-unit id="4467323362722952678" datatype="html"> 8112 <trans-unit id="4467323362722952678" datatype="html">
8055 <source>Unknown</source> 8113 <source>Unknown</source>
8056 <target state="new">Unknown</target> 8114 <target state="new">Unknown</target>
8057 8115
8058 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8116 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8059 <trans-unit id="8781423666414310853" datatype="html"> 8117 <trans-unit id="8781423666414310853" datatype="html">
8060 <source>Your password has been successfully reset!</source> 8118 <source>Your password has been successfully reset!</source>
8061 <target state="new">Your password has been successfully reset!</target> 8119 <target state="new">Your password has been successfully reset!</target>
@@ -9598,17 +9656,17 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9598 <x id="PH"/> minutes. 9656 <x id="PH"/> minutes.
9599 </target> 9657 </target>
9600 9658
9601 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 9659 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9602 <trans-unit id="4965472196059235310" datatype="html"> 9660 <trans-unit id="4965472196059235310" datatype="html">
9603 <source>Too many attempts, please try again later.</source> 9661 <source>Too many attempts, please try again later.</source>
9604 <target state="new">Too many attempts, please try again later.</target> 9662 <target state="new">Too many attempts, please try again later.</target>
9605 9663
9606 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group></trans-unit> 9664 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9607 <trans-unit id="1693549688987384699" datatype="html"> 9665 <trans-unit id="1693549688987384699" datatype="html">
9608 <source>Server error. Please retry later.</source> 9666 <source>Server error. Please retry later.</source>
9609 <target state="new">Server error. Please retry later.</target> 9667 <target state="new">Server error. Please retry later.</target>
9610 9668
9611 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group></trans-unit> 9669 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9612 <trans-unit id="5927402622550505067" datatype="html"> 9670 <trans-unit id="5927402622550505067" datatype="html">
9613 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9671 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9614 <target state="new">Subscribed to all current channels of 9672 <target state="new">Subscribed to all current channels of
@@ -10111,20 +10169,20 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10111 <source>Your video was uploaded to your account and is private.</source> 10169 <source>Your video was uploaded to your account and is private.</source>
10112 <target state="new">Your video was uploaded to your account and is private.</target> 10170 <target state="new">Your video was uploaded to your account and is private.</target>
10113 10171
10114 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10172 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10115 <trans-unit id="5699822024600815733" datatype="html"> 10173 <trans-unit id="5699822024600815733" datatype="html">
10116 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10174 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10117 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target> 10175 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target>
10118 10176
10119 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10177 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10120 <trans-unit id="1219739004043110649" datatype="html"> 10178 <trans-unit id="1219739004043110649" datatype="html">
10121 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10179 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10122 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target> 10180 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target>
10123 10181
10124 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html"> 10182 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit><trans-unit id="6932865105766151309" datatype="html">
10125 <source>Upload</source><target state="new">Upload</target> 10183 <source>Upload</source><target state="new">Upload</target>
10126 10184
10127 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10185 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10128 <trans-unit id="8278735427925094503" datatype="html"> 10186 <trans-unit id="8278735427925094503" datatype="html">
10129 <source>Upload 10187 <source>Upload
10130 <x id="PH"/> 10188 <x id="PH"/>
@@ -10133,13 +10191,13 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10133 <x id="PH"/> 10191 <x id="PH"/>
10134 </target> 10192 </target>
10135 10193
10136 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10194 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10137 10195
10138 <trans-unit id="5981816353437801748" datatype="html"> 10196 <trans-unit id="5981816353437801748" datatype="html">
10139 <source>Video published.</source> 10197 <source>Video published.</source>
10140 <target state="new">Video published.</target> 10198 <target state="new">Video published.</target>
10141 10199
10142 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10200 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10143 10201
10144 10202
10145 <trans-unit id="764164089183618119" datatype="html"> 10203 <trans-unit id="764164089183618119" datatype="html">
@@ -10203,26 +10261,26 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10203 <trans-unit id="961774488937452220" datatype="html"> 10261 <trans-unit id="961774488937452220" datatype="html">
10204 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10262 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source><target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10205 10263
10206 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html"> 10264 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit><trans-unit id="5761611056224181752" datatype="html">
10207 <source>Redirection</source><target state="new">Redirection</target> 10265 <source>Redirection</source><target state="new">Redirection</target>
10208 10266
10209 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10267 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10210 10268
10211 <trans-unit id="8858527736400081688" datatype="html"> 10269 <trans-unit id="8858527736400081688" datatype="html">
10212 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10270 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10213 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target> 10271 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target>
10214 10272
10215 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10273 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10216 <trans-unit id="3937119019020041049" datatype="html"> 10274 <trans-unit id="3937119019020041049" datatype="html">
10217 <source>Mature or explicit content</source> 10275 <source>Mature or explicit content</source>
10218 <target state="new">Mature or explicit content</target> 10276 <target state="new">Mature or explicit content</target>
10219 10277
10220 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10278 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10221 <trans-unit id="1755474755114288376" datatype="html"> 10279 <trans-unit id="1755474755114288376" datatype="html">
10222 <source>Up Next</source> 10280 <source>Up Next</source>
10223 <target state="new">Up Next</target> 10281 <target state="new">Up Next</target>
10224 10282
10225 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html"> 10283 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit><trans-unit id="2159130950882492111" datatype="html">
10226 <source>Cancel</source><target state="new">Cancel</target> 10284 <source>Cancel</source><target state="new">Cancel</target>
10227 10285
10228 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit> 10286 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">646</context></context-group></trans-unit>
@@ -10230,62 +10288,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10230 <source>Autoplay is suspended</source> 10288 <source>Autoplay is suspended</source>
10231 <target state="new">Autoplay is suspended</target> 10289 <target state="new">Autoplay is suspended</target>
10232 10290
10233 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10291 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10234 <trans-unit id="7895294730547405228" datatype="html"> 10292 <trans-unit id="7895294730547405228" datatype="html">
10235 <source>Enter/exit fullscreen (requires player focus)</source> 10293 <source>Enter/exit fullscreen (requires player focus)</source>
10236 <target state="new">Enter/exit fullscreen (requires player focus)</target> 10294 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10237 10295
10238 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10296 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10239 <trans-unit id="7618388257165864759" datatype="html"> 10297 <trans-unit id="7618388257165864759" datatype="html">
10240 <source>Play/Pause the video (requires player focus)</source> 10298 <source>Play/Pause the video (requires player focus)</source>
10241 <target state="new">Play/Pause the video (requires player focus)</target> 10299 <target state="new">Play/Pause the video (requires player focus)</target>
10242 10300
10243 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10301 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10244 <trans-unit id="7761890399634216630" datatype="html"> 10302 <trans-unit id="7761890399634216630" datatype="html">
10245 <source>Mute/unmute the video (requires player focus)</source> 10303 <source>Mute/unmute the video (requires player focus)</source>
10246 <target state="new">Mute/unmute the video (requires player focus)</target> 10304 <target state="new">Mute/unmute the video (requires player focus)</target>
10247 10305
10248 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10306 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10249 <trans-unit id="5996585232248234904" datatype="html"> 10307 <trans-unit id="5996585232248234904" datatype="html">
10250 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10308 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10251 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10309 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10252 10310
10253 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10311 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10254 <trans-unit id="3748765405903319998" datatype="html"> 10312 <trans-unit id="3748765405903319998" datatype="html">
10255 <source>Increase the volume (requires player focus)</source> 10313 <source>Increase the volume (requires player focus)</source>
10256 <target state="new">Increase the volume (requires player focus)</target> 10314 <target state="new">Increase the volume (requires player focus)</target>
10257 10315
10258 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10316 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10259 <trans-unit id="5810704036407159982" datatype="html"> 10317 <trans-unit id="5810704036407159982" datatype="html">
10260 <source>Decrease the volume (requires player focus)</source> 10318 <source>Decrease the volume (requires player focus)</source>
10261 <target state="new">Decrease the volume (requires player focus)</target> 10319 <target state="new">Decrease the volume (requires player focus)</target>
10262 10320
10263 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10321 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10264 <trans-unit id="2622048822548065691" datatype="html"> 10322 <trans-unit id="2622048822548065691" datatype="html">
10265 <source>Seek the video forward (requires player focus)</source> 10323 <source>Seek the video forward (requires player focus)</source>
10266 <target state="new">Seek the video forward (requires player focus)</target> 10324 <target state="new">Seek the video forward (requires player focus)</target>
10267 10325
10268 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10326 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10269 <trans-unit id="6540078205109221153" datatype="html"> 10327 <trans-unit id="6540078205109221153" datatype="html">
10270 <source>Seek the video backward (requires player focus)</source> 10328 <source>Seek the video backward (requires player focus)</source>
10271 <target state="new">Seek the video backward (requires player focus)</target> 10329 <target state="new">Seek the video backward (requires player focus)</target>
10272 10330
10273 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10331 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10274 <trans-unit id="1956491957766210808" datatype="html"> 10332 <trans-unit id="1956491957766210808" datatype="html">
10275 <source>Increase playback rate (requires player focus)</source> 10333 <source>Increase playback rate (requires player focus)</source>
10276 <target state="new">Increase playback rate (requires player focus)</target> 10334 <target state="new">Increase playback rate (requires player focus)</target>
10277 10335
10278 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10336 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10279 <trans-unit id="5495529997674803186" datatype="html"> 10337 <trans-unit id="5495529997674803186" datatype="html">
10280 <source>Decrease playback rate (requires player focus)</source> 10338 <source>Decrease playback rate (requires player focus)</source>
10281 <target state="new">Decrease playback rate (requires player focus)</target> 10339 <target state="new">Decrease playback rate (requires player focus)</target>
10282 10340
10283 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10341 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10284 <trans-unit id="3178343147230721210" datatype="html"> 10342 <trans-unit id="3178343147230721210" datatype="html">
10285 <source>Navigate in the video frame by frame (requires player focus)</source> 10343 <source>Navigate in the video frame by frame (requires player focus)</source>
10286 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 10344 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10287 10345
10288 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10346 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10289 <trans-unit id="8025996572234182184" datatype="html"> 10347 <trans-unit id="8025996572234182184" datatype="html">
10290 <source>Like the video</source> 10348 <source>Like the video</source>
10291 <target state="new">Like the video</target> 10349 <target state="new">Like the video</target>
diff --git a/client/src/locale/angular.th-TH.xlf b/client/src/locale/angular.th-TH.xlf
index 4cfdc4f61..7ffd05f1a 100644
--- a/client/src/locale/angular.th-TH.xlf
+++ b/client/src/locale/angular.th-TH.xlf
@@ -242,19 +242,19 @@
242 <target state="new"> 242 <target state="new">
243 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 243 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
244 </target> 244 </target>
245 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group> 245
246 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group> 246
247 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group> 247
248 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group> 248
249 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group> 249
250 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 250
251 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group> 251
252 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group> 252
253 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group> 253
254 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group> 254
255 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group> 255
256 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group> 256
257 </trans-unit> 257 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
258 <trans-unit id="1486537403020619891" datatype="html"> 258 <trans-unit id="1486537403020619891" datatype="html">
259 <source>My watch history</source> 259 <source>My watch history</source>
260 <target state="new">My watch history</target> 260 <target state="new">My watch history</target>
@@ -323,22 +323,22 @@
323 <trans-unit id="5674286808255988565"> 323 <trans-unit id="5674286808255988565">
324 <source>Create</source> 324 <source>Create</source>
325 <target>สร้าง</target> 325 <target>สร้าง</target>
326 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 326
327 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 327
328 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 328
329 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 329
330 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 330
331 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 331
332 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 332
333 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 333
334 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 334
335 </trans-unit> 335 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
336 <trans-unit id="1006562256968398209" datatype="html"> 336 <trans-unit id="1006562256968398209" datatype="html">
337 <source>video</source> 337 <source>video</source>
338 <target state="translated">วิดีโอ</target> 338 <target state="translated">วิดีโอ</target>
339 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group> 339
340 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group> 340
341 </trans-unit> 341 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
342 <trans-unit id="6438815964972582865" datatype="html"> 342 <trans-unit id="6438815964972582865" datatype="html">
343 <source>The following link contains a private token and should not be shared with anyone.</source> 343 <source>The following link contains a private token and should not be shared with anyone.</source>
344 <target state="new"> The following link contains a private token and should not be shared with anyone. </target> 344 <target state="new"> The following link contains a private token and should not be shared with anyone. </target>
@@ -410,13 +410,13 @@
410 <trans-unit id="6995024616159044376" datatype="html"> 410 <trans-unit id="6995024616159044376" datatype="html">
411 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 411 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
412 <target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 412 <target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
413 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 413
414 </trans-unit> 414 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
415 <trans-unit id="7873395933409147217" datatype="html"> 415 <trans-unit id="7873395933409147217" datatype="html">
416 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 416 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
417 <target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 417 <target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
418 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 418
419 </trans-unit> 419 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
420 <trans-unit id="5235042777215655908" datatype="html"> 420 <trans-unit id="5235042777215655908" datatype="html">
421 <source>subtitles</source> 421 <source>subtitles</source>
422 <target state="translated">คำบรรยายใต้ภาพ</target> 422 <target state="translated">คำบรรยายใต้ภาพ</target>
@@ -872,10 +872,10 @@
872 <trans-unit id="2602586221576511475"> 872 <trans-unit id="2602586221576511475">
873 <source>Video quota</source> 873 <source>Video quota</source>
874 <target>ปริมาณวิดีโอที่สามารถอัปโหลดได้</target> 874 <target>ปริมาณวิดีโอที่สามารถอัปโหลดได้</target>
875 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 875
876 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 876
877 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 877
878 </trans-unit> 878 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
879 <trans-unit id="1502595455339510144"> 879 <trans-unit id="1502595455339510144">
880 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 880 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
881 <target> 881 <target>
@@ -960,6 +960,30 @@
960 <target state="translated">รวมเว็บไซต์อื่น</target> 960 <target state="translated">รวมเว็บไซต์อื่น</target>
961 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 961 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
962 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 962 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
963 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
964 <source>Following</source><target state="new">Following</target>
965 <context-group purpose="location">
966 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
967 <context context-type="linenumber">29</context>
968 </context-group>
969 <context-group purpose="location">
970 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
971 <context context-type="linenumber">31</context>
972 </context-group>
973 <context-group purpose="location">
974 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
975 <context context-type="linenumber">28</context>
976 </context-group>
977 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
978 <source>Followers</source><target state="new">Followers</target>
979 <context-group purpose="location">
980 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
981 <context context-type="linenumber">34</context>
982 </context-group>
983 <context-group purpose="location">
984 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
985 <context context-type="linenumber">37</context>
986 </context-group>
963 </trans-unit> 987 </trans-unit>
964 <trans-unit id="3541687134897970106" datatype="html"> 988 <trans-unit id="3541687134897970106" datatype="html">
965 <source>followers</source> 989 <source>followers</source>
@@ -1023,25 +1047,25 @@
1023 <trans-unit id="2159130950882492111"> 1047 <trans-unit id="2159130950882492111">
1024 <source>Cancel</source> 1048 <source>Cancel</source>
1025 <target>ยกเลิก</target> 1049 <target>ยกเลิก</target>
1026 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group> 1050
1027 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group> 1051
1028 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group> 1052
1029 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group> 1053
1030 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group> 1054
1031 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group> 1055
1032 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group> 1056
1033 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group> 1057
1034 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group> 1058
1035 <context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group> 1059
1036 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group> 1060
1037 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group> 1061
1038 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 1062
1039 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 1063
1040 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group> 1064
1041 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group> 1065
1042 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group> 1066
1043 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group> 1067
1044 </trans-unit> 1068 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
1045 <trans-unit id="3616223838716839702"> 1069 <trans-unit id="3616223838716839702">
1046 <source>Ban this user</source> 1070 <source>Ban this user</source>
1047 <target>แบนผู้ใช้นี้</target> 1071 <target>แบนผู้ใช้นี้</target>
@@ -1107,19 +1131,13 @@
1107 <trans-unit id="7252854992688790751" datatype="html"> 1131 <trans-unit id="7252854992688790751" datatype="html">
1108 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1132 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1109 <target state="translated">เซิร์ฟเวอร์นี้เปิดให้ลงทะเบียนผู้ใช้ใหม่ อย่างไรก็ตาม โปรดตรวจสอบ<x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>เงื่อนไข<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>เงื่อนไข<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>อย่างระมัดระวังก่อนการสร้างบัญชี คุณสามารถหาเซิร์ฟเวอร์อื่นที่ตรงกับความต้องการของคุณโดยเฉพาะได้ที่: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> </target> 1133 <target state="translated">เซิร์ฟเวอร์นี้เปิดให้ลงทะเบียนผู้ใช้ใหม่ อย่างไรก็ตาม โปรดตรวจสอบ<x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>เงื่อนไข<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>เงื่อนไข<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>อย่างระมัดระวังก่อนการสร้างบัญชี คุณสามารถหาเซิร์ฟเวอร์อื่นที่ตรงกับความต้องการของคุณโดยเฉพาะได้ที่: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> </target>
1110 <context-group purpose="location"> 1134
1111 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1135 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1112 <context context-type="linenumber">60,62</context>
1113 </context-group>
1114 </trans-unit>
1115 <trans-unit id="7215649348148521605" datatype="html"> 1136 <trans-unit id="7215649348148521605" datatype="html">
1116 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1137 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1117 <target state="translated">ณ ตอนนี้ เซิร์ฟเวอร์นี้ไม่เปิดให้ลงทะเบียนผู้ใช้ใหม่ คุณสามารถตรวจสอบ<x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>เงื่อนไข<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>เพื่อดูข้อมูลเพิ่มเติมหรือหาเซิร์ฟเวอร์อื่นที่ให้คุณสร้างบัญชีและอัปโหลดวิดีโอได้ หาเว็บไซต์ที่เหมาะกับคุณได้ที่: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> </target> 1138 <target state="translated">ณ ตอนนี้ เซิร์ฟเวอร์นี้ไม่เปิดให้ลงทะเบียนผู้ใช้ใหม่ คุณสามารถตรวจสอบ<x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>เงื่อนไข<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>เพื่อดูข้อมูลเพิ่มเติมหรือหาเซิร์ฟเวอร์อื่นที่ให้คุณสร้างบัญชีและอัปโหลดวิดีโอได้ หาเว็บไซต์ที่เหมาะกับคุณได้ที่: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> </target>
1118 <context-group purpose="location"> 1139
1119 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1140 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1120 <context context-type="linenumber">65,67</context>
1121 </context-group>
1122 </trans-unit>
1123 <trans-unit id="2392488717875840729"> 1141 <trans-unit id="2392488717875840729">
1124 <source>User</source> 1142 <source>User</source>
1125 <target>ผู้ใช้</target> 1143 <target>ผู้ใช้</target>
@@ -1130,67 +1148,67 @@
1130 <source>Username or email address</source> 1148 <source>Username or email address</source>
1131 <target>ชื่อผู้ใช้หรือที่อยู่อีเมล</target> 1149 <target>ชื่อผู้ใช้หรือที่อยู่อีเมล</target>
1132 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1150 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1151 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1152 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1153 <context-group purpose="location">
1154 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1155 <context context-type="linenumber">33,34</context>
1156 </context-group>
1133 </trans-unit> 1157 </trans-unit>
1134 <trans-unit id="1431416938026210429"> 1158 <trans-unit id="1431416938026210429">
1135 <source>Password</source> 1159 <source>Password</source>
1136 <target>รหัสผ่าน</target> 1160 <target>รหัสผ่าน</target>
1137 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1161
1138 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1162
1139 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1163
1140 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1164
1141 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1165
1142 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1166
1143 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1167
1144 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1168
1145 </trans-unit> 1169 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1146 <trans-unit id="8715156686857791956" datatype="html"> 1170 <trans-unit id="8715156686857791956" datatype="html">
1147 <source>Click here to reset your password</source> 1171 <source>Click here to reset your password</source>
1148 <target state="translated">คลิกที่นี่เพื่อรีเซ็ตรหัสผ่าน</target> 1172 <target state="translated">คลิกที่นี่เพื่อรีเซ็ตรหัสผ่าน</target>
1149 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1173
1150 </trans-unit> 1174 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1151 <trans-unit id="892063502898494584" datatype="html"> 1175 <trans-unit id="892063502898494584" datatype="html">
1152 <source>I forgot my password</source> 1176 <source>I forgot my password</source>
1153 <target state="translated">ฉันลืมรหัสผ่าน</target> 1177 <target state="translated">ฉันลืมรหัสผ่าน</target>
1154 <context-group purpose="location"> 1178
1155 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1179 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1156 <context context-type="linenumber">47</context>
1157 </context-group>
1158 </trans-unit>
1159 <trans-unit id="2101170466365500913" datatype="html"> 1180 <trans-unit id="2101170466365500913" datatype="html">
1160 <source>Logging into an account lets you publish content</source> 1181 <source>Logging into an account lets you publish content</source>
1161 <target state="translated">การเข้าสู่ระบบทำให้คุณสามารถเผยแพร่เนื้อหา</target> 1182 <target state="translated">การเข้าสู่ระบบทำให้คุณสามารถเผยแพร่เนื้อหา</target>
1162 <context-group purpose="location"> 1183
1163 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1184 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1164 <context context-type="linenumber">56,57</context>
1165 </context-group>
1166 </trans-unit>
1167 <trans-unit id="2454050363478003966"> 1185 <trans-unit id="2454050363478003966">
1168 <source>Login</source> 1186 <source>Login</source>
1169 <target>เข้าสู่ระบบ</target> 1187 <target>เข้าสู่ระบบ</target>
1170 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1188
1171 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1189
1172 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1190
1173 </trans-unit> 1191 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1174 <trans-unit id="3183213940445113677" datatype="html"> 1192 <trans-unit id="3183213940445113677" datatype="html">
1175 <source>Or sign in with</source> 1193 <source>Or sign in with</source>
1176 <target state="translated">หรือเข้าสู่ระบบด้วย</target> 1194 <target state="translated">หรือเข้าสู่ระบบด้วย</target>
1177 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1195
1178 </trans-unit> 1196 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1179 <trans-unit id="3238209155172574367"> 1197 <trans-unit id="3238209155172574367">
1180 <source>Forgot your password</source> 1198 <source>Forgot your password</source>
1181 <target>ลืมรหัสผ่าน</target> 1199 <target>ลืมรหัสผ่าน</target>
1182 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1200
1183 </trans-unit> 1201 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1184 <trans-unit id="87327320394367488" datatype="html"> 1202 <trans-unit id="87327320394367488" datatype="html">
1185 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1203 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1186 <target state="translated">ขออภัย คุณไม่สามารถกู้คืนรหัสผ่านของคุณเนื่องจากผู้ดูแลระบบไม่ได้ตั้งค่าระบบอีเมล PeerTube</target> 1204 <target state="translated">ขออภัย คุณไม่สามารถกู้คืนรหัสผ่านของคุณเนื่องจากผู้ดูแลระบบไม่ได้ตั้งค่าระบบอีเมล PeerTube</target>
1187 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1205
1188 </trans-unit> 1206 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1189 <trans-unit id="3188014010833256853" datatype="html"> 1207 <trans-unit id="3188014010833256853" datatype="html">
1190 <source>Enter your email address and we will send you a link to reset your password.</source> 1208 <source>Enter your email address and we will send you a link to reset your password.</source>
1191 <target state="translated">ใส่ที่อยู่อีเมลของคุณ เราจะส่งลิงก์เพื่อรีเซ็ตรหัสผ่านของคุณทางอีเมล</target> 1209 <target state="translated">ใส่ที่อยู่อีเมลของคุณ เราจะส่งลิงก์เพื่อรีเซ็ตรหัสผ่านของคุณทางอีเมล</target>
1192 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1210
1193 </trans-unit> 1211 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1194 <trans-unit id="1190256911880544559" datatype="html"> 1212 <trans-unit id="1190256911880544559" datatype="html">
1195 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1213 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1196The link will expire within 1 hour.</source> 1214The link will expire within 1 hour.</source>
@@ -1200,26 +1218,26 @@ The link will expire within 1 hour.</source>
1200 <trans-unit id="4768749765465246664"> 1218 <trans-unit id="4768749765465246664">
1201 <source>Email</source> 1219 <source>Email</source>
1202 <target>อีเมล</target> 1220 <target>อีเมล</target>
1203 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1221
1204 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1222
1205 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1223
1206 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1224
1207 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1225
1208 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1226
1209 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1227
1210 </trans-unit> 1228 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1211 <trans-unit id="3967269098753656610"> 1229 <trans-unit id="3967269098753656610">
1212 <source>Email address</source> 1230 <source>Email address</source>
1213 <target>ที่อยู่อีเมล</target> 1231 <target>ที่อยู่อีเมล</target>
1214 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1232
1215 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1233
1216 </trans-unit> 1234 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1217 <trans-unit id="7808756054397155068" datatype="html"> 1235 <trans-unit id="7808756054397155068" datatype="html">
1218 <source>Reset</source> 1236 <source>Reset</source>
1219 <target state="translated">รีเซ็ต</target> 1237 <target state="translated">รีเซ็ต</target>
1220 <note priority="1" from="description">Password reset button</note> 1238 <note priority="1" from="description">Password reset button</note>
1221 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1239
1222 </trans-unit> 1240 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1223 <trans-unit id="4319634264526091601" datatype="html"> 1241 <trans-unit id="4319634264526091601" datatype="html">
1224 <source>on this instance</source> 1242 <source>on this instance</source>
1225 <target state="translated">บนเซิร์ฟเวอร์นี้</target> 1243 <target state="translated">บนเซิร์ฟเวอร์นี้</target>
@@ -1586,9 +1604,9 @@ The link will expire within 1 hour.</source>
1586 <trans-unit id="2308975396733519902"> 1604 <trans-unit id="2308975396733519902">
1587 <source>Create an account</source> 1605 <source>Create an account</source>
1588 <target>สร้างบัญชีผู้ใช้</target> 1606 <target>สร้างบัญชีผู้ใช้</target>
1589 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1607
1590 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1608
1591 </trans-unit> 1609 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1592 <trans-unit id="3058024914967508975" datatype="html"> 1610 <trans-unit id="3058024914967508975" datatype="html">
1593 <source>My videos</source> 1611 <source>My videos</source>
1594 <target state="translated">วิดีโอของฉัน</target> 1612 <target state="translated">วิดีโอของฉัน</target>
@@ -1655,10 +1673,10 @@ The link will expire within 1 hour.</source>
1655 <trans-unit id="1504521795586863905" datatype="html"> 1673 <trans-unit id="1504521795586863905" datatype="html">
1656 <source>VIDEOS</source> 1674 <source>VIDEOS</source>
1657 <target state="translated">วิดีโอ</target> 1675 <target state="translated">วิดีโอ</target>
1658 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1676
1659 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1677
1660 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1678
1661 </trans-unit> 1679 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1662 <trans-unit id="667372110624203230" datatype="html"> 1680 <trans-unit id="667372110624203230" datatype="html">
1663 <source>Import jobs concurrency</source> 1681 <source>Import jobs concurrency</source>
1664 <target state="new">Import jobs concurrency</target> 1682 <target state="new">Import jobs concurrency</target>
@@ -1737,8 +1755,8 @@ The link will expire within 1 hour.</source>
1737 <trans-unit id="4424964105331349857" datatype="html"> 1755 <trans-unit id="4424964105331349857" datatype="html">
1738 <source>I'm a teapot</source> 1756 <source>I'm a teapot</source>
1739 <target state="translated">ฉันเป็นกาน้ำชา</target> 1757 <target state="translated">ฉันเป็นกาน้ำชา</target>
1740 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1758
1741 </trans-unit> 1759 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1742 <trans-unit id="1597262876035959248" datatype="html"> 1760 <trans-unit id="1597262876035959248" datatype="html">
1743 <source>That's an error.</source> 1761 <source>That's an error.</source>
1744 <target state="translated">เกิดข้อผิดพลาด</target> 1762 <target state="translated">เกิดข้อผิดพลาด</target>
@@ -1831,8 +1849,8 @@ The link will expire within 1 hour.</source>
1831 <trans-unit id="2971365540217107489" datatype="html"> 1849 <trans-unit id="2971365540217107489" datatype="html">
1832 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1850 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1833 <target state="translated">สื่อมีขนาดใหญ่เกินที่จะอยู่บนเซิร์ฟเวอร์ โปรดติดต่อผู้ดูแลระบบหากคุณต้องการเพิ่มขีดจำกัดขนาด</target> 1851 <target state="translated">สื่อมีขนาดใหญ่เกินที่จะอยู่บนเซิร์ฟเวอร์ โปรดติดต่อผู้ดูแลระบบหากคุณต้องการเพิ่มขีดจำกัดขนาด</target>
1834 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1852
1835 </trans-unit> 1853 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1836 <trans-unit id="5131854469652959713" datatype="html"> 1854 <trans-unit id="5131854469652959713" datatype="html">
1837 <source>GLOBAL SEARCH</source> 1855 <source>GLOBAL SEARCH</source>
1838 <target state="translated">ค้นหาทุกเซิร์ฟเวอร์</target> 1856 <target state="translated">ค้นหาทุกเซิร์ฟเวอร์</target>
@@ -2580,8 +2598,8 @@ The link will expire within 1 hour.</source>
2580 <trans-unit id="6161604372916832458" datatype="html"> 2598 <trans-unit id="6161604372916832458" datatype="html">
2581 <source>Upload on hold</source> 2599 <source>Upload on hold</source>
2582 <target state="new">Upload on hold</target> 2600 <target state="new">Upload on hold</target>
2583 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2601
2584 </trans-unit> 2602 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2585 <trans-unit id="285180972645018518" datatype="html"> 2603 <trans-unit id="285180972645018518" datatype="html">
2586 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2604 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2587 <target state="translated">ขออภัย คุณสมบัติการอัปโหลดถูกปิดใช้งานสำหรับบัญชีของคุณ หากคุณต้องการเพิ่มวิดีโอ ผู้ดูแลระบบต้องปลดล็อกโควต้าของคุณก่อน</target> 2605 <target state="translated">ขออภัย คุณสมบัติการอัปโหลดถูกปิดใช้งานสำหรับบัญชีของคุณ หากคุณต้องการเพิ่มวิดีโอ ผู้ดูแลระบบต้องปลดล็อกโควต้าของคุณก่อน</target>
@@ -3285,11 +3303,7 @@ The link will expire within 1 hour.</source>
3285 <target state="new">ID</target> 3303 <target state="new">ID</target>
3286 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3304 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3287 </trans-unit> 3305 </trans-unit>
3288 <trans-unit id="2265605798180116441" datatype="html"> 3306
3289 <source>Follower handle</source>
3290 <target state="new">Follower handle</target>
3291 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3292 </trans-unit>
3293 <trans-unit id="5911214550882917183" datatype="html"> 3307 <trans-unit id="5911214550882917183" datatype="html">
3294 <source>State</source> 3308 <source>State</source>
3295 <target state="new">State</target> 3309 <target state="new">State</target>
@@ -3364,11 +3378,7 @@ The link will expire within 1 hour.</source>
3364 </target> 3378 </target>
3365 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3379 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3366 </trans-unit> 3380 </trans-unit>
3367 <trans-unit id="6641024648411549335" datatype="html"> 3381
3368 <source>Host</source>
3369 <target state="new">Host</target>
3370 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3371 </trans-unit>
3372 <trans-unit id="6571718060636962350" datatype="html"> 3382 <trans-unit id="6571718060636962350" datatype="html">
3373 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3383 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3374 <target state="new">Redundancy allowed 3384 <target state="new">Redundancy allowed
@@ -3380,9 +3390,9 @@ The link will expire within 1 hour.</source>
3380 <trans-unit id="9160510009013134726" datatype="html"> 3390 <trans-unit id="9160510009013134726" datatype="html">
3381 <source>Unfollow</source> 3391 <source>Unfollow</source>
3382 <target state="new">Unfollow</target> 3392 <target state="new">Unfollow</target>
3383 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3393
3384 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3394
3385 </trans-unit> 3395 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3386 <trans-unit id="8246779176913476983" datatype="html"> 3396 <trans-unit id="8246779176913476983" datatype="html">
3387 <source>Open instance in a new tab</source> 3397 <source>Open instance in a new tab</source>
3388 <target state="new">Open instance in a new tab</target> 3398 <target state="new">Open instance in a new tab</target>
@@ -3393,13 +3403,13 @@ The link will expire within 1 hour.</source>
3393 <trans-unit id="9132918641931433659" datatype="html"> 3403 <trans-unit id="9132918641931433659" datatype="html">
3394 <source>No host found matching current filters.</source> 3404 <source>No host found matching current filters.</source>
3395 <target state="new">No host found matching current filters.</target> 3405 <target state="new">No host found matching current filters.</target>
3396 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3406
3397 </trans-unit> 3407 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3398 <trans-unit id="7274241885665071790" datatype="html"> 3408 <trans-unit id="7274241885665071790" datatype="html">
3399 <source>Your instance is not following anyone.</source> 3409 <source>Your instance is not following anyone.</source>
3400 <target state="new">Your instance is not following anyone.</target> 3410 <target state="new">Your instance is not following anyone.</target>
3401 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3411
3402 </trans-unit> 3412 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3403 <trans-unit id="4774348799569692380" datatype="html"> 3413 <trans-unit id="4774348799569692380" datatype="html">
3404 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3414 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3405 <target state="new">Showing 3415 <target state="new">Showing
@@ -3409,16 +3419,8 @@ The link will expire within 1 hour.</source>
3409 </target> 3419 </target>
3410 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3420 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3411 </trans-unit> 3421 </trans-unit>
3412 <trans-unit id="6275803119759621687" datatype="html"> 3422
3413 <source>Follow domains</source> 3423
3414 <target state="new">Follow domains</target>
3415 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3416 </trans-unit>
3417 <trans-unit id="1268699198448750610" datatype="html">
3418 <source>Follow instances</source>
3419 <target state="new">Follow instances</target>
3420 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3421 </trans-unit>
3422 <trans-unit id="9216117865911519658" datatype="html"> 3424 <trans-unit id="9216117865911519658" datatype="html">
3423 <source>Action</source> 3425 <source>Action</source>
3424 <target state="new">Action</target> 3426 <target state="new">Action</target>
@@ -3468,11 +3470,11 @@ The link will expire within 1 hour.</source>
3468 <trans-unit id="5248717555542428023"> 3470 <trans-unit id="5248717555542428023">
3469 <source>Username</source> 3471 <source>Username</source>
3470 <target>ชื่อผู้ใช้</target> 3472 <target>ชื่อผู้ใช้</target>
3471 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3473
3472 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3474
3473 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3475
3474 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3476
3475 </trans-unit> 3477 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3476 <trans-unit id="5428411040014095392" datatype="html"> 3478 <trans-unit id="5428411040014095392" datatype="html">
3477 <source>e.g. jane_doe</source> 3479 <source>e.g. jane_doe</source>
3478 <target state="new">e.g. jane_doe</target> 3480 <target state="new">e.g. jane_doe</target>
@@ -3502,9 +3504,9 @@ The link will expire within 1 hour.</source>
3502 <trans-unit id="4145496584631696119" datatype="html"> 3504 <trans-unit id="4145496584631696119" datatype="html">
3503 <source>Role</source> 3505 <source>Role</source>
3504 <target state="translated">หน้าที่</target> 3506 <target state="translated">หน้าที่</target>
3505 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3507
3506 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3508
3507 </trans-unit> 3509 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3508 <trans-unit id="7046347992315328430" datatype="html"> 3510 <trans-unit id="7046347992315328430" datatype="html">
3509 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3511 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3510 <target state="new"> 3512 <target state="new">
@@ -3529,15 +3531,9 @@ The link will expire within 1 hour.</source>
3529 <trans-unit id="2622255144026150901" datatype="html"> 3531 <trans-unit id="2622255144026150901" datatype="html">
3530 <source>Auth plugin</source> 3532 <source>Auth plugin</source>
3531 <target state="new">Auth plugin</target> 3533 <target state="new">Auth plugin</target>
3532 <context-group purpose="location"> 3534
3533 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3535
3534 <context context-type="linenumber">188</context> 3536 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3535 </context-group>
3536 <context-group purpose="location">
3537 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3538 <context context-type="linenumber">188</context>
3539 </context-group>
3540 </trans-unit>
3541 <trans-unit id="588099657508661970" datatype="html"> 3537 <trans-unit id="588099657508661970" datatype="html">
3542 <source>None (local authentication)</source> 3538 <source>None (local authentication)</source>
3543 <target state="new">None (local authentication)</target> 3539 <target state="new">None (local authentication)</target>
@@ -3802,6 +3798,12 @@ The link will expire within 1 hour.</source>
3802 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3798 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3803 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3799 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3804 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3800 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3801 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3802 <source>Follower</source><target state="new">Follower</target>
3803 <context-group purpose="location">
3804 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3805 <context context-type="linenumber">24</context>
3806 </context-group>
3805 </trans-unit> 3807 </trans-unit>
3806 <trans-unit id="4691552465058437520" datatype="html"> 3808 <trans-unit id="4691552465058437520" datatype="html">
3807 <source>Commented video</source> 3809 <source>Commented video</source>
@@ -4138,8 +4140,8 @@ The link will expire within 1 hour.</source>
4138 <target state="new"> 4140 <target state="new">
4139 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 4141 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
4140 </target> 4142 </target>
4141 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 4143
4142 </trans-unit> 4144 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
4143 <trans-unit id="4058814854824495833" datatype="html"> 4145 <trans-unit id="4058814854824495833" datatype="html">
4144 <source>Mute domains</source> 4146 <source>Mute domains</source>
4145 <target state="new">Mute domains</target> 4147 <target state="new">Mute domains</target>
@@ -6128,11 +6130,8 @@ color: red;
6128 <trans-unit id="5512878593724620692" datatype="html"> 6130 <trans-unit id="5512878593724620692" datatype="html">
6129 <source>CHANNELS</source> 6131 <source>CHANNELS</source>
6130 <target state="translated">ช่อง</target> 6132 <target state="translated">ช่อง</target>
6131 <context-group purpose="location"> 6133
6132 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 6134 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
6133 <context context-type="linenumber">82</context>
6134 </context-group>
6135 </trans-unit>
6136 <trans-unit id="3666829335406793239" datatype="html"> 6135 <trans-unit id="3666829335406793239" datatype="html">
6137 <source>This account does not have channels.</source> 6136 <source>This account does not have channels.</source>
6138 <target state="translated">บัญชีนี้ไม่มีช่อง</target> 6137 <target state="translated">บัญชีนี้ไม่มีช่อง</target>
@@ -6175,6 +6174,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6175It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 6174It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
6176channel with the same name (<x id="PH_2"/>)!</target> 6175channel with the same name (<x id="PH_2"/>)!</target>
6177 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 6176 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
6177 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
6178 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
6179 <context-group purpose="location">
6180 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
6181 <context context-type="linenumber">48</context>
6182 </context-group>
6178 </trans-unit> 6183 </trans-unit>
6179 <trans-unit id="5387007581996837469" datatype="html"> 6184 <trans-unit id="5387007581996837469" datatype="html">
6180 <source>My Channels</source> 6185 <source>My Channels</source>
@@ -6737,13 +6742,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6737 <trans-unit id="6979021199788941693" datatype="html"> 6742 <trans-unit id="6979021199788941693" datatype="html">
6738 <source>Your message has been sent.</source> 6743 <source>Your message has been sent.</source>
6739 <target state="translated">ข้อความของคุณถูกส่งแล้ว</target> 6744 <target state="translated">ข้อความของคุณถูกส่งแล้ว</target>
6740 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group> 6745
6741 </trans-unit> 6746 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6742 <trans-unit id="2072135752262464360" datatype="html"> 6747 <trans-unit id="2072135752262464360" datatype="html">
6743 <source>You already sent this form recently</source> 6748 <source>You already sent this form recently</source>
6744 <target state="translated">คุณเพิ่งส่งฟอร์มนี้ไปเมื่อสักครู่</target> 6749 <target state="translated">คุณเพิ่งส่งฟอร์มนี้ไปเมื่อสักครู่</target>
6745 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group> 6750
6746 </trans-unit> 6751 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6747 <trans-unit id="819067926858619041" datatype="html"> 6752 <trans-unit id="819067926858619041" datatype="html">
6748 <source>Account videos</source> 6753 <source>Account videos</source>
6749 <target state="translated">วิดีโอในบัญชี</target> 6754 <target state="translated">วิดีโอในบัญชี</target>
@@ -6790,13 +6795,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6790 <target state="new"> 6795 <target state="new">
6791 <x id="PH"/> direct account followers 6796 <x id="PH"/> direct account followers
6792 </target> 6797 </target>
6793 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6798
6794 </trans-unit> 6799 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6795 <trans-unit id="6250999352462648289" datatype="html"> 6800 <trans-unit id="6250999352462648289" datatype="html">
6796 <source>Report this account</source> 6801 <source>Report this account</source>
6797 <target state="translated">รายงานบัญชีนี้</target> 6802 <target state="translated">รายงานบัญชีนี้</target>
6798 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6803
6799 </trans-unit> 6804 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6800 <trans-unit id="1504521795586863905" datatype="html"> 6805 <trans-unit id="1504521795586863905" datatype="html">
6801 <source>VIDEOS</source> 6806 <source>VIDEOS</source>
6802 <target state="translated">วิดีโอ</target> 6807 <target state="translated">วิดีโอ</target>
@@ -6806,31 +6811,21 @@ channel with the same name (<x id="PH_2"/>)!</target>
6806 <trans-unit id="25349740244798533" datatype="html"> 6811 <trans-unit id="25349740244798533" datatype="html">
6807 <source>Username copied</source> 6812 <source>Username copied</source>
6808 <target state="translated">คัดลอกชื่อผู้ใช้แล้ว</target> 6813 <target state="translated">คัดลอกชื่อผู้ใช้แล้ว</target>
6809 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6814
6810 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6815
6811 </trans-unit> 6816 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6812 <trans-unit id="9221735175659318025" datatype="html"> 6817 <trans-unit id="9221735175659318025" datatype="html">
6813 <source>1 subscriber</source> 6818 <source>1 subscriber</source>
6814 <target state="new">1 subscriber</target> 6819 <target state="new">1 subscriber</target>
6815 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6820
6816 </trans-unit> 6821 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6817 <trans-unit id="4097331874769079975" datatype="html"> 6822 <trans-unit id="4097331874769079975" datatype="html">
6818 <source><x id="PH"/> subscribers</source> 6823 <source><x id="PH"/> subscribers</source>
6819 <target state="translated">ผู้ติดตาม <x id="PH"/> คน</target> 6824 <target state="translated">ผู้ติดตาม <x id="PH"/> คน</target>
6820 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6825
6821 </trans-unit> 6826 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6822 <trans-unit id="4682675125751819107" datatype="html"> 6827
6823 <source>Instances you follow</source> 6828
6824 <target state="new">Instances you follow</target>
6825 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6826 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6827 </trans-unit>
6828 <trans-unit id="8899833753704589712" datatype="html">
6829 <source>Instances following you</source>
6830 <target state="new">Instances following you</target>
6831 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6832 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6833 </trans-unit>
6834 <trans-unit id="1035838766454786107" datatype="html"> 6829 <trans-unit id="1035838766454786107" datatype="html">
6835 <source>Audio-only</source> 6830 <source>Audio-only</source>
6836 <target state="translated">เฉพาะเสียง</target> 6831 <target state="translated">เฉพาะเสียง</target>
@@ -6880,6 +6875,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6880 <source>Auto (via ffmpeg)</source> 6875 <source>Auto (via ffmpeg)</source>
6881 <target state="translated">อัตโนมัติ (ผ่าน ffmpeg)</target> 6876 <target state="translated">อัตโนมัติ (ผ่าน ffmpeg)</target>
6882 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6877 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6878 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6879 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6880 <context-group purpose="location">
6881 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6882 <context context-type="linenumber">3</context>
6883 </context-group>
6883 </trans-unit> 6884 </trans-unit>
6884 <trans-unit id="931255636742351800" datatype="html"> 6885 <trans-unit id="931255636742351800" datatype="html">
6885 <source>No limit</source> 6886 <source>No limit</source>
@@ -7028,18 +7029,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
7028 <trans-unit id="2127446333083057097" datatype="html"> 7029 <trans-unit id="2127446333083057097" datatype="html">
7029 <source>Domain is required.</source> 7030 <source>Domain is required.</source>
7030 <target state="new">Domain is required.</target> 7031 <target state="new">Domain is required.</target>
7031 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 7032
7032 </trans-unit> 7033 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
7033 <trans-unit id="6780793142903080663" datatype="html"> 7034 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
7034 <source>Domains entered are invalid.</source> 7035 <context-group purpose="location">
7035 <target state="new">Domains entered are invalid.</target> 7036 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7036 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 7037 <context context-type="linenumber">93</context>
7037 </trans-unit> 7038 </context-group>
7038 <trans-unit id="5886492514458202177" datatype="html"> 7039 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
7039 <source>Domains entered contain duplicates.</source> 7040 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
7040 <target state="new">Domains entered contain duplicates.</target> 7041 <context-group purpose="location">
7041 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 7042 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7043 <context context-type="linenumber">94</context>
7044 </context-group>
7045 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
7046 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
7047 <context-group purpose="location">
7048 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7049 <context context-type="linenumber">102</context>
7050 </context-group>
7051 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
7052 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
7053 <context-group purpose="location">
7054 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
7055 <context context-type="linenumber">103</context>
7056 </context-group>
7042 </trans-unit> 7057 </trans-unit>
7058
7059
7043 <trans-unit id="240806681889331244" datatype="html"> 7060 <trans-unit id="240806681889331244" datatype="html">
7044 <source>Unlimited</source> 7061 <source>Unlimited</source>
7045 <target state="translated">ไม่จำกัด</target> 7062 <target state="translated">ไม่จำกัด</target>
@@ -7199,26 +7216,52 @@ channel with the same name (<x id="PH_2"/>)!</target>
7199 <x id="PH"/> removed from instance followers 7216 <x id="PH"/> removed from instance followers
7200 </target> 7217 </target>
7201 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 7218 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
7219 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
7220 <source>Follow</source><target state="new">Follow</target>
7221 <context-group purpose="location">
7222 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7223 <context context-type="linenumber">3</context>
7224 </context-group>
7225 <context-group purpose="location">
7226 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7227 <context context-type="linenumber">37</context>
7228 </context-group>
7229 <context-group purpose="location">
7230 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7231 <context context-type="linenumber">18</context>
7232 </context-group>
7233 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
7234 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
7235 <context-group purpose="location">
7236 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
7237 <context context-type="linenumber">11</context>
7238 </context-group>
7202 </trans-unit> 7239 </trans-unit>
7203 <trans-unit id="2740793005745065895" datatype="html"> 7240 <trans-unit id="2740793005745065895" datatype="html">
7204 <source><x id="PH"/> is not valid </source> 7241 <source><x id="PH"/> is not valid </source>
7205 <target state="new"> 7242 <target state="new">
7206 <x id="PH"/> is not valid 7243 <x id="PH"/> is not valid
7207 </target> 7244 </target>
7208 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 7245
7209 </trans-unit> 7246 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
7210 <trans-unit id="2355066641781598196" datatype="html"> 7247 <trans-unit id="2355066641781598196" datatype="html">
7211 <source>Follow request(s) sent!</source> 7248 <source>Follow request(s) sent!</source>
7212 <target state="new">Follow request(s) sent!</target> 7249 <target state="new">Follow request(s) sent!</target>
7213 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 7250
7251 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
7252 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
7253 <context-group purpose="location">
7254 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7255 <context context-type="linenumber">3</context>
7256 </context-group>
7214 </trans-unit> 7257 </trans-unit>
7215 <trans-unit id="4245720728052819482" datatype="html"> 7258 <trans-unit id="4245720728052819482" datatype="html">
7216 <source>Do you really want to unfollow <x id="PH"/>?</source> 7259 <source>Do you really want to unfollow <x id="PH"/>?</source>
7217 <target state="new">Do you really want to unfollow 7260 <target state="new">Do you really want to unfollow
7218 <x id="PH"/>? 7261 <x id="PH"/>?
7219 </target> 7262 </target>
7220 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 7263
7221 </trans-unit> 7264 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
7222 <trans-unit id="9160510009013134726" datatype="html"> 7265 <trans-unit id="9160510009013134726" datatype="html">
7223 <source>Unfollow</source> 7266 <source>Unfollow</source>
7224 <target state="new">Unfollow</target> 7267 <target state="new">Unfollow</target>
@@ -7229,8 +7272,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7229 <target state="new">You are not following 7272 <target state="new">You are not following
7230 <x id="PH"/> anymore. 7273 <x id="PH"/> anymore.
7231 </target> 7274 </target>
7232 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 7275
7233 </trans-unit> 7276 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
7234 <trans-unit id="2593763089859685916" datatype="html"> 7277 <trans-unit id="2593763089859685916" datatype="html">
7235 <source>enabled</source> 7278 <source>enabled</source>
7236 <target state="new">enabled</target> 7279 <target state="new">enabled</target>
@@ -7721,9 +7764,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7721 <trans-unit id="1519954996184640001" datatype="html"> 7764 <trans-unit id="1519954996184640001" datatype="html">
7722 <source>Error</source> 7765 <source>Error</source>
7723 <target state="translated">ข้อผิดพลาด</target> 7766 <target state="translated">ข้อผิดพลาด</target>
7724 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7767
7725 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7768
7726 </trans-unit> 7769 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7727 <trans-unit id="5076187961693950167" datatype="html"> 7770 <trans-unit id="5076187961693950167" datatype="html">
7728 <source>Standard logs</source> 7771 <source>Standard logs</source>
7729 <target state="new">Standard logs</target> 7772 <target state="new">Standard logs</target>
@@ -7768,16 +7811,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7768 <target state="new">Update user password</target> 7811 <target state="new">Update user password</target>
7769 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7812 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7770 </trans-unit> 7813 </trans-unit>
7771 <trans-unit id="177544274549739411" datatype="html"> 7814
7772 <source>Following list</source> 7815
7773 <target state="new">Following list</target>
7774 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7775 </trans-unit>
7776 <trans-unit id="8092429110007204784" datatype="html">
7777 <source>Followers list</source>
7778 <target state="new">Followers list</target>
7779 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7780 </trans-unit>
7781 <trans-unit id="780323526182667308" datatype="html"> 7816 <trans-unit id="780323526182667308" datatype="html">
7782 <source>User <x id="PH"/> updated.</source> 7817 <source>User <x id="PH"/> updated.</source>
7783 <target state="new">User 7818 <target state="new">User
@@ -7817,16 +7852,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7817 <target state="new">Federation</target> 7852 <target state="new">Federation</target>
7818 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7853 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7819 </trans-unit> 7854 </trans-unit>
7820 <trans-unit id="4682675125751819107" datatype="html"> 7855
7821 <source>Instances you follow</source> 7856
7822 <target state="new">Instances you follow</target>
7823 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7824 </trans-unit>
7825 <trans-unit id="8899833753704589712" datatype="html">
7826 <source>Instances following you</source>
7827 <target state="new">Instances following you</target>
7828 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7829 </trans-unit>
7830 <trans-unit id="3767259920053407667" datatype="html"> 7857 <trans-unit id="3767259920053407667" datatype="html">
7831 <source>Videos will be deleted, comments will be tombstoned.</source> 7858 <source>Videos will be deleted, comments will be tombstoned.</source>
7832 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7859 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7857,6 +7884,24 @@ channel with the same name (<x id="PH_2"/>)!</target>
7857 <target state="new">Set Email as Verified</target> 7884 <target state="new">Set Email as Verified</target>
7858 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7885 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7859 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7886 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7887 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7888 <source>Created</source><target state="new">Created</target>
7889 <context-group purpose="location">
7890 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7891 <context context-type="linenumber">115</context>
7892 </context-group>
7893 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7894 <source>Daily quota</source><target state="new">Daily quota</target>
7895 <context-group purpose="location">
7896 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7897 <context context-type="linenumber">120</context>
7898 </context-group>
7899 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7900 <source>Last login</source><target state="new">Last login</target>
7901 <context-group purpose="location">
7902 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7903 <context context-type="linenumber">122</context>
7904 </context-group>
7860 </trans-unit> 7905 </trans-unit>
7861 <trans-unit id="3403978719736970622" datatype="html"> 7906 <trans-unit id="3403978719736970622" datatype="html">
7862 <source>You cannot ban root.</source> 7907 <source>You cannot ban root.</source>
@@ -8166,13 +8211,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
8166 <trans-unit id="1137937154872046253" datatype="html"> 8211 <trans-unit id="1137937154872046253" datatype="html">
8167 <source>Video channel <x id="PH"/> created.</source> 8212 <source>Video channel <x id="PH"/> created.</source>
8168 <target state="translated">ช่องวิดีโอ <x id="PH"/> ถูกสร้างแล้ว</target> 8213 <target state="translated">ช่องวิดีโอ <x id="PH"/> ถูกสร้างแล้ว</target>
8169 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 8214
8170 </trans-unit> 8215 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
8171 <trans-unit id="8723777130353305761" datatype="html"> 8216 <trans-unit id="8723777130353305761" datatype="html">
8172 <source>This name already exists on this instance.</source> 8217 <source>This name already exists on this instance.</source>
8173 <target state="translated">ชื่อนี้มีอยู่ในเซิร์ฟเวอร์นี้แล้ว</target> 8218 <target state="translated">ชื่อนี้มีอยู่ในเซิร์ฟเวอร์นี้แล้ว</target>
8174 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 8219
8175 </trans-unit> 8220 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
8176 <trans-unit id="7589345916094713536" datatype="html"> 8221 <trans-unit id="7589345916094713536" datatype="html">
8177 <source>Video channel <x id="PH"/> updated.</source> 8222 <source>Video channel <x id="PH"/> updated.</source>
8178 <target state="translated">อัปเดตช่องวิดีโอ <x id="PH"/> แล้ว</target> 8223 <target state="translated">อัปเดตช่องวิดีโอ <x id="PH"/> แล้ว</target>
@@ -8193,11 +8238,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8193 <target state="new">Banner deleted.</target> 8238 <target state="new">Banner deleted.</target>
8194 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 8239 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
8195 </trans-unit> 8240 </trans-unit>
8196 <trans-unit id="2575302837003821736" datatype="html"> 8241
8197 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
8198 <target state="translated">กรุณาพิมพ์ชื่อแสดงของช่อง ( <x id="PH"/>) เพื่อยืนยัน</target>
8199 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
8200 </trans-unit>
8201 <trans-unit id="624066830180032195" datatype="html"> 8242 <trans-unit id="624066830180032195" datatype="html">
8202 <source>Video channel <x id="PH"/> deleted.</source> 8243 <source>Video channel <x id="PH"/> deleted.</source>
8203 <target state="translated">ลบช่องวิดีโอ <x id="PH"/> แล้ว</target> 8244 <target state="translated">ลบช่องวิดีโอ <x id="PH"/> แล้ว</target>
@@ -8361,6 +8402,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8361 <source>Ownership change request sent.</source> 8402 <source>Ownership change request sent.</source>
8362 <target state="translated">ส่งคำขอเปลี่ยนเจ้าของแล้ว</target> 8403 <target state="translated">ส่งคำขอเปลี่ยนเจ้าของแล้ว</target>
8363 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8404 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8405 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8406 <source>Sort by</source><target state="new">Sort by</target>
8407 <context-group purpose="location">
8408 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8409 <context context-type="linenumber">26</context>
8410 </context-group>
8364 </trans-unit> 8411 </trans-unit>
8365 <trans-unit id="3245220240937722814" datatype="html"> 8412 <trans-unit id="3245220240937722814" datatype="html">
8366 <source>My channels</source> 8413 <source>My channels</source>
@@ -8463,7 +8510,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8463 <target state="translated">ติดตามบัญชี</target> 8510 <target state="translated">ติดตามบัญชี</target>
8464 8511
8465 8512
8466 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8513 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8467 <trans-unit id="3131904093925601441" datatype="html"> 8514 <trans-unit id="3131904093925601441" datatype="html">
8468 <source>PLAYLISTS</source> 8515 <source>PLAYLISTS</source>
8469 <target state="translated">เพลย์ลิสต์</target> 8516 <target state="translated">เพลย์ลิสต์</target>
@@ -8510,35 +8557,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8510 <trans-unit id="3779524668013120370" datatype="html"> 8557 <trans-unit id="3779524668013120370" datatype="html">
8511 <source>Go to my subscriptions</source> 8558 <source>Go to my subscriptions</source>
8512 <target state="translated">ไปที่การติดตามของฉัน</target> 8559 <target state="translated">ไปที่การติดตามของฉัน</target>
8513 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8560
8514 </trans-unit> 8561 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8515 <trans-unit id="1136469849928650779" datatype="html"> 8562 <trans-unit id="1136469849928650779" datatype="html">
8516 <source>Go to my videos</source> 8563 <source>Go to my videos</source>
8517 <target state="translated">ไปที่วิดีโอของฉัน</target> 8564 <target state="translated">ไปที่วิดีโอของฉัน</target>
8518 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8565
8519 </trans-unit> 8566 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8520 <trans-unit id="7836683738999600376" datatype="html"> 8567 <trans-unit id="7836683738999600376" datatype="html">
8521 <source>Go to my imports</source> 8568 <source>Go to my imports</source>
8522 <target state="translated">ไปที่การนำเข้าของฉัน</target> 8569 <target state="translated">ไปที่การนำเข้าของฉัน</target>
8523 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8570
8524 </trans-unit> 8571 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8525 <trans-unit id="7511292153332773503" datatype="html"> 8572 <trans-unit id="7511292153332773503" datatype="html">
8526 <source>Go to my channels</source> 8573 <source>Go to my channels</source>
8527 <target state="translated">ไปที่ช่องของฉัน</target> 8574 <target state="translated">ไปที่ช่องของฉัน</target>
8528 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8575
8529 </trans-unit> 8576 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8530 <trans-unit id="2013324644839511073" datatype="html"> 8577 <trans-unit id="2013324644839511073" datatype="html">
8531 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8578 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8532Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8579Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8533 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8580 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8534Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8581Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8535 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8582
8536 </trans-unit> 8583 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8537 <trans-unit id="375263728166936544" datatype="html"> 8584 <trans-unit id="375263728166936544" datatype="html">
8538 <source>You need to reconnect.</source> 8585 <source>You need to reconnect.</source>
8539 <target state="translated">คุณต้องเชื่อมต่อใหม่</target> 8586 <target state="translated">คุณต้องเชื่อมต่อใหม่</target>
8540 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8587
8541 </trans-unit> 8588 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8542 <trans-unit id="2206638022166154361" datatype="html"> 8589 <trans-unit id="2206638022166154361" datatype="html">
8543 <source>Keyboard Shortcuts:</source> 8590 <source>Keyboard Shortcuts:</source>
8544 <target state="translated">ปุ่มลัดคีย์บอร์ด:</target> 8591 <target state="translated">ปุ่มลัดคีย์บอร์ด:</target>
@@ -8551,6 +8598,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8551 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8598 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8552 <context context-type="linenumber">98</context> 8599 <context context-type="linenumber">98</context>
8553 </context-group> 8600 </context-group>
8601 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8602 <source>In my library</source><target state="new">In my library</target>
8603 <context-group purpose="location">
8604 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8605 <context context-type="linenumber">104</context>
8606 </context-group>
8554 </trans-unit> 8607 </trans-unit>
8555 <trans-unit id="232050922346936574" datatype="html"> 8608 <trans-unit id="232050922346936574" datatype="html">
8556 <source>Trending</source> 8609 <source>Trending</source>
@@ -8579,38 +8632,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8579 <trans-unit id="1266887509445371246" datatype="html"> 8632 <trans-unit id="1266887509445371246" datatype="html">
8580 <source>Incorrect username or password.</source> 8633 <source>Incorrect username or password.</source>
8581 <target state="translated">ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง</target> 8634 <target state="translated">ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง</target>
8582 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8635
8583 </trans-unit> 8636 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8584 <trans-unit id="6974874606619467663" datatype="html"> 8637 <trans-unit id="6974874606619467663" datatype="html">
8585 <source>Your account is blocked.</source> 8638 <source>Your account is blocked.</source>
8586 <target state="translated">บัญชีของคุณถูกบล็อก</target> 8639 <target state="translated">บัญชีของคุณถูกบล็อก</target>
8587 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8640
8588 </trans-unit> 8641 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8589 <trans-unit id="7939914198003891823" datatype="html"> 8642 <trans-unit id="7939914198003891823" datatype="html">
8590 <source>any language</source> 8643 <source>any language</source>
8591 <target state="translated">ภาษาใดก็ได้</target> 8644 <target state="translated">ภาษาใดก็ได้</target>
8592 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8645
8593 </trans-unit> 8646 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8594 <trans-unit id="5633144232269377096" datatype="html"> 8647 <trans-unit id="5633144232269377096" datatype="html">
8595 <source>hide</source> 8648 <source>hide</source>
8596 <target state="translated">ซ่อน</target> 8649 <target state="translated">ซ่อน</target>
8597 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8650
8598 </trans-unit> 8651 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8599 <trans-unit id="8603861867909474404" datatype="html"> 8652 <trans-unit id="8603861867909474404" datatype="html">
8600 <source>blur</source> 8653 <source>blur</source>
8601 <target state="translated">เบลอ</target> 8654 <target state="translated">เบลอ</target>
8602 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8655
8603 </trans-unit> 8656 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8604 <trans-unit id="4534458451100881847" datatype="html"> 8657 <trans-unit id="4534458451100881847" datatype="html">
8605 <source>display</source> 8658 <source>display</source>
8606 <target state="translated">แสดง</target> 8659 <target state="translated">แสดง</target>
8607 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8660
8608 </trans-unit> 8661 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8609 <trans-unit id="4467323362722952678" datatype="html"> 8662 <trans-unit id="4467323362722952678" datatype="html">
8610 <source>Unknown</source> 8663 <source>Unknown</source>
8611 <target state="translated">ไม่รู้จัก</target> 8664 <target state="translated">ไม่รู้จัก</target>
8612 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8665
8613 </trans-unit> 8666 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8614 <trans-unit id="8781423666414310853" datatype="html"> 8667 <trans-unit id="8781423666414310853" datatype="html">
8615 <source>Your password has been successfully reset!</source> 8668 <source>Your password has been successfully reset!</source>
8616 <target state="translated">รีเซ็ตรหัสผ่านเรียบร้อย</target> 8669 <target state="translated">รีเซ็ตรหัสผ่านเรียบร้อย</target>
@@ -10217,18 +10270,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10217 <trans-unit id="968295009933361070" datatype="html"> 10270 <trans-unit id="968295009933361070" datatype="html">
10218 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 10271 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
10219 <target state="translated">พยายามหลายครั้งติดต่อกัน โปรดลองอีกครั้งในอีก <x id="PH"/> นาที</target> 10272 <target state="translated">พยายามหลายครั้งติดต่อกัน โปรดลองอีกครั้งในอีก <x id="PH"/> นาที</target>
10220 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 10273
10221 </trans-unit> 10274 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
10222 <trans-unit id="4965472196059235310" datatype="html"> 10275 <trans-unit id="4965472196059235310" datatype="html">
10223 <source>Too many attempts, please try again later.</source> 10276 <source>Too many attempts, please try again later.</source>
10224 <target state="translated">พยายามหลายครั้งติดต่อกัน โปรดลองอีกครั้งในภายหลัง</target> 10277 <target state="translated">พยายามหลายครั้งติดต่อกัน โปรดลองอีกครั้งในภายหลัง</target>
10225 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 10278
10226 </trans-unit> 10279 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
10227 <trans-unit id="1693549688987384699" datatype="html"> 10280 <trans-unit id="1693549688987384699" datatype="html">
10228 <source>Server error. Please retry later.</source> 10281 <source>Server error. Please retry later.</source>
10229 <target state="translated">เซิร์ฟเวอร์เกิดข้อผิดพลาด โปรดลองอีกครั้งในภายหลัง</target> 10282 <target state="translated">เซิร์ฟเวอร์เกิดข้อผิดพลาด โปรดลองอีกครั้งในภายหลัง</target>
10230 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 10283
10231 </trans-unit> 10284 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
10232 <trans-unit id="5927402622550505067" datatype="html"> 10285 <trans-unit id="5927402622550505067" datatype="html">
10233 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 10286 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
10234 <target state="translated">ติดตามทุกช่องในปัจจุบันของ <x id="PH"/> แล้ว คุณจะได้รับการแจ้งเตือนสำหรับวิดีโอใหม่ทุกวิดีโอ</target> 10287 <target state="translated">ติดตามทุกช่องในปัจจุบันของ <x id="PH"/> แล้ว คุณจะได้รับการแจ้งเตือนสำหรับวิดีโอใหม่ทุกวิดีโอ</target>
@@ -10826,35 +10879,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10826 <trans-unit id="3284171506518522275" datatype="html"> 10879 <trans-unit id="3284171506518522275" datatype="html">
10827 <source>Your video was uploaded to your account and is private.</source> 10880 <source>Your video was uploaded to your account and is private.</source>
10828 <target state="translated">วิดีโอของคุณถูกอัปโหลดไปยังบัญชีของคุณและเป็นส่วนตัวแล้ว</target> 10881 <target state="translated">วิดีโอของคุณถูกอัปโหลดไปยังบัญชีของคุณและเป็นส่วนตัวแล้ว</target>
10829 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10882
10830 </trans-unit> 10883 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10831 <trans-unit id="5699822024600815733" datatype="html"> 10884 <trans-unit id="5699822024600815733" datatype="html">
10832 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10885 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10833 <target state="translated">แต่ข้อมูลที่เกี่ยวข้อง (เช่น แท็ก คำอธิบาย) จะไม่ถูกบันทึก คุณแน่ใจว่าต้องการออกจากหน้านี้หรือไม่</target> 10886 <target state="translated">แต่ข้อมูลที่เกี่ยวข้อง (เช่น แท็ก คำอธิบาย) จะไม่ถูกบันทึก คุณแน่ใจว่าต้องการออกจากหน้านี้หรือไม่</target>
10834 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10887
10835 </trans-unit> 10888 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10836 <trans-unit id="1219739004043110649" datatype="html"> 10889 <trans-unit id="1219739004043110649" datatype="html">
10837 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10890 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10838 <target state="translated">วิดีโอยังไม่ถูกอัปโหลด คุณแน่ใจว่าต้องการออกจากหน้านี้หรือไม่</target> 10891 <target state="translated">วิดีโอยังไม่ถูกอัปโหลด คุณแน่ใจว่าต้องการออกจากหน้านี้หรือไม่</target>
10839 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10892
10840 </trans-unit> 10893 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10841 <trans-unit id="6932865105766151309" datatype="html"> 10894 <trans-unit id="6932865105766151309" datatype="html">
10842 <source>Upload</source> 10895 <source>Upload</source>
10843 <target state="translated">อัปโหลด</target> 10896 <target state="translated">อัปโหลด</target>
10844 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10897
10845 </trans-unit> 10898 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10846 <trans-unit id="8278735427925094503" datatype="html"> 10899 <trans-unit id="8278735427925094503" datatype="html">
10847 <source>Upload <x id="PH"/> </source> 10900 <source>Upload <x id="PH"/> </source>
10848 <target state="translated">อัปโหลด 10901 <target state="translated">อัปโหลด
10849 <x id="PH"/> 10902 <x id="PH"/>
10850 </target> 10903 </target>
10851 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10904
10852 </trans-unit> 10905 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10853 <trans-unit id="5981816353437801748" datatype="html"> 10906 <trans-unit id="5981816353437801748" datatype="html">
10854 <source>Video published.</source> 10907 <source>Video published.</source>
10855 <target state="translated">เผยแพร่วิดีโอแล้ว</target> 10908 <target state="translated">เผยแพร่วิดีโอแล้ว</target>
10856 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10909
10857 </trans-unit> 10910 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10858 <trans-unit id="764164089183618119" datatype="html"> 10911 <trans-unit id="764164089183618119" datatype="html">
10859 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10912 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10860 <target state="translated">คุณมีการเปลี่ยนแปลงที่ยังไม่ได้บันทึก ถ้าคุณออกจากหน้านี้ ข้อมูลที่ไม่ได้บันทึกจะหายไป</target> 10913 <target state="translated">คุณมีการเปลี่ยนแปลงที่ยังไม่ได้บันทึก ถ้าคุณออกจากหน้านี้ ข้อมูลที่ไม่ได้บันทึกจะหายไป</target>
@@ -10901,28 +10954,28 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10901 <trans-unit id="961774488937452220" datatype="html"> 10954 <trans-unit id="961774488937452220" datatype="html">
10902 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10955 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10903 <target state="translated">วิดีโอนี้ไม่สามารถรับชมบนเซิร์ฟเวอร์นี้ คุณต้องการเปลี่ยนเส้นทางไปยังเซิร์ฟเวอร์ต้นทางหรือไม่?: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a></target> 10956 <target state="translated">วิดีโอนี้ไม่สามารถรับชมบนเซิร์ฟเวอร์นี้ คุณต้องการเปลี่ยนเส้นทางไปยังเซิร์ฟเวอร์ต้นทางหรือไม่?: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a></target>
10904 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group> 10957
10905 </trans-unit> 10958 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10906 <trans-unit id="5761611056224181752" datatype="html"> 10959 <trans-unit id="5761611056224181752" datatype="html">
10907 <source>Redirection</source> 10960 <source>Redirection</source>
10908 <target state="translated">การเปลี่ยนเส้นทาง</target> 10961 <target state="translated">การเปลี่ยนเส้นทาง</target>
10909 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group> 10962
10910 </trans-unit> 10963 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10911 <trans-unit id="8858527736400081688" datatype="html"> 10964 <trans-unit id="8858527736400081688" datatype="html">
10912 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10965 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10913 <target state="translated">วิดีโอนี้มีเนื้อหาไม่เหมาะสม คุณต้องการรับชมหรือไม่</target> 10966 <target state="translated">วิดีโอนี้มีเนื้อหาไม่เหมาะสม คุณต้องการรับชมหรือไม่</target>
10914 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group> 10967
10915 </trans-unit> 10968 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10916 <trans-unit id="3937119019020041049" datatype="html"> 10969 <trans-unit id="3937119019020041049" datatype="html">
10917 <source>Mature or explicit content</source> 10970 <source>Mature or explicit content</source>
10918 <target state="translated">เนื้อหาไม่เหมาะสม</target> 10971 <target state="translated">เนื้อหาไม่เหมาะสม</target>
10919 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group> 10972
10920 </trans-unit> 10973 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10921 <trans-unit id="1755474755114288376" datatype="html"> 10974 <trans-unit id="1755474755114288376" datatype="html">
10922 <source>Up Next</source> 10975 <source>Up Next</source>
10923 <target state="translated">รายการถัดไป</target> 10976 <target state="translated">รายการถัดไป</target>
10924 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group> 10977
10925 </trans-unit> 10978 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10926 <trans-unit id="2159130950882492111" datatype="html"> 10979 <trans-unit id="2159130950882492111" datatype="html">
10927 <source>Cancel</source> 10980 <source>Cancel</source>
10928 <target state="translated">ยกเลิก</target> 10981 <target state="translated">ยกเลิก</target>
@@ -10931,63 +10984,63 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10931 <trans-unit id="3354816756665089864" datatype="html"> 10984 <trans-unit id="3354816756665089864" datatype="html">
10932 <source>Autoplay is suspended</source> 10985 <source>Autoplay is suspended</source>
10933 <target state="translated">การเล่นวิดีโออัตโนมัติถูกหยุด</target> 10986 <target state="translated">การเล่นวิดีโออัตโนมัติถูกหยุด</target>
10934 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group> 10987
10935 </trans-unit> 10988 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10936 <trans-unit id="7895294730547405228" datatype="html"> 10989 <trans-unit id="7895294730547405228" datatype="html">
10937 <source>Enter/exit fullscreen (requires player focus)</source> 10990 <source>Enter/exit fullscreen (requires player focus)</source>
10938 <target state="new">Enter/exit fullscreen (requires player focus)</target> 10991 <target state="new">Enter/exit fullscreen (requires player focus)</target>
10939 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group> 10992
10940 </trans-unit> 10993 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10941 <trans-unit id="7618388257165864759" datatype="html"> 10994 <trans-unit id="7618388257165864759" datatype="html">
10942 <source>Play/Pause the video (requires player focus)</source> 10995 <source>Play/Pause the video (requires player focus)</source>
10943 <target state="new">Play/Pause the video (requires player focus)</target> 10996 <target state="new">Play/Pause the video (requires player focus)</target>
10944 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group> 10997
10945 </trans-unit> 10998 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10946 <trans-unit id="7761890399634216630" datatype="html"> 10999 <trans-unit id="7761890399634216630" datatype="html">
10947 <source>Mute/unmute the video (requires player focus)</source> 11000 <source>Mute/unmute the video (requires player focus)</source>
10948 <target state="new">Mute/unmute the video (requires player focus)</target> 11001 <target state="new">Mute/unmute the video (requires player focus)</target>
10949 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group> 11002
10950 </trans-unit> 11003 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10951 <trans-unit id="5996585232248234904" datatype="html"> 11004 <trans-unit id="5996585232248234904" datatype="html">
10952 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 11005 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10953 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 11006 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10954 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group> 11007
10955 </trans-unit> 11008 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10956 <trans-unit id="3748765405903319998" datatype="html"> 11009 <trans-unit id="3748765405903319998" datatype="html">
10957 <source>Increase the volume (requires player focus)</source> 11010 <source>Increase the volume (requires player focus)</source>
10958 <target state="new">Increase the volume (requires player focus)</target> 11011 <target state="new">Increase the volume (requires player focus)</target>
10959 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group> 11012
10960 </trans-unit> 11013 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10961 <trans-unit id="5810704036407159982" datatype="html"> 11014 <trans-unit id="5810704036407159982" datatype="html">
10962 <source>Decrease the volume (requires player focus)</source> 11015 <source>Decrease the volume (requires player focus)</source>
10963 <target state="new">Decrease the volume (requires player focus)</target> 11016 <target state="new">Decrease the volume (requires player focus)</target>
10964 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group> 11017
10965 </trans-unit> 11018 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10966 <trans-unit id="2622048822548065691" datatype="html"> 11019 <trans-unit id="2622048822548065691" datatype="html">
10967 <source>Seek the video forward (requires player focus)</source> 11020 <source>Seek the video forward (requires player focus)</source>
10968 <target state="new">Seek the video forward (requires player focus)</target> 11021 <target state="new">Seek the video forward (requires player focus)</target>
10969 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group> 11022
10970 </trans-unit> 11023 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10971 <trans-unit id="6540078205109221153" datatype="html"> 11024 <trans-unit id="6540078205109221153" datatype="html">
10972 <source>Seek the video backward (requires player focus)</source> 11025 <source>Seek the video backward (requires player focus)</source>
10973 <target state="new">Seek the video backward (requires player focus)</target> 11026 <target state="new">Seek the video backward (requires player focus)</target>
10974 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group> 11027
10975 </trans-unit> 11028 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10976 <trans-unit id="1956491957766210808" datatype="html"> 11029 <trans-unit id="1956491957766210808" datatype="html">
10977 <source>Increase playback rate (requires player focus)</source> 11030 <source>Increase playback rate (requires player focus)</source>
10978 <target state="new">Increase playback rate (requires player focus)</target> 11031 <target state="new">Increase playback rate (requires player focus)</target>
10979 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group> 11032
10980 </trans-unit> 11033 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10981 <trans-unit id="5495529997674803186" datatype="html"> 11034 <trans-unit id="5495529997674803186" datatype="html">
10982 <source>Decrease playback rate (requires player focus)</source> 11035 <source>Decrease playback rate (requires player focus)</source>
10983 <target state="new">Decrease playback rate (requires player focus)</target> 11036 <target state="new">Decrease playback rate (requires player focus)</target>
10984 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group> 11037
10985 </trans-unit> 11038 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10986 <trans-unit id="3178343147230721210" datatype="html"> 11039 <trans-unit id="3178343147230721210" datatype="html">
10987 <source>Navigate in the video frame by frame (requires player focus)</source> 11040 <source>Navigate in the video frame by frame (requires player focus)</source>
10988 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 11041 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10989 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group> 11042
10990 </trans-unit> 11043 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10991 <trans-unit id="8025996572234182184" datatype="html"> 11044 <trans-unit id="8025996572234182184" datatype="html">
10992 <source>Like the video</source> 11045 <source>Like the video</source>
10993 <target state="translated">ชอบวิดีโอ</target> 11046 <target state="translated">ชอบวิดีโอ</target>
diff --git a/client/src/locale/angular.tr-TR.xlf b/client/src/locale/angular.tr-TR.xlf
index c42282c5d..b43dbb444 100644
--- a/client/src/locale/angular.tr-TR.xlf
+++ b/client/src/locale/angular.tr-TR.xlf
@@ -243,7 +243,7 @@
243 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source> 243 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source>
244 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target> 244 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target>
245 245
246 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 246 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
247 <trans-unit id="1486537403020619891" datatype="html"> 247 <trans-unit id="1486537403020619891" datatype="html">
248 <source>My watch history</source> 248 <source>My watch history</source>
249 <target state="translated">İzleme geçmişim</target> 249 <target state="translated">İzleme geçmişim</target>
@@ -325,12 +325,12 @@
325 325
326 326
327 327
328 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 328 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
329 <trans-unit id="1006562256968398209" datatype="html"> 329 <trans-unit id="1006562256968398209" datatype="html">
330 <source>video</source> 330 <source>video</source>
331 <target state="translated">video</target> 331 <target state="translated">video</target>
332 332
333 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 333 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
334 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 334 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
335 335
336 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 336 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -392,10 +392,10 @@
392 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 392 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
393 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 393 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
394 394
395 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 395 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
396 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 396 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
397 397
398 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 398 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
399 399
400 <trans-unit id="5235042777215655908" datatype="html"> 400 <trans-unit id="5235042777215655908" datatype="html">
401 <source>subtitles</source> 401 <source>subtitles</source>
@@ -785,10 +785,10 @@
785 <trans-unit id="2602586221576511475"> 785 <trans-unit id="2602586221576511475">
786 <source>Video quota</source> 786 <source>Video quota</source>
787 <target>Video sınırı</target> 787 <target>Video sınırı</target>
788 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 788
789 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 789
790 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 790
791 </trans-unit> 791 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
792 <trans-unit id="1502595455339510144" datatype="html"> 792 <trans-unit id="1502595455339510144" datatype="html">
793 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 793 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
794 <target state="new">Unlimited 794 <target state="new">Unlimited
@@ -871,7 +871,31 @@
871 <source>Federation</source> 871 <source>Federation</source>
872 <target state="new">Federation</target> 872 <target state="new">Federation</target>
873 873
874 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 874 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
875 <source>Following</source><target state="new">Following</target>
876 <context-group purpose="location">
877 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
878 <context context-type="linenumber">29</context>
879 </context-group>
880 <context-group purpose="location">
881 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
882 <context context-type="linenumber">31</context>
883 </context-group>
884 <context-group purpose="location">
885 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
886 <context context-type="linenumber">28</context>
887 </context-group>
888 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
889 <source>Followers</source><target state="new">Followers</target>
890 <context-group purpose="location">
891 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
892 <context context-type="linenumber">34</context>
893 </context-group>
894 <context-group purpose="location">
895 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
896 <context context-type="linenumber">37</context>
897 </context-group>
898 </trans-unit>
875 <trans-unit id="3541687134897970106" datatype="html"> 899 <trans-unit id="3541687134897970106" datatype="html">
876 <source>followers</source> 900 <source>followers</source>
877 <target state="new">followers</target> 901 <target state="new">followers</target>
@@ -944,7 +968,7 @@
944 968
945 969
946 970
947 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 971 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
948 <trans-unit id="3616223838716839702" datatype="html"> 972 <trans-unit id="3616223838716839702" datatype="html">
949 <source>Ban this user</source> 973 <source>Ban this user</source>
950 <target state="translated">Bu kullanıcıyı yasakla</target> 974 <target state="translated">Bu kullanıcıyı yasakla</target>
@@ -1015,19 +1039,13 @@
1015 <trans-unit id="7252854992688790751" datatype="html"> 1039 <trans-unit id="7252854992688790751" datatype="html">
1016 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1040 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1017 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1041 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1018 <context-group purpose="location"> 1042
1019 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1043 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1020 <context context-type="linenumber">60,62</context>
1021 </context-group>
1022 </trans-unit>
1023 <trans-unit id="7215649348148521605" datatype="html"> 1044 <trans-unit id="7215649348148521605" datatype="html">
1024 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1045 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1025 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1046 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1026 <context-group purpose="location"> 1047
1027 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1048 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1028 <context context-type="linenumber">65,67</context>
1029 </context-group>
1030 </trans-unit>
1031 <trans-unit id="2392488717875840729"> 1049 <trans-unit id="2392488717875840729">
1032 <source>User</source> 1050 <source>User</source>
1033 <target>Kullanıcı</target> 1051 <target>Kullanıcı</target>
@@ -1038,66 +1056,66 @@
1038 <source>Username or email address</source> 1056 <source>Username or email address</source>
1039 <target>Kullanıcı adı ya da e-posta adresi</target> 1057 <target>Kullanıcı adı ya da e-posta adresi</target>
1040 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1058 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1059 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1060 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1061 <context-group purpose="location">
1062 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1063 <context context-type="linenumber">33,34</context>
1064 </context-group>
1041 </trans-unit> 1065 </trans-unit>
1042 <trans-unit id="1431416938026210429"> 1066 <trans-unit id="1431416938026210429">
1043 <source>Password</source> 1067 <source>Password</source>
1044 <target>Şifre</target> 1068 <target>Şifre</target>
1045 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1069
1046 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1070
1047 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1071
1048 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1072
1049 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1073
1050 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1074
1051 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1075
1052 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1076
1053 </trans-unit> 1077 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1054 <trans-unit id="8715156686857791956" datatype="html"> 1078 <trans-unit id="8715156686857791956" datatype="html">
1055 <source>Click here to reset your password</source> 1079 <source>Click here to reset your password</source>
1056 <target state="translated">Şifrenizi sıfırlamak için buraya tıklayın</target> 1080 <target state="translated">Şifrenizi sıfırlamak için buraya tıklayın</target>
1057 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1081
1058 </trans-unit> 1082 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1059 <trans-unit id="892063502898494584" datatype="html"> 1083 <trans-unit id="892063502898494584" datatype="html">
1060 <source>I forgot my password</source> 1084 <source>I forgot my password</source>
1061 <target state="translated">Şifremi unuttum</target> 1085 <target state="translated">Şifremi unuttum</target>
1062 <context-group purpose="location"> 1086
1063 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1087 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1064 <context context-type="linenumber">47</context>
1065 </context-group>
1066 </trans-unit>
1067 <trans-unit id="2101170466365500913" datatype="html"> 1088 <trans-unit id="2101170466365500913" datatype="html">
1068 <source>Logging into an account lets you publish content</source> 1089 <source>Logging into an account lets you publish content</source>
1069 <target state="translated">Hesabınıza giriş yapmak içerik yayınlamanızı sağlar</target> 1090 <target state="translated">Hesabınıza giriş yapmak içerik yayınlamanızı sağlar</target>
1070 <context-group purpose="location"> 1091
1071 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1092 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1072 <context context-type="linenumber">56,57</context>
1073 </context-group>
1074 </trans-unit>
1075 <trans-unit id="2454050363478003966"> 1093 <trans-unit id="2454050363478003966">
1076 <source>Login</source> 1094 <source>Login</source>
1077 <target>Oturum aç</target> 1095 <target>Oturum aç</target>
1078 1096
1079 1097
1080 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 1098 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1081 <trans-unit id="3183213940445113677" datatype="html"> 1099 <trans-unit id="3183213940445113677" datatype="html">
1082 <source>Or sign in with</source> 1100 <source>Or sign in with</source>
1083 <target state="translated">Ya da şununla oturum aç</target> 1101 <target state="translated">Ya da şununla oturum aç</target>
1084 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1102
1085 </trans-unit> 1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1086 <trans-unit id="3238209155172574367" datatype="html"> 1104 <trans-unit id="3238209155172574367" datatype="html">
1087 <source>Forgot your password</source> 1105 <source>Forgot your password</source>
1088 <target state="translated">Şifrenizi mi unuttunuz?</target> 1106 <target state="translated">Şifrenizi mi unuttunuz?</target>
1089 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1107
1090 </trans-unit> 1108 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1091 <trans-unit id="87327320394367488" datatype="html"> 1109 <trans-unit id="87327320394367488" datatype="html">
1092 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1110 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1093 <target state="new">We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</target> 1111 <target state="new">We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</target>
1094 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1112
1095 </trans-unit> 1113 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1096 <trans-unit id="3188014010833256853" datatype="html"> 1114 <trans-unit id="3188014010833256853" datatype="html">
1097 <source>Enter your email address and we will send you a link to reset your password.</source> 1115 <source>Enter your email address and we will send you a link to reset your password.</source>
1098 <target state="translated">E-posta adresinizi girin, şifrenizi sıfırlamak için bir bağlantı göndereceğiz.</target> 1116 <target state="translated">E-posta adresinizi girin, şifrenizi sıfırlamak için bir bağlantı göndereceğiz.</target>
1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1117
1100 </trans-unit> 1118 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1101 <trans-unit id="1190256911880544559" datatype="html"> 1119 <trans-unit id="1190256911880544559" datatype="html">
1102 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1120 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1103The link will expire within 1 hour.</source> 1121The link will expire within 1 hour.</source>
@@ -1108,26 +1126,26 @@ The link will expire within 1 hour.</target>
1108 <trans-unit id="4768749765465246664" datatype="html"> 1126 <trans-unit id="4768749765465246664" datatype="html">
1109 <source>Email</source> 1127 <source>Email</source>
1110 <target state="translated">E-posta</target> 1128 <target state="translated">E-posta</target>
1111 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1129
1112 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1130
1113 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1131
1114 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1132
1115 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1133
1116 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1134
1117 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1135
1118 </trans-unit> 1136 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1119 <trans-unit id="3967269098753656610" datatype="html"> 1137 <trans-unit id="3967269098753656610" datatype="html">
1120 <source>Email address</source> 1138 <source>Email address</source>
1121 <target state="translated">E-posta adresi</target> 1139 <target state="translated">E-posta adresi</target>
1122 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1140
1123 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1141
1124 </trans-unit> 1142 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1125 <trans-unit id="7808756054397155068" datatype="html"> 1143 <trans-unit id="7808756054397155068" datatype="html">
1126 <source>Reset</source> 1144 <source>Reset</source>
1127 <target state="translated">Sıfırla</target> 1145 <target state="translated">Sıfırla</target>
1128 <note priority="1" from="description">Password reset button</note> 1146 <note priority="1" from="description">Password reset button</note>
1129 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1147
1130 </trans-unit> 1148 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1131 1149
1132 <trans-unit id="4319634264526091601" datatype="html"> 1150 <trans-unit id="4319634264526091601" datatype="html">
1133 <source>on this instance</source> 1151 <source>on this instance</source>
@@ -1466,7 +1484,7 @@ The link will expire within 1 hour.</target>
1466 <target state="translated">Hesap oluştur</target> 1484 <target state="translated">Hesap oluştur</target>
1467 1485
1468 1486
1469 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1487 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1470 1488
1471 <trans-unit id="3058024914967508975" datatype="html"> 1489 <trans-unit id="3058024914967508975" datatype="html">
1472 <source>My videos</source> 1490 <source>My videos</source>
@@ -1523,7 +1541,7 @@ The link will expire within 1 hour.</target>
1523 <source>VIDEOS</source> 1541 <source>VIDEOS</source>
1524 <target state="translated">VİDEOLAR</target> 1542 <target state="translated">VİDEOLAR</target>
1525 1543
1526 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html"> 1544 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit><trans-unit id="667372110624203230" datatype="html">
1527 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target> 1545 <source>Import jobs concurrency</source><target state="new">Import jobs concurrency</target>
1528 1546
1529 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html"> 1547 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">225</context></context-group></trans-unit><trans-unit id="2184839376696112704" datatype="html">
@@ -1597,7 +1615,7 @@ The link will expire within 1 hour.</target>
1597 <source>I'm a teapot</source> 1615 <source>I'm a teapot</source>
1598 <target state="new">I'm a teapot</target> 1616 <target state="new">I'm a teapot</target>
1599 1617
1600 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit> 1618 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1601 <trans-unit id="1597262876035959248" datatype="html"> 1619 <trans-unit id="1597262876035959248" datatype="html">
1602 <source>That's an error.</source> 1620 <source>That's an error.</source>
1603 <target state="translated">Bu bir hata.</target> 1621 <target state="translated">Bu bir hata.</target>
@@ -1681,8 +1699,8 @@ The link will expire within 1 hour.</target>
1681 <trans-unit id="2971365540217107489" datatype="html"> 1699 <trans-unit id="2971365540217107489" datatype="html">
1682 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1700 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1683 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1701 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1684 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1702
1685 </trans-unit> 1703 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1686 1704
1687 <trans-unit id="5131854469652959713" datatype="html"> 1705 <trans-unit id="5131854469652959713" datatype="html">
1688 <source>GLOBAL SEARCH</source> 1706 <source>GLOBAL SEARCH</source>
@@ -2376,7 +2394,7 @@ The link will expire within 1 hour.</target>
2376 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2394 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2377 <source>Upload on hold</source><target state="new">Upload on hold</target> 2395 <source>Upload on hold</source><target state="new">Upload on hold</target>
2378 2396
2379 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2397 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2380 <trans-unit id="285180972645018518" datatype="html"> 2398 <trans-unit id="285180972645018518" datatype="html">
2381 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2399 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2382 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2400 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3089,11 +3107,7 @@ The link will expire within 1 hour.</target>
3089 <target state="new">ID</target> 3107 <target state="new">ID</target>
3090 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3108 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3091 </trans-unit> 3109 </trans-unit>
3092 <trans-unit id="2265605798180116441" datatype="html"> 3110
3093 <source>Follower handle</source>
3094 <target state="new">Follower handle</target>
3095
3096 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
3097 <trans-unit id="5911214550882917183" datatype="html"> 3111 <trans-unit id="5911214550882917183" datatype="html">
3098 <source>State</source> 3112 <source>State</source>
3099 <target state="new">State</target> 3113 <target state="new">State</target>
@@ -3168,11 +3182,7 @@ The link will expire within 1 hour.</target>
3168 </target> 3182 </target>
3169 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3183 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3170 </trans-unit> 3184 </trans-unit>
3171 <trans-unit id="6641024648411549335" datatype="html"> 3185
3172 <source>Host</source>
3173 <target state="new">Host</target>
3174
3175 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3176 <trans-unit id="6571718060636962350" datatype="html"> 3186 <trans-unit id="6571718060636962350" datatype="html">
3177 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3187 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3178 <target state="new">Redundancy allowed 3188 <target state="new">Redundancy allowed
@@ -3185,7 +3195,7 @@ The link will expire within 1 hour.</target>
3185 <source>Unfollow</source> 3195 <source>Unfollow</source>
3186 <target state="translated">Takipten çık</target> 3196 <target state="translated">Takipten çık</target>
3187 3197
3188 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3198 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3189 <trans-unit id="8246779176913476983" datatype="html"> 3199 <trans-unit id="8246779176913476983" datatype="html">
3190 <source>Open instance in a new tab</source> 3200 <source>Open instance in a new tab</source>
3191 <target state="new">Open instance in a new tab</target> 3201 <target state="new">Open instance in a new tab</target>
@@ -3197,12 +3207,12 @@ The link will expire within 1 hour.</target>
3197 <source>No host found matching current filters.</source> 3207 <source>No host found matching current filters.</source>
3198 <target state="new">No host found matching current filters.</target> 3208 <target state="new">No host found matching current filters.</target>
3199 3209
3200 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3210 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3201 <trans-unit id="7274241885665071790" datatype="html"> 3211 <trans-unit id="7274241885665071790" datatype="html">
3202 <source>Your instance is not following anyone.</source> 3212 <source>Your instance is not following anyone.</source>
3203 <target state="new">Your instance is not following anyone.</target> 3213 <target state="new">Your instance is not following anyone.</target>
3204 3214
3205 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3215 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3206 <trans-unit id="4774348799569692380" datatype="html"> 3216 <trans-unit id="4774348799569692380" datatype="html">
3207 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3217 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3208 <target state="new">Showing 3218 <target state="new">Showing
@@ -3212,16 +3222,8 @@ The link will expire within 1 hour.</target>
3212 </target> 3222 </target>
3213 3223
3214 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3224 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3215 <trans-unit id="6275803119759621687" datatype="html"> 3225
3216 <source>Follow domains</source> 3226 <trans-unit id="9216117865911519658" datatype="html">
3217 <target state="new">Follow domains</target>
3218
3219 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit>
3220 <trans-unit id="1268699198448750610" datatype="html">
3221 <source>Follow instances</source>
3222 <target state="new">Follow instances</target>
3223
3224 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3225 <source>Action</source><target state="new">Action</target> 3227 <source>Action</source><target state="new">Action</target>
3226 3228
3227 3229
@@ -3268,11 +3270,11 @@ The link will expire within 1 hour.</target>
3268 <trans-unit id="5248717555542428023"> 3270 <trans-unit id="5248717555542428023">
3269 <source>Username</source> 3271 <source>Username</source>
3270 <target>Kullanıcı adı</target> 3272 <target>Kullanıcı adı</target>
3271 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3273
3272 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3274
3273 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3275
3274 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3276
3275 </trans-unit> 3277 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3276 <trans-unit id="5428411040014095392" datatype="html"> 3278 <trans-unit id="5428411040014095392" datatype="html">
3277 <source>e.g. jane_doe</source> 3279 <source>e.g. jane_doe</source>
3278 <target state="new">e.g. jane_doe</target> 3280 <target state="new">e.g. jane_doe</target>
@@ -3302,9 +3304,9 @@ The link will expire within 1 hour.</target>
3302 <trans-unit id="4145496584631696119" datatype="html"> 3304 <trans-unit id="4145496584631696119" datatype="html">
3303 <source>Role</source> 3305 <source>Role</source>
3304 <target state="new">Role</target> 3306 <target state="new">Role</target>
3305 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3307
3306 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3308
3307 </trans-unit> 3309 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3308 <trans-unit id="7046347992315328430" datatype="html"> 3310 <trans-unit id="7046347992315328430" datatype="html">
3309 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3311 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3310 <target state="new"> 3312 <target state="new">
@@ -3327,15 +3329,9 @@ The link will expire within 1 hour.</target>
3327 3329
3328 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html"> 3330 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">172</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/users/user-quota.component.html</context><context context-type="linenumber">13</context></context-group></trans-unit><trans-unit id="2622255144026150901" datatype="html">
3329 <source>Auth plugin</source><target state="new">Auth plugin</target> 3331 <source>Auth plugin</source><target state="new">Auth plugin</target>
3330 <context-group purpose="location"> 3332
3331 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3333
3332 <context context-type="linenumber">188</context> 3334 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit><trans-unit id="588099657508661970" datatype="html">
3333 </context-group>
3334 <context-group purpose="location">
3335 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3336 <context context-type="linenumber">188</context>
3337 </context-group>
3338 </trans-unit><trans-unit id="588099657508661970" datatype="html">
3339 <source>None (local authentication)</source><target state="new">None (local authentication)</target> 3335 <source>None (local authentication)</source><target state="new">None (local authentication)</target>
3340 <context-group purpose="location"> 3336 <context-group purpose="location">
3341 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3337 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
@@ -3592,7 +3588,13 @@ The link will expire within 1 hour.</target>
3592 3588
3593 3589
3594 3590
3595 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit> 3591 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3592 <source>Follower</source><target state="new">Follower</target>
3593 <context-group purpose="location">
3594 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3595 <context context-type="linenumber">24</context>
3596 </context-group>
3597 </trans-unit>
3596 <trans-unit id="4691552465058437520" datatype="html"> 3598 <trans-unit id="4691552465058437520" datatype="html">
3597 <source>Commented video</source> 3599 <source>Commented video</source>
3598 <target state="new">Commented video</target> 3600 <target state="new">Commented video</target>
@@ -3917,7 +3919,7 @@ The link will expire within 1 hour.</target>
3917 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 3919 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
3918 </target> 3920 </target>
3919 3921
3920 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3922 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3921 <trans-unit id="4058814854824495833" datatype="html"> 3923 <trans-unit id="4058814854824495833" datatype="html">
3922 <source>Mute domains</source> 3924 <source>Mute domains</source>
3923 <target state="new">Mute domains</target> 3925 <target state="new">Mute domains</target>
@@ -5838,11 +5840,8 @@ color: red;
5838 5840
5839 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5841 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5840 <source>CHANNELS</source><target state="new">CHANNELS</target> 5842 <source>CHANNELS</source><target state="new">CHANNELS</target>
5841 <context-group purpose="location"> 5843
5842 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5844 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5843 <context context-type="linenumber">82</context>
5844 </context-group>
5845 </trans-unit>
5846 5845
5847 <trans-unit id="3666829335406793239" datatype="html"> 5846 <trans-unit id="3666829335406793239" datatype="html">
5848 <source>This account does not have channels.</source> 5847 <source>This account does not have channels.</source>
@@ -5882,7 +5881,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
5882It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 5881It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
5883channel with the same name (<x id="PH_2"/>)!</target> 5882channel with the same name (<x id="PH_2"/>)!</target>
5884 5883
5885 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5884 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5885 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5886 <context-group purpose="location">
5887 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5888 <context context-type="linenumber">48</context>
5889 </context-group>
5890 </trans-unit>
5886 <trans-unit id="5387007581996837469" datatype="html"> 5891 <trans-unit id="5387007581996837469" datatype="html">
5887 <source>My Channels</source> 5892 <source>My Channels</source>
5888 <target state="translated">Kanallarım</target> 5893 <target state="translated">Kanallarım</target>
@@ -6466,12 +6471,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6466 <source>Your message has been sent.</source> 6471 <source>Your message has been sent.</source>
6467 <target state="new">Your message has been sent.</target> 6472 <target state="new">Your message has been sent.</target>
6468 6473
6469 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6474 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6470 <trans-unit id="2072135752262464360" datatype="html"> 6475 <trans-unit id="2072135752262464360" datatype="html">
6471 <source>You already sent this form recently</source> 6476 <source>You already sent this form recently</source>
6472 <target state="new">You already sent this form recently</target> 6477 <target state="new">You already sent this form recently</target>
6473 6478
6474 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6479 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6475 6480
6476 <trans-unit id="819067926858619041" datatype="html"> 6481 <trans-unit id="819067926858619041" datatype="html">
6477 <source>Account videos</source> 6482 <source>Account videos</source>
@@ -6519,12 +6524,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6519 <x id="PH"/> direct account followers 6524 <x id="PH"/> direct account followers
6520 </target> 6525 </target>
6521 6526
6522 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit> 6527 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6523 <trans-unit id="6250999352462648289" datatype="html"> 6528 <trans-unit id="6250999352462648289" datatype="html">
6524 <source>Report this account</source> 6529 <source>Report this account</source>
6525 <target state="translated">Bu hesabı ihbar et</target> 6530 <target state="translated">Bu hesabı ihbar et</target>
6526 6531
6527 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6532 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6528 6533
6529 6534
6530 <trans-unit id="1504521795586863905" datatype="html"> 6535 <trans-unit id="1504521795586863905" datatype="html">
@@ -6539,27 +6544,19 @@ channel with the same name (<x id="PH_2"/>)!</target>
6539 <target state="translated">Kullanıcı adı kopyalandı</target> 6544 <target state="translated">Kullanıcı adı kopyalandı</target>
6540 6545
6541 6546
6542 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit> 6547 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6543 <trans-unit id="9221735175659318025" datatype="html"> 6548 <trans-unit id="9221735175659318025" datatype="html">
6544 <source>1 subscriber</source> 6549 <source>1 subscriber</source>
6545 <target state="translated">1 abone</target> 6550 <target state="translated">1 abone</target>
6546 6551
6547 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit> 6552 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6548 <trans-unit id="4097331874769079975" datatype="html"> 6553 <trans-unit id="4097331874769079975" datatype="html">
6549 <source><x id="PH"/> subscribers</source> 6554 <source><x id="PH"/> subscribers</source>
6550 <target state="translated"><x id="PH"/> abone</target> 6555 <target state="translated"><x id="PH"/> abone</target>
6551 6556
6552 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6557 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6553 <trans-unit id="4682675125751819107" datatype="html"> 6558
6554 <source>Instances you follow</source> 6559
6555 <target state="new">Instances you follow</target>
6556
6557 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6558 <trans-unit id="8899833753704589712" datatype="html">
6559 <source>Instances following you</source>
6560 <target state="new">Instances following you</target>
6561
6562 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6563 <trans-unit id="1035838766454786107" datatype="html"> 6560 <trans-unit id="1035838766454786107" datatype="html">
6564 <source>Audio-only</source> 6561 <source>Audio-only</source>
6565 <target state="translated">Yalnızca ses</target> 6562 <target state="translated">Yalnızca ses</target>
@@ -6609,7 +6606,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6609 <source>Auto (via ffmpeg)</source> 6606 <source>Auto (via ffmpeg)</source>
6610 <target state="translated">Kendiliğinden (ffmpeg ile)</target> 6607 <target state="translated">Kendiliğinden (ffmpeg ile)</target>
6611 6608
6612 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit> 6609 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group></trans-unit><trans-unit id="3642770981085338761" datatype="html">
6610 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6611 <context-group purpose="location">
6612 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6613 <context context-type="linenumber">3</context>
6614 </context-group>
6615 </trans-unit>
6613 <trans-unit id="931255636742351800" datatype="html"> 6616 <trans-unit id="931255636742351800" datatype="html">
6614 <source>No limit</source> 6617 <source>No limit</source>
6615 <target state="translated">Sınır yok</target> 6618 <target state="translated">Sınır yok</target>
@@ -6741,18 +6744,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
6741 <trans-unit id="2127446333083057097" datatype="html"> 6744 <trans-unit id="2127446333083057097" datatype="html">
6742 <source>Domain is required.</source> 6745 <source>Domain is required.</source>
6743 <target state="new">Domain is required.</target> 6746 <target state="new">Domain is required.</target>
6744 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6747
6745 </trans-unit> 6748 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6746 <trans-unit id="6780793142903080663" datatype="html"> 6749 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6747 <source>Domains entered are invalid.</source> 6750 <context-group purpose="location">
6748 <target state="new">Domains entered are invalid.</target> 6751 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6749 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6752 <context context-type="linenumber">93</context>
6750 </trans-unit> 6753 </context-group>
6751 <trans-unit id="5886492514458202177" datatype="html"> 6754 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6752 <source>Domains entered contain duplicates.</source> 6755 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6753 <target state="new">Domains entered contain duplicates.</target> 6756 <context-group purpose="location">
6754 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6757 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6758 <context context-type="linenumber">94</context>
6759 </context-group>
6760 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6761 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6762 <context-group purpose="location">
6763 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6764 <context context-type="linenumber">102</context>
6765 </context-group>
6766 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6767 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6768 <context-group purpose="location">
6769 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6770 <context context-type="linenumber">103</context>
6771 </context-group>
6755 </trans-unit> 6772 </trans-unit>
6773
6774
6756 <trans-unit id="240806681889331244" datatype="html"> 6775 <trans-unit id="240806681889331244" datatype="html">
6757 <source>Unlimited</source> 6776 <source>Unlimited</source>
6758 <target state="translated">Sınırsız</target> 6777 <target state="translated">Sınırsız</target>
@@ -6902,26 +6921,52 @@ channel with the same name (<x id="PH_2"/>)!</target>
6902 <x id="PH"/> removed from instance followers 6921 <x id="PH"/> removed from instance followers
6903 </target> 6922 </target>
6904 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6923 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6924 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6925 <source>Follow</source><target state="new">Follow</target>
6926 <context-group purpose="location">
6927 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6928 <context context-type="linenumber">3</context>
6929 </context-group>
6930 <context-group purpose="location">
6931 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6932 <context context-type="linenumber">37</context>
6933 </context-group>
6934 <context-group purpose="location">
6935 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6936 <context context-type="linenumber">18</context>
6937 </context-group>
6938 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6939 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6940 <context-group purpose="location">
6941 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6942 <context context-type="linenumber">11</context>
6943 </context-group>
6905 </trans-unit> 6944 </trans-unit>
6906 <trans-unit id="2740793005745065895" datatype="html"> 6945 <trans-unit id="2740793005745065895" datatype="html">
6907 <source><x id="PH"/> is not valid </source> 6946 <source><x id="PH"/> is not valid </source>
6908 <target state="new"> 6947 <target state="new">
6909 <x id="PH"/> is not valid 6948 <x id="PH"/> is not valid
6910 </target> 6949 </target>
6911 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6950
6912 </trans-unit> 6951 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6913 <trans-unit id="2355066641781598196" datatype="html"> 6952 <trans-unit id="2355066641781598196" datatype="html">
6914 <source>Follow request(s) sent!</source> 6953 <source>Follow request(s) sent!</source>
6915 <target state="new">Follow request(s) sent!</target> 6954 <target state="new">Follow request(s) sent!</target>
6916 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6955
6956 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6957 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6958 <context-group purpose="location">
6959 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6960 <context context-type="linenumber">3</context>
6961 </context-group>
6917 </trans-unit> 6962 </trans-unit>
6918 <trans-unit id="4245720728052819482" datatype="html"> 6963 <trans-unit id="4245720728052819482" datatype="html">
6919 <source>Do you really want to unfollow <x id="PH"/>?</source> 6964 <source>Do you really want to unfollow <x id="PH"/>?</source>
6920 <target state="new">Do you really want to unfollow 6965 <target state="new">Do you really want to unfollow
6921 <x id="PH"/>? 6966 <x id="PH"/>?
6922 </target> 6967 </target>
6923 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6968
6924 </trans-unit> 6969 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6925 <trans-unit id="9160510009013134726" datatype="html"> 6970 <trans-unit id="9160510009013134726" datatype="html">
6926 <source>Unfollow</source> 6971 <source>Unfollow</source>
6927 <target state="translated">Takipten çık</target> 6972 <target state="translated">Takipten çık</target>
@@ -6932,8 +6977,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
6932 <target state="new">You are not following 6977 <target state="new">You are not following
6933 <x id="PH"/> anymore. 6978 <x id="PH"/> anymore.
6934 </target> 6979 </target>
6935 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6980
6936 </trans-unit> 6981 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6937 <trans-unit id="2593763089859685916" datatype="html"> 6982 <trans-unit id="2593763089859685916" datatype="html">
6938 <source>enabled</source> 6983 <source>enabled</source>
6939 <target state="new">enabled</target> 6984 <target state="new">enabled</target>
@@ -7392,9 +7437,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7392 <trans-unit id="1519954996184640001" datatype="html"> 7437 <trans-unit id="1519954996184640001" datatype="html">
7393 <source>Error</source> 7438 <source>Error</source>
7394 <target state="translated">Hata</target> 7439 <target state="translated">Hata</target>
7395 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7440
7396 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7441
7397 </trans-unit> 7442 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7398 <trans-unit id="5076187961693950167" datatype="html"> 7443 <trans-unit id="5076187961693950167" datatype="html">
7399 <source>Standard logs</source> 7444 <source>Standard logs</source>
7400 <target state="new">Standard logs</target> 7445 <target state="new">Standard logs</target>
@@ -7439,16 +7484,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7439 <target state="new">Update user password</target> 7484 <target state="new">Update user password</target>
7440 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7485 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7441 </trans-unit> 7486 </trans-unit>
7442 <trans-unit id="177544274549739411" datatype="html"> 7487
7443 <source>Following list</source> 7488
7444 <target state="new">Following list</target>
7445 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7446 </trans-unit>
7447 <trans-unit id="8092429110007204784" datatype="html">
7448 <source>Followers list</source>
7449 <target state="new">Followers list</target>
7450 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7451 </trans-unit>
7452 <trans-unit id="780323526182667308" datatype="html"> 7489 <trans-unit id="780323526182667308" datatype="html">
7453 <source>User <x id="PH"/> updated.</source> 7490 <source>User <x id="PH"/> updated.</source>
7454 <target state="new">User 7491 <target state="new">User
@@ -7488,16 +7525,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7488 <target state="new">Federation</target> 7525 <target state="new">Federation</target>
7489 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7526 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7490 </trans-unit> 7527 </trans-unit>
7491 <trans-unit id="4682675125751819107" datatype="html"> 7528
7492 <source>Instances you follow</source> 7529
7493 <target state="new">Instances you follow</target>
7494 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7495 </trans-unit>
7496 <trans-unit id="8899833753704589712" datatype="html">
7497 <source>Instances following you</source>
7498 <target state="new">Instances following you</target>
7499 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7500 </trans-unit>
7501 <trans-unit id="3767259920053407667" datatype="html"> 7530 <trans-unit id="3767259920053407667" datatype="html">
7502 <source>Videos will be deleted, comments will be tombstoned.</source> 7531 <source>Videos will be deleted, comments will be tombstoned.</source>
7503 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7532 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7528,7 +7557,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7528 <target state="new">Set Email as Verified</target> 7557 <target state="new">Set Email as Verified</target>
7529 7558
7530 7559
7531 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7560 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7561 <source>Created</source><target state="new">Created</target>
7562 <context-group purpose="location">
7563 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7564 <context context-type="linenumber">115</context>
7565 </context-group>
7566 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7567 <source>Daily quota</source><target state="new">Daily quota</target>
7568 <context-group purpose="location">
7569 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7570 <context context-type="linenumber">120</context>
7571 </context-group>
7572 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7573 <source>Last login</source><target state="new">Last login</target>
7574 <context-group purpose="location">
7575 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7576 <context context-type="linenumber">122</context>
7577 </context-group>
7578 </trans-unit>
7532 <trans-unit id="3403978719736970622" datatype="html"> 7579 <trans-unit id="3403978719736970622" datatype="html">
7533 <source>You cannot ban root.</source> 7580 <source>You cannot ban root.</source>
7534 <target state="new">You cannot ban root.</target> 7581 <target state="new">You cannot ban root.</target>
@@ -7836,12 +7883,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
7836 <x id="PH"/> created. 7883 <x id="PH"/> created.
7837 </target> 7884 </target>
7838 7885
7839 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7886 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7840 <trans-unit id="8723777130353305761" datatype="html"> 7887 <trans-unit id="8723777130353305761" datatype="html">
7841 <source>This name already exists on this instance.</source> 7888 <source>This name already exists on this instance.</source>
7842 <target state="new">This name already exists on this instance.</target> 7889 <target state="new">This name already exists on this instance.</target>
7843 7890
7844 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7891 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7845 <trans-unit id="7589345916094713536" datatype="html"> 7892 <trans-unit id="7589345916094713536" datatype="html">
7846 <source>Video channel <x id="PH"/> updated.</source> 7893 <source>Video channel <x id="PH"/> updated.</source>
7847 <target state="new">Video channel 7894 <target state="new">Video channel
@@ -7858,13 +7905,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7858 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 7905 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7859 7906
7860 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7907 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7861 <trans-unit id="2575302837003821736" datatype="html"> 7908
7862 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7863 <target state="new">Please type the display name of the video channel (
7864 <x id="PH"/>) to confirm
7865 </target>
7866
7867 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7868 <trans-unit id="624066830180032195" datatype="html"> 7909 <trans-unit id="624066830180032195" datatype="html">
7869 <source>Video channel <x id="PH"/> deleted.</source> 7910 <source>Video channel <x id="PH"/> deleted.</source>
7870 <target state="new">Video channel 7911 <target state="new">Video channel
@@ -8025,6 +8066,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8025 <source>Ownership change request sent.</source> 8066 <source>Ownership change request sent.</source>
8026 <target state="new">Ownership change request sent.</target> 8067 <target state="new">Ownership change request sent.</target>
8027 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8068 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8069 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8070 <source>Sort by</source><target state="new">Sort by</target>
8071 <context-group purpose="location">
8072 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8073 <context context-type="linenumber">26</context>
8074 </context-group>
8028 </trans-unit> 8075 </trans-unit>
8029 <trans-unit id="3245220240937722814" datatype="html"> 8076 <trans-unit id="3245220240937722814" datatype="html">
8030 <source>My channels</source> 8077 <source>My channels</source>
@@ -8130,7 +8177,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8130 <target state="translated">Hesaba abone olundu</target> 8177 <target state="translated">Hesaba abone olundu</target>
8131 8178
8132 8179
8133 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 8180 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
8134 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 8181 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
8135 <context-group purpose="location"> 8182 <context-group purpose="location">
8136 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 8183 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -8176,35 +8223,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8176 <trans-unit id="3779524668013120370" datatype="html"> 8223 <trans-unit id="3779524668013120370" datatype="html">
8177 <source>Go to my subscriptions</source> 8224 <source>Go to my subscriptions</source>
8178 <target state="translated">Aboneliklerime git</target> 8225 <target state="translated">Aboneliklerime git</target>
8179 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8226
8180 </trans-unit> 8227 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8181 <trans-unit id="1136469849928650779" datatype="html"> 8228 <trans-unit id="1136469849928650779" datatype="html">
8182 <source>Go to my videos</source> 8229 <source>Go to my videos</source>
8183 <target state="translated">Videolarıma git</target> 8230 <target state="translated">Videolarıma git</target>
8184 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8231
8185 </trans-unit> 8232 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8186 <trans-unit id="7836683738999600376" datatype="html"> 8233 <trans-unit id="7836683738999600376" datatype="html">
8187 <source>Go to my imports</source> 8234 <source>Go to my imports</source>
8188 <target state="new">Go to my imports</target> 8235 <target state="new">Go to my imports</target>
8189 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8236
8190 </trans-unit> 8237 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8191 <trans-unit id="7511292153332773503" datatype="html"> 8238 <trans-unit id="7511292153332773503" datatype="html">
8192 <source>Go to my channels</source> 8239 <source>Go to my channels</source>
8193 <target state="translated">Kanallarıma git</target> 8240 <target state="translated">Kanallarıma git</target>
8194 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8241
8195 </trans-unit> 8242 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8196 <trans-unit id="2013324644839511073" datatype="html"> 8243 <trans-unit id="2013324644839511073" datatype="html">
8197 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8244 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8198Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8245Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8199 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8246 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8200Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8247Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8201 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8248
8202 </trans-unit> 8249 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8203 <trans-unit id="375263728166936544" datatype="html"> 8250 <trans-unit id="375263728166936544" datatype="html">
8204 <source>You need to reconnect.</source> 8251 <source>You need to reconnect.</source>
8205 <target state="new">You need to reconnect.</target> 8252 <target state="new">You need to reconnect.</target>
8206 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8253
8207 </trans-unit> 8254 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8208 <trans-unit id="2206638022166154361"> 8255 <trans-unit id="2206638022166154361">
8209 <source>Keyboard Shortcuts:</source> 8256 <source>Keyboard Shortcuts:</source>
8210 <target>Klavye Kısayolları:</target> 8257 <target>Klavye Kısayolları:</target>
@@ -8215,6 +8262,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8215 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8262 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8216 <context context-type="linenumber">98</context> 8263 <context context-type="linenumber">98</context>
8217 </context-group> 8264 </context-group>
8265 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8266 <source>In my library</source><target state="new">In my library</target>
8267 <context-group purpose="location">
8268 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8269 <context context-type="linenumber">104</context>
8270 </context-group>
8218 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 8271 </trans-unit><trans-unit id="232050922346936574" datatype="html">
8219 <source>Trending</source><target state="new">Trending</target> 8272 <source>Trending</source><target state="new">Trending</target>
8220 8273
@@ -8238,38 +8291,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8238 <source>Incorrect username or password.</source> 8291 <source>Incorrect username or password.</source>
8239 <target state="translated">Hatalı kullanıcı adı ya da şifre.</target> 8292 <target state="translated">Hatalı kullanıcı adı ya da şifre.</target>
8240 8293
8241 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 8294 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8242 <trans-unit id="6974874606619467663" datatype="html"> 8295 <trans-unit id="6974874606619467663" datatype="html">
8243 <source>Your account is blocked.</source> 8296 <source>Your account is blocked.</source>
8244 <target state="translated">Hesabınız engellenmiş.</target> 8297 <target state="translated">Hesabınız engellenmiş.</target>
8245 8298
8246 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 8299 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8247 <trans-unit id="7939914198003891823" datatype="html"> 8300 <trans-unit id="7939914198003891823" datatype="html">
8248 <source>any language</source> 8301 <source>any language</source>
8249 <target state="translated">herhangi bir dil</target> 8302 <target state="translated">herhangi bir dil</target>
8250 8303
8251 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8304 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8252 8305
8253 <trans-unit id="5633144232269377096" datatype="html"> 8306 <trans-unit id="5633144232269377096" datatype="html">
8254 <source>hide</source> 8307 <source>hide</source>
8255 <target state="translated">gizle</target> 8308 <target state="translated">gizle</target>
8256 8309
8257 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8310 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8258 <trans-unit id="8603861867909474404" datatype="html"> 8311 <trans-unit id="8603861867909474404" datatype="html">
8259 <source>blur</source> 8312 <source>blur</source>
8260 <target state="translated">bulanıklaştır</target> 8313 <target state="translated">bulanıklaştır</target>
8261 8314
8262 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8315 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8263 <trans-unit id="4534458451100881847" datatype="html"> 8316 <trans-unit id="4534458451100881847" datatype="html">
8264 <source>display</source> 8317 <source>display</source>
8265 <target state="translated">göster</target> 8318 <target state="translated">göster</target>
8266 8319
8267 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8320 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8268 <trans-unit id="4467323362722952678" datatype="html"> 8321 <trans-unit id="4467323362722952678" datatype="html">
8269 <source>Unknown</source> 8322 <source>Unknown</source>
8270 <target state="translated">Bilinmiyor</target> 8323 <target state="translated">Bilinmiyor</target>
8271 8324
8272 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8325 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8273 <trans-unit id="8781423666414310853" datatype="html"> 8326 <trans-unit id="8781423666414310853" datatype="html">
8274 <source>Your password has been successfully reset!</source> 8327 <source>Your password has been successfully reset!</source>
8275 <target state="translated">Şifreniz başarıyla sıfırlandı!</target> 8328 <target state="translated">Şifreniz başarıyla sıfırlandı!</target>
@@ -9833,18 +9886,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9833 <target state="new">Too many attempts, please try again after 9886 <target state="new">Too many attempts, please try again after
9834 <x id="PH"/> minutes. 9887 <x id="PH"/> minutes.
9835 </target> 9888 </target>
9836 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9889
9837 </trans-unit> 9890 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9838 <trans-unit id="4965472196059235310" datatype="html"> 9891 <trans-unit id="4965472196059235310" datatype="html">
9839 <source>Too many attempts, please try again later.</source> 9892 <source>Too many attempts, please try again later.</source>
9840 <target state="new">Too many attempts, please try again later.</target> 9893 <target state="new">Too many attempts, please try again later.</target>
9841 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9894
9842 </trans-unit> 9895 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9843 <trans-unit id="1693549688987384699" datatype="html"> 9896 <trans-unit id="1693549688987384699" datatype="html">
9844 <source>Server error. Please retry later.</source> 9897 <source>Server error. Please retry later.</source>
9845 <target state="new">Server error. Please retry later.</target> 9898 <target state="new">Server error. Please retry later.</target>
9846 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9899
9847 </trans-unit> 9900 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9848 <trans-unit id="5927402622550505067" datatype="html"> 9901 <trans-unit id="5927402622550505067" datatype="html">
9849 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9902 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9850 <target state="new">Subscribed to all current channels of 9903 <target state="new">Subscribed to all current channels of
@@ -10439,35 +10492,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10439 <source>Your video was uploaded to your account and is private.</source> 10492 <source>Your video was uploaded to your account and is private.</source>
10440 <target state="new">Your video was uploaded to your account and is private.</target> 10493 <target state="new">Your video was uploaded to your account and is private.</target>
10441 10494
10442 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10495 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10443 <trans-unit id="5699822024600815733" datatype="html"> 10496 <trans-unit id="5699822024600815733" datatype="html">
10444 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10497 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10445 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target> 10498 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target>
10446 10499
10447 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10500 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10448 <trans-unit id="1219739004043110649" datatype="html"> 10501 <trans-unit id="1219739004043110649" datatype="html">
10449 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10502 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10450 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target> 10503 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target>
10451 10504
10452 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10505 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10453 <trans-unit id="6932865105766151309" datatype="html"> 10506 <trans-unit id="6932865105766151309" datatype="html">
10454 <source>Upload</source> 10507 <source>Upload</source>
10455 <target state="new">Upload</target> 10508 <target state="new">Upload</target>
10456 10509
10457 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10510 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10458 <trans-unit id="8278735427925094503" datatype="html"> 10511 <trans-unit id="8278735427925094503" datatype="html">
10459 <source>Upload <x id="PH"/> </source> 10512 <source>Upload <x id="PH"/> </source>
10460 <target state="new">Upload 10513 <target state="new">Upload
10461 <x id="PH"/> 10514 <x id="PH"/>
10462 </target> 10515 </target>
10463 10516
10464 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10517 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10465 10518
10466 <trans-unit id="5981816353437801748" datatype="html"> 10519 <trans-unit id="5981816353437801748" datatype="html">
10467 <source>Video published.</source> 10520 <source>Video published.</source>
10468 <target state="translated">Video yayınlandı.</target> 10521 <target state="translated">Video yayınlandı.</target>
10469 10522
10470 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10523 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10471 10524
10472 10525
10473 <trans-unit id="764164089183618119" datatype="html"> 10526 <trans-unit id="764164089183618119" datatype="html">
@@ -10535,27 +10588,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10535 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10588 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10536 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10589 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10537 10590
10538 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10591 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10539 <trans-unit id="5761611056224181752" datatype="html"> 10592 <trans-unit id="5761611056224181752" datatype="html">
10540 <source>Redirection</source> 10593 <source>Redirection</source>
10541 <target state="new">Redirection</target> 10594 <target state="new">Redirection</target>
10542 10595
10543 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10596 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10544 <trans-unit id="8858527736400081688" datatype="html"> 10597 <trans-unit id="8858527736400081688" datatype="html">
10545 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10598 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10546 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target> 10599 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target>
10547 10600
10548 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10601 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10549 <trans-unit id="3937119019020041049" datatype="html"> 10602 <trans-unit id="3937119019020041049" datatype="html">
10550 <source>Mature or explicit content</source> 10603 <source>Mature or explicit content</source>
10551 <target state="new">Mature or explicit content</target> 10604 <target state="new">Mature or explicit content</target>
10552 10605
10553 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10606 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10554 <trans-unit id="1755474755114288376" datatype="html"> 10607 <trans-unit id="1755474755114288376" datatype="html">
10555 <source>Up Next</source> 10608 <source>Up Next</source>
10556 <target state="new">Up Next</target> 10609 <target state="new">Up Next</target>
10557 10610
10558 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10611 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10559 <trans-unit id="2159130950882492111" datatype="html"> 10612 <trans-unit id="2159130950882492111" datatype="html">
10560 <source>Cancel</source> 10613 <source>Cancel</source>
10561 <target state="new">Cancel</target> 10614 <target state="new">Cancel</target>
@@ -10565,62 +10618,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10565 <source>Autoplay is suspended</source> 10618 <source>Autoplay is suspended</source>
10566 <target state="new">Autoplay is suspended</target> 10619 <target state="new">Autoplay is suspended</target>
10567 10620
10568 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10621 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10569 <trans-unit id="7895294730547405228" datatype="html"> 10622 <trans-unit id="7895294730547405228" datatype="html">
10570 <source>Enter/exit fullscreen (requires player focus)</source> 10623 <source>Enter/exit fullscreen (requires player focus)</source>
10571 <target state="translated">Tam ekrana gir/ekrandan çık (oynatıcı odağı gerekli)</target> 10624 <target state="translated">Tam ekrana gir/ekrandan çık (oynatıcı odağı gerekli)</target>
10572 10625
10573 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10626 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10574 <trans-unit id="7618388257165864759" datatype="html"> 10627 <trans-unit id="7618388257165864759" datatype="html">
10575 <source>Play/Pause the video (requires player focus)</source> 10628 <source>Play/Pause the video (requires player focus)</source>
10576 <target state="translated">Videoyu oynat/durdur (oynatıcı odağı gerekli)</target> 10629 <target state="translated">Videoyu oynat/durdur (oynatıcı odağı gerekli)</target>
10577 10630
10578 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10631 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10579 <trans-unit id="7761890399634216630" datatype="html"> 10632 <trans-unit id="7761890399634216630" datatype="html">
10580 <source>Mute/unmute the video (requires player focus)</source> 10633 <source>Mute/unmute the video (requires player focus)</source>
10581 <target state="translated">Videonun sesini aç/kapat (oynatıcı odağı gerekli)</target> 10634 <target state="translated">Videonun sesini aç/kapat (oynatıcı odağı gerekli)</target>
10582 10635
10583 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10636 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10584 <trans-unit id="5996585232248234904" datatype="html"> 10637 <trans-unit id="5996585232248234904" datatype="html">
10585 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10638 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10586 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10639 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10587 10640
10588 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10641 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10589 <trans-unit id="3748765405903319998" datatype="html"> 10642 <trans-unit id="3748765405903319998" datatype="html">
10590 <source>Increase the volume (requires player focus)</source> 10643 <source>Increase the volume (requires player focus)</source>
10591 <target state="new">Increase the volume (requires player focus)</target> 10644 <target state="new">Increase the volume (requires player focus)</target>
10592 10645
10593 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10646 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10594 <trans-unit id="5810704036407159982" datatype="html"> 10647 <trans-unit id="5810704036407159982" datatype="html">
10595 <source>Decrease the volume (requires player focus)</source> 10648 <source>Decrease the volume (requires player focus)</source>
10596 <target state="new">Decrease the volume (requires player focus)</target> 10649 <target state="new">Decrease the volume (requires player focus)</target>
10597 10650
10598 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10651 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10599 <trans-unit id="2622048822548065691" datatype="html"> 10652 <trans-unit id="2622048822548065691" datatype="html">
10600 <source>Seek the video forward (requires player focus)</source> 10653 <source>Seek the video forward (requires player focus)</source>
10601 <target state="new">Seek the video forward (requires player focus)</target> 10654 <target state="new">Seek the video forward (requires player focus)</target>
10602 10655
10603 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10656 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10604 <trans-unit id="6540078205109221153" datatype="html"> 10657 <trans-unit id="6540078205109221153" datatype="html">
10605 <source>Seek the video backward (requires player focus)</source> 10658 <source>Seek the video backward (requires player focus)</source>
10606 <target state="new">Seek the video backward (requires player focus)</target> 10659 <target state="new">Seek the video backward (requires player focus)</target>
10607 10660
10608 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10661 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10609 <trans-unit id="1956491957766210808" datatype="html"> 10662 <trans-unit id="1956491957766210808" datatype="html">
10610 <source>Increase playback rate (requires player focus)</source> 10663 <source>Increase playback rate (requires player focus)</source>
10611 <target state="new">Increase playback rate (requires player focus)</target> 10664 <target state="new">Increase playback rate (requires player focus)</target>
10612 10665
10613 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10666 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10614 <trans-unit id="5495529997674803186" datatype="html"> 10667 <trans-unit id="5495529997674803186" datatype="html">
10615 <source>Decrease playback rate (requires player focus)</source> 10668 <source>Decrease playback rate (requires player focus)</source>
10616 <target state="new">Decrease playback rate (requires player focus)</target> 10669 <target state="new">Decrease playback rate (requires player focus)</target>
10617 10670
10618 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10671 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10619 <trans-unit id="3178343147230721210" datatype="html"> 10672 <trans-unit id="3178343147230721210" datatype="html">
10620 <source>Navigate in the video frame by frame (requires player focus)</source> 10673 <source>Navigate in the video frame by frame (requires player focus)</source>
10621 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 10674 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10622 10675
10623 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10676 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10624 <trans-unit id="8025996572234182184" datatype="html"> 10677 <trans-unit id="8025996572234182184" datatype="html">
10625 <source>Like the video</source> 10678 <source>Like the video</source>
10626 <target state="translated">Videoyu beğen</target> 10679 <target state="translated">Videoyu beğen</target>
diff --git a/client/src/locale/angular.uk-UA.xlf b/client/src/locale/angular.uk-UA.xlf
index b3edca5d1..031552454 100644
--- a/client/src/locale/angular.uk-UA.xlf
+++ b/client/src/locale/angular.uk-UA.xlf
@@ -159,7 +159,7 @@
159 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 159 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
160 </target> 160 </target>
161 161
162 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 162 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
163 <trans-unit id="1486537403020619891" datatype="html"> 163 <trans-unit id="1486537403020619891" datatype="html">
164 <source>My watch history</source> 164 <source>My watch history</source>
165 <target state="translated">Моя історія перегляду</target> 165 <target state="translated">Моя історія перегляду</target>
@@ -234,12 +234,12 @@
234 234
235 235
236 236
237 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 237 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
238 <trans-unit id="1006562256968398209" datatype="html"> 238 <trans-unit id="1006562256968398209" datatype="html">
239 <source>video</source> 239 <source>video</source>
240 <target state="translated">відео</target> 240 <target state="translated">відео</target>
241 241
242 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html"> 242 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit><trans-unit id="6438815964972582865" datatype="html">
243 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target> 243 <source> The following link contains a private token and should not be shared with anyone. </source><target state="new"> The following link contains a private token and should not be shared with anyone. </target>
244 244
245 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html"> 245 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">19</context></context-group></trans-unit><trans-unit id="187187500641108332" datatype="html">
@@ -303,10 +303,10 @@
303 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html"> 303 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">289</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit><trans-unit id="6995024616159044376" datatype="html">
304 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 304 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source><target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
305 305
306 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html"> 306 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit><trans-unit id="7873395933409147217" datatype="html">
307 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 307 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source><target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
308 308
309 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group></trans-unit> 309 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
310 310
311 <trans-unit id="5235042777215655908" datatype="html"> 311 <trans-unit id="5235042777215655908" datatype="html">
312 <source>subtitles</source> 312 <source>subtitles</source>
@@ -692,10 +692,10 @@
692 <trans-unit id="2602586221576511475"> 692 <trans-unit id="2602586221576511475">
693 <source>Video quota</source> 693 <source>Video quota</source>
694 <target>Квота відео</target> 694 <target>Квота відео</target>
695 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 695
696 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 696
697 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 697
698 </trans-unit> 698 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
699 <trans-unit id="1502595455339510144"> 699 <trans-unit id="1502595455339510144">
700 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 700 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
701 <target state="translated">Без обмежень <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> на день)<x id="CLOSE_TAG_NG_CONTAINER"/></target> 701 <target state="translated">Без обмежень <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> на день)<x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -774,7 +774,31 @@
774 <source>Federation</source> 774 <source>Federation</source>
775 <target state="translated">Федерація</target> 775 <target state="translated">Федерація</target>
776 776
777 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit> 777 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit><trans-unit id="8726138323871139597" datatype="html">
778 <source>Following</source><target state="new">Following</target>
779 <context-group purpose="location">
780 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
781 <context context-type="linenumber">29</context>
782 </context-group>
783 <context-group purpose="location">
784 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
785 <context context-type="linenumber">31</context>
786 </context-group>
787 <context-group purpose="location">
788 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
789 <context context-type="linenumber">28</context>
790 </context-group>
791 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
792 <source>Followers</source><target state="new">Followers</target>
793 <context-group purpose="location">
794 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
795 <context context-type="linenumber">34</context>
796 </context-group>
797 <context-group purpose="location">
798 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
799 <context context-type="linenumber">37</context>
800 </context-group>
801 </trans-unit>
778 <trans-unit id="3541687134897970106" datatype="html"> 802 <trans-unit id="3541687134897970106" datatype="html">
779 <source>followers</source> 803 <source>followers</source>
780 <target state="translated">підписники</target> 804 <target state="translated">підписники</target>
@@ -849,7 +873,7 @@
849 873
850 874
851 875
852 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 876 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
853 <trans-unit id="3616223838716839702"> 877 <trans-unit id="3616223838716839702">
854 <source>Ban this user</source> 878 <source>Ban this user</source>
855 <target>Заблокувати цього користувача</target> 879 <target>Заблокувати цього користувача</target>
@@ -913,19 +937,13 @@
913 <trans-unit id="7252854992688790751" datatype="html"> 937 <trans-unit id="7252854992688790751" datatype="html">
914 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 938 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
915 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 939 <target state="new"> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
916 <context-group purpose="location"> 940
917 <context context-type="sourcefile">src/app/+login/login.component.html</context> 941 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
918 <context context-type="linenumber">60,62</context>
919 </context-group>
920 </trans-unit>
921 <trans-unit id="7215649348148521605" datatype="html"> 942 <trans-unit id="7215649348148521605" datatype="html">
922 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 943 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
923 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 944 <target state="new"> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
924 <context-group purpose="location"> 945
925 <context context-type="sourcefile">src/app/+login/login.component.html</context> 946 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
926 <context context-type="linenumber">65,67</context>
927 </context-group>
928 </trans-unit>
929 <trans-unit id="2392488717875840729"> 947 <trans-unit id="2392488717875840729">
930 <source>User</source> 948 <source>User</source>
931 <target>Користувач</target> 949 <target>Користувач</target>
@@ -936,66 +954,66 @@
936 <source>Username or email address</source> 954 <source>Username or email address</source>
937 <target>Ім'я користувача або email</target> 955 <target>Ім'я користувача або email</target>
938 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 956 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
957 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
958 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
959 <context-group purpose="location">
960 <context context-type="sourcefile">src/app/+login/login.component.html</context>
961 <context context-type="linenumber">33,34</context>
962 </context-group>
939 </trans-unit> 963 </trans-unit>
940 <trans-unit id="1431416938026210429"> 964 <trans-unit id="1431416938026210429">
941 <source>Password</source> 965 <source>Password</source>
942 <target>Пароль</target> 966 <target>Пароль</target>
943 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 967
944 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 968
945 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 969
946 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 970
947 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 971
948 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 972
949 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 973
950 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 974
951 </trans-unit> 975 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
952 <trans-unit id="8715156686857791956" datatype="html"> 976 <trans-unit id="8715156686857791956" datatype="html">
953 <source>Click here to reset your password</source> 977 <source>Click here to reset your password</source>
954 <target state="translated">Натисніть, щоб скинути ваш пароль</target> 978 <target state="translated">Натисніть, щоб скинути ваш пароль</target>
955 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 979
956 </trans-unit> 980 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
957 <trans-unit id="892063502898494584" datatype="html"> 981 <trans-unit id="892063502898494584" datatype="html">
958 <source>I forgot my password</source> 982 <source>I forgot my password</source>
959 <target state="new">I forgot my password</target> 983 <target state="new">I forgot my password</target>
960 <context-group purpose="location"> 984
961 <context context-type="sourcefile">src/app/+login/login.component.html</context> 985 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
962 <context context-type="linenumber">47</context>
963 </context-group>
964 </trans-unit>
965 <trans-unit id="2101170466365500913" datatype="html"> 986 <trans-unit id="2101170466365500913" datatype="html">
966 <source>Logging into an account lets you publish content</source> 987 <source>Logging into an account lets you publish content</source>
967 <target state="new"> Logging into an account lets you publish content </target> 988 <target state="new"> Logging into an account lets you publish content </target>
968 <context-group purpose="location"> 989
969 <context context-type="sourcefile">src/app/+login/login.component.html</context> 990 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
970 <context context-type="linenumber">56,57</context>
971 </context-group>
972 </trans-unit>
973 <trans-unit id="2454050363478003966" datatype="html"> 991 <trans-unit id="2454050363478003966" datatype="html">
974 <source>Login</source> 992 <source>Login</source>
975 <target state="translated">Увійти за допомогою імені</target> 993 <target state="translated">Увійти за допомогою імені</target>
976 994
977 995
978 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit> 996 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
979 <trans-unit id="3183213940445113677" datatype="html"> 997 <trans-unit id="3183213940445113677" datatype="html">
980 <source>Or sign in with</source> 998 <source>Or sign in with</source>
981 <target state="translated">Або увійти за допомогою</target> 999 <target state="translated">Або увійти за допомогою</target>
982 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1000
983 </trans-unit> 1001 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
984 <trans-unit id="3238209155172574367" datatype="html"> 1002 <trans-unit id="3238209155172574367" datatype="html">
985 <source>Forgot your password</source> 1003 <source>Forgot your password</source>
986 <target state="translated">Не пам'ятаю пароль</target> 1004 <target state="translated">Не пам'ятаю пароль</target>
987 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1005
988 </trans-unit> 1006 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
989 <trans-unit id="87327320394367488" datatype="html"> 1007 <trans-unit id="87327320394367488" datatype="html">
990 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1008 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
991 <target state="translated">Вибачте, але ви не можете відновити пароль, тому що адміністратор вашого сервера не налаштував систему надсилання електронних листів PeerTube.</target> 1009 <target state="translated">Вибачте, але ви не можете відновити пароль, тому що адміністратор вашого сервера не налаштував систему надсилання електронних листів PeerTube.</target>
992 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1010
993 </trans-unit> 1011 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
994 <trans-unit id="3188014010833256853" datatype="html"> 1012 <trans-unit id="3188014010833256853" datatype="html">
995 <source>Enter your email address and we will send you a link to reset your password.</source> 1013 <source>Enter your email address and we will send you a link to reset your password.</source>
996 <target state="new"> Enter your email address and we will send you a link to reset your password. </target> 1014 <target state="new"> Enter your email address and we will send you a link to reset your password. </target>
997 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1015
998 </trans-unit> 1016 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
999 <trans-unit id="1190256911880544559" datatype="html"> 1017 <trans-unit id="1190256911880544559" datatype="html">
1000 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1018 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1001The link will expire within 1 hour.</source> 1019The link will expire within 1 hour.</source>
@@ -1006,26 +1024,26 @@ The link will expire within 1 hour.</target>
1006 <trans-unit id="4768749765465246664" datatype="html"> 1024 <trans-unit id="4768749765465246664" datatype="html">
1007 <source>Email</source> 1025 <source>Email</source>
1008 <target state="translated">Email</target> 1026 <target state="translated">Email</target>
1009 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1027
1010 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1028
1011 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1029
1012 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1030
1013 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1031
1014 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1032
1015 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1033
1016 </trans-unit> 1034 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1017 <trans-unit id="3967269098753656610" datatype="html"> 1035 <trans-unit id="3967269098753656610" datatype="html">
1018 <source>Email address</source> 1036 <source>Email address</source>
1019 <target state="translated">Адреса електронної пошти</target> 1037 <target state="translated">Адреса електронної пошти</target>
1020 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1038
1021 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1039
1022 </trans-unit> 1040 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1023 <trans-unit id="7808756054397155068" datatype="html"> 1041 <trans-unit id="7808756054397155068" datatype="html">
1024 <source>Reset</source> 1042 <source>Reset</source>
1025 <target state="new">Reset</target> 1043 <target state="new">Reset</target>
1026 <note priority="1" from="description">Password reset button</note> 1044 <note priority="1" from="description">Password reset button</note>
1027 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1045
1028 </trans-unit> 1046 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1029 1047
1030 <trans-unit id="4319634264526091601" datatype="html"> 1048 <trans-unit id="4319634264526091601" datatype="html">
1031 <source>on this instance</source> 1049 <source>on this instance</source>
@@ -1373,7 +1391,7 @@ The link will expire within 1 hour.</target>
1373 <target state="translated">Створити обліковий запис</target> 1391 <target state="translated">Створити обліковий запис</target>
1374 1392
1375 1393
1376 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit> 1394 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1377 1395
1378 <trans-unit id="3058024914967508975" datatype="html"> 1396 <trans-unit id="3058024914967508975" datatype="html">
1379 <source>My videos</source> 1397 <source>My videos</source>
@@ -1430,7 +1448,7 @@ The link will expire within 1 hour.</target>
1430 <source>VIDEOS</source> 1448 <source>VIDEOS</source>
1431 <target state="translated">ВІДЕО</target> 1449 <target state="translated">ВІДЕО</target>
1432 1450
1433 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 1451 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1434 <trans-unit id="667372110624203230" datatype="html"> 1452 <trans-unit id="667372110624203230" datatype="html">
1435 <source>Import jobs concurrency</source> 1453 <source>Import jobs concurrency</source>
1436 <target state="new">Import jobs concurrency</target> 1454 <target state="new">Import jobs concurrency</target>
@@ -1510,7 +1528,7 @@ The link will expire within 1 hour.</target>
1510 <source>I'm a teapot</source> 1528 <source>I'm a teapot</source>
1511 <target state="new">I'm a teapot</target> 1529 <target state="new">I'm a teapot</target>
1512 1530
1513 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group></trans-unit> 1531 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1514 <trans-unit id="1597262876035959248" datatype="html"> 1532 <trans-unit id="1597262876035959248" datatype="html">
1515 <source>That's an error.</source> 1533 <source>That's an error.</source>
1516 <target state="new">That's an error.</target> 1534 <target state="new">That's an error.</target>
@@ -1594,8 +1612,8 @@ The link will expire within 1 hour.</target>
1594 <trans-unit id="2971365540217107489" datatype="html"> 1612 <trans-unit id="2971365540217107489" datatype="html">
1595 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1613 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1596 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target> 1614 <target state="new">Media is too large for the server. Please contact you administrator if you want to increase the limit size.</target>
1597 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1615
1598 </trans-unit> 1616 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1599 1617
1600 <trans-unit id="5131854469652959713" datatype="html"> 1618 <trans-unit id="5131854469652959713" datatype="html">
1601 <source>GLOBAL SEARCH</source> 1619 <source>GLOBAL SEARCH</source>
@@ -2306,7 +2324,7 @@ The link will expire within 1 hour.</target>
2306 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html"> 2324 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">106</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/header/header.component.html</context><context context-type="linenumber">5</context></context-group></trans-unit><trans-unit id="6161604372916832458" datatype="html">
2307 <source>Upload on hold</source><target state="new">Upload on hold</target> 2325 <source>Upload on hold</source><target state="new">Upload on hold</target>
2308 2326
2309 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit> 2327 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2310 <trans-unit id="285180972645018518" datatype="html"> 2328 <trans-unit id="285180972645018518" datatype="html">
2311 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2329 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2312 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target> 2330 <target state="new">Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</target>
@@ -3047,11 +3065,7 @@ The link will expire within 1 hour.</target>
3047 <target state="new">ID</target> 3065 <target state="new">ID</target>
3048 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3066 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3049 </trans-unit> 3067 </trans-unit>
3050 <trans-unit id="2265605798180116441" datatype="html"> 3068
3051 <source>Follower handle</source>
3052 <target state="new">Follower handle</target>
3053
3054 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group></trans-unit>
3055 <trans-unit id="5911214550882917183" datatype="html"> 3069 <trans-unit id="5911214550882917183" datatype="html">
3056 <source>State</source> 3070 <source>State</source>
3057 <target state="new">State</target> 3071 <target state="new">State</target>
@@ -3119,11 +3133,7 @@ The link will expire within 1 hour.</target>
3119 </target> 3133 </target>
3120 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3134 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3121 </trans-unit> 3135 </trans-unit>
3122 <trans-unit id="6641024648411549335" datatype="html"> 3136
3123 <source>Host</source>
3124 <target state="new">Host</target>
3125
3126 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group></trans-unit>
3127 <trans-unit id="6571718060636962350" datatype="html"> 3137 <trans-unit id="6571718060636962350" datatype="html">
3128 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3138 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3129 <target state="translated">Надлишковість дозволена <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 3139 <target state="translated">Надлишковість дозволена <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -3133,7 +3143,7 @@ The link will expire within 1 hour.</target>
3133 <source>Unfollow</source> 3143 <source>Unfollow</source>
3134 <target state="new">Unfollow</target> 3144 <target state="new">Unfollow</target>
3135 3145
3136 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group></trans-unit> 3146 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3137 <trans-unit id="8246779176913476983" datatype="html"> 3147 <trans-unit id="8246779176913476983" datatype="html">
3138 <source>Open instance in a new tab</source> 3148 <source>Open instance in a new tab</source>
3139 <target state="new">Open instance in a new tab</target> 3149 <target state="new">Open instance in a new tab</target>
@@ -3145,27 +3155,19 @@ The link will expire within 1 hour.</target>
3145 <source>No host found matching current filters.</source> 3155 <source>No host found matching current filters.</source>
3146 <target state="new">No host found matching current filters.</target> 3156 <target state="new">No host found matching current filters.</target>
3147 3157
3148 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group></trans-unit> 3158 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3149 <trans-unit id="7274241885665071790" datatype="html"> 3159 <trans-unit id="7274241885665071790" datatype="html">
3150 <source>Your instance is not following anyone.</source> 3160 <source>Your instance is not following anyone.</source>
3151 <target state="new">Your instance is not following anyone.</target> 3161 <target state="new">Your instance is not following anyone.</target>
3152 3162
3153 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit> 3163 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3154 <trans-unit id="4774348799569692380" datatype="html"> 3164 <trans-unit id="4774348799569692380" datatype="html">
3155 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3165 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3156 <target state="translated">Показано <x id="INTERPOLATION"/> до <x id="INTERPOLATION_1"/> з <x id="INTERPOLATION_2"/> хостів</target> 3166 <target state="translated">Показано <x id="INTERPOLATION"/> до <x id="INTERPOLATION_1"/> з <x id="INTERPOLATION_2"/> хостів</target>
3157 3167
3158 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit> 3168 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group></trans-unit>
3159 <trans-unit id="6275803119759621687" datatype="html"> 3169
3160 <source>Follow domains</source> 3170 <trans-unit id="9216117865911519658" datatype="html">
3161 <target state="new">Follow domains</target>
3162
3163 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group></trans-unit>
3164 <trans-unit id="1268699198448750610" datatype="html">
3165 <source>Follow instances</source>
3166 <target state="new">Follow instances</target>
3167
3168 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group></trans-unit><trans-unit id="9216117865911519658" datatype="html">
3169 <source>Action</source><target state="new">Action</target> 3171 <source>Action</source><target state="new">Action</target>
3170 3172
3171 3173
@@ -3212,11 +3214,11 @@ The link will expire within 1 hour.</target>
3212 <trans-unit id="5248717555542428023" datatype="html"> 3214 <trans-unit id="5248717555542428023" datatype="html">
3213 <source>Username</source> 3215 <source>Username</source>
3214 <target state="new">Username</target> 3216 <target state="new">Username</target>
3215 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3217
3216 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3218
3217 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3219
3218 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3220
3219 </trans-unit> 3221 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3220 <trans-unit id="5428411040014095392" datatype="html"> 3222 <trans-unit id="5428411040014095392" datatype="html">
3221 <source>e.g. jane_doe</source> 3223 <source>e.g. jane_doe</source>
3222 <target state="new">e.g. jane_doe</target> 3224 <target state="new">e.g. jane_doe</target>
@@ -3246,9 +3248,9 @@ The link will expire within 1 hour.</target>
3246 <trans-unit id="4145496584631696119" datatype="html"> 3248 <trans-unit id="4145496584631696119" datatype="html">
3247 <source>Role</source> 3249 <source>Role</source>
3248 <target state="new">Role</target> 3250 <target state="new">Role</target>
3249 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3251
3250 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3252
3251 </trans-unit> 3253 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3252 <trans-unit id="7046347992315328430" datatype="html"> 3254 <trans-unit id="7046347992315328430" datatype="html">
3253 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3255 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3254 <target state="new"> 3256 <target state="new">
@@ -3273,15 +3275,9 @@ The link will expire within 1 hour.</target>
3273 <trans-unit id="2622255144026150901" datatype="html"> 3275 <trans-unit id="2622255144026150901" datatype="html">
3274 <source>Auth plugin</source> 3276 <source>Auth plugin</source>
3275 <target state="new">Auth plugin</target> 3277 <target state="new">Auth plugin</target>
3276 <context-group purpose="location"> 3278
3277 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3279
3278 <context context-type="linenumber">188</context> 3280 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3279 </context-group>
3280 <context-group purpose="location">
3281 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3282 <context context-type="linenumber">188</context>
3283 </context-group>
3284 </trans-unit>
3285 <trans-unit id="588099657508661970" datatype="html"> 3281 <trans-unit id="588099657508661970" datatype="html">
3286 <source>None (local authentication)</source> 3282 <source>None (local authentication)</source>
3287 <target state="new">None (local authentication)</target> 3283 <target state="new">None (local authentication)</target>
@@ -3530,7 +3526,13 @@ The link will expire within 1 hour.</target>
3530 3526
3531 3527
3532 3528
3533 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit> 3529 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">23</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-block-list/video-block-list.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group></trans-unit><trans-unit id="8390803680962035202" datatype="html">
3530 <source>Follower</source><target state="new">Follower</target>
3531 <context-group purpose="location">
3532 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3533 <context context-type="linenumber">24</context>
3534 </context-group>
3535 </trans-unit>
3534 <trans-unit id="4691552465058437520" datatype="html"> 3536 <trans-unit id="4691552465058437520" datatype="html">
3535 <source>Commented video</source> 3537 <source>Commented video</source>
3536 <target state="new">Commented video</target> 3538 <target state="new">Commented video</target>
@@ -3826,7 +3828,7 @@ The link will expire within 1 hour.</target>
3826 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 3828 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
3827 </target> 3829 </target>
3828 3830
3829 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit> 3831 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3830 <trans-unit id="4058814854824495833" datatype="html"> 3832 <trans-unit id="4058814854824495833" datatype="html">
3831 <source>Mute domains</source> 3833 <source>Mute domains</source>
3832 <target state="new">Mute domains</target> 3834 <target state="new">Mute domains</target>
@@ -5773,11 +5775,8 @@ color: red;
5773 5775
5774 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html"> 5776 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.html</context><context context-type="linenumber">80</context></context-group></trans-unit><trans-unit id="5512878593724620692" datatype="html">
5775 <source>CHANNELS</source><target state="new">CHANNELS</target> 5777 <source>CHANNELS</source><target state="new">CHANNELS</target>
5776 <context-group purpose="location"> 5778
5777 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5779 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5778 <context context-type="linenumber">82</context>
5779 </context-group>
5780 </trans-unit>
5781 5780
5782 <trans-unit id="3666829335406793239" datatype="html"> 5781 <trans-unit id="3666829335406793239" datatype="html">
5783 <source>This account does not have channels.</source> 5782 <source>This account does not have channels.</source>
@@ -5813,7 +5812,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
5813It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 5812It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
5814channel with the same name (<x id="PH_2"/>)!</target> 5813channel with the same name (<x id="PH_2"/>)!</target>
5815 5814
5816 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit> 5815 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group></trans-unit><trans-unit id="4433306639366959484" datatype="html">
5816 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5817 <context-group purpose="location">
5818 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5819 <context context-type="linenumber">48</context>
5820 </context-group>
5821 </trans-unit>
5817 <trans-unit id="5387007581996837469" datatype="html"> 5822 <trans-unit id="5387007581996837469" datatype="html">
5818 <source>My Channels</source> 5823 <source>My Channels</source>
5819 <target state="new">My Channels</target> 5824 <target state="new">My Channels</target>
@@ -6404,12 +6409,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6404 <source>Your message has been sent.</source> 6409 <source>Your message has been sent.</source>
6405 <target state="new">Your message has been sent.</target> 6410 <target state="new">Your message has been sent.</target>
6406 6411
6407 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6412 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6408 <trans-unit id="2072135752262464360" datatype="html"> 6413 <trans-unit id="2072135752262464360" datatype="html">
6409 <source>You already sent this form recently</source> 6414 <source>You already sent this form recently</source>
6410 <target state="new">You already sent this form recently</target> 6415 <target state="new">You already sent this form recently</target>
6411 6416
6412 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6417 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6413 6418
6414 <trans-unit id="819067926858619041" datatype="html"> 6419 <trans-unit id="819067926858619041" datatype="html">
6415 <source>Account videos</source> 6420 <source>Account videos</source>
@@ -6457,12 +6462,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6457 <x id="PH"/> direct account followers 6462 <x id="PH"/> direct account followers
6458 </target> 6463 </target>
6459 6464
6460 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group></trans-unit> 6465 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6461 <trans-unit id="6250999352462648289" datatype="html"> 6466 <trans-unit id="6250999352462648289" datatype="html">
6462 <source>Report this account</source> 6467 <source>Report this account</source>
6463 <target state="new">Report this account</target> 6468 <target state="new">Report this account</target>
6464 6469
6465 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit> 6470 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6466 6471
6467 6472
6468 <trans-unit id="1504521795586863905" datatype="html"> 6473 <trans-unit id="1504521795586863905" datatype="html">
@@ -6477,27 +6482,19 @@ channel with the same name (<x id="PH_2"/>)!</target>
6477 <target state="translated">Ім'я користувача скопійовано</target> 6482 <target state="translated">Ім'я користувача скопійовано</target>
6478 6483
6479 6484
6480 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit> 6485 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6481 <trans-unit id="9221735175659318025" datatype="html"> 6486 <trans-unit id="9221735175659318025" datatype="html">
6482 <source>1 subscriber</source> 6487 <source>1 subscriber</source>
6483 <target state="translated">1 підписник</target> 6488 <target state="translated">1 підписник</target>
6484 6489
6485 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group></trans-unit> 6490 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6486 <trans-unit id="4097331874769079975" datatype="html"> 6491 <trans-unit id="4097331874769079975" datatype="html">
6487 <source><x id="PH"/> subscribers</source> 6492 <source><x id="PH"/> subscribers</source>
6488 <target state="translated"><x id="PH"/> підписники</target> 6493 <target state="translated"><x id="PH"/> підписники</target>
6489 6494
6490 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group></trans-unit> 6495 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6491 <trans-unit id="4682675125751819107" datatype="html"> 6496
6492 <source>Instances you follow</source> 6497
6493 <target state="new">Instances you follow</target>
6494
6495 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6496 <trans-unit id="8899833753704589712" datatype="html">
6497 <source>Instances following you</source>
6498 <target state="new">Instances following you</target>
6499
6500 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group></trans-unit>
6501 <trans-unit id="1035838766454786107" datatype="html"> 6498 <trans-unit id="1035838766454786107" datatype="html">
6502 <source>Audio-only</source> 6499 <source>Audio-only</source>
6503 <target state="new">Audio-only</target> 6500 <target state="new">Audio-only</target>
@@ -6547,6 +6544,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6547 <source>Auto (via ffmpeg)</source> 6544 <source>Auto (via ffmpeg)</source>
6548 <target state="new">Auto (via ffmpeg)</target> 6545 <target state="new">Auto (via ffmpeg)</target>
6549 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6546 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6547 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6548 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6549 <context-group purpose="location">
6550 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6551 <context context-type="linenumber">3</context>
6552 </context-group>
6550 </trans-unit> 6553 </trans-unit>
6551 <trans-unit id="931255636742351800" datatype="html"> 6554 <trans-unit id="931255636742351800" datatype="html">
6552 <source>No limit</source> 6555 <source>No limit</source>
@@ -6689,18 +6692,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
6689 <trans-unit id="2127446333083057097" datatype="html"> 6692 <trans-unit id="2127446333083057097" datatype="html">
6690 <source>Domain is required.</source> 6693 <source>Domain is required.</source>
6691 <target state="new">Domain is required.</target> 6694 <target state="new">Domain is required.</target>
6692 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6695
6693 </trans-unit> 6696 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6694 <trans-unit id="6780793142903080663" datatype="html"> 6697 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6695 <source>Domains entered are invalid.</source> 6698 <context-group purpose="location">
6696 <target state="new">Domains entered are invalid.</target> 6699 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6697 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6700 <context context-type="linenumber">93</context>
6698 </trans-unit> 6701 </context-group>
6699 <trans-unit id="5886492514458202177" datatype="html"> 6702 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6700 <source>Domains entered contain duplicates.</source> 6703 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6701 <target state="new">Domains entered contain duplicates.</target> 6704 <context-group purpose="location">
6702 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6705 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6706 <context context-type="linenumber">94</context>
6707 </context-group>
6708 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6709 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6710 <context-group purpose="location">
6711 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6712 <context context-type="linenumber">102</context>
6713 </context-group>
6714 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6715 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6716 <context-group purpose="location">
6717 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6718 <context context-type="linenumber">103</context>
6719 </context-group>
6703 </trans-unit> 6720 </trans-unit>
6721
6722
6704 <trans-unit id="240806681889331244" datatype="html"> 6723 <trans-unit id="240806681889331244" datatype="html">
6705 <source>Unlimited</source> 6724 <source>Unlimited</source>
6706 <target state="new">Unlimited</target> 6725 <target state="new">Unlimited</target>
@@ -6860,24 +6879,50 @@ channel with the same name (<x id="PH_2"/>)!</target>
6860 <x id="PH"/> removed from instance followers 6879 <x id="PH"/> removed from instance followers
6861 </target> 6880 </target>
6862 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6881 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6882 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6883 <source>Follow</source><target state="new">Follow</target>
6884 <context-group purpose="location">
6885 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6886 <context context-type="linenumber">3</context>
6887 </context-group>
6888 <context-group purpose="location">
6889 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6890 <context context-type="linenumber">37</context>
6891 </context-group>
6892 <context-group purpose="location">
6893 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6894 <context context-type="linenumber">18</context>
6895 </context-group>
6896 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6897 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6898 <context-group purpose="location">
6899 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6900 <context context-type="linenumber">11</context>
6901 </context-group>
6863 </trans-unit> 6902 </trans-unit>
6864 <trans-unit id="2740793005745065895" datatype="html"> 6903 <trans-unit id="2740793005745065895" datatype="html">
6865 <source><x id="PH"/> is not valid </source> 6904 <source><x id="PH"/> is not valid </source>
6866 <target state="new"> 6905 <target state="new">
6867 <x id="PH"/> is not valid 6906 <x id="PH"/> is not valid
6868 </target> 6907 </target>
6869 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6908
6870 </trans-unit> 6909 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6871 <trans-unit id="2355066641781598196" datatype="html"> 6910 <trans-unit id="2355066641781598196" datatype="html">
6872 <source>Follow request(s) sent!</source> 6911 <source>Follow request(s) sent!</source>
6873 <target state="new">Follow request(s) sent!</target> 6912 <target state="new">Follow request(s) sent!</target>
6874 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6913
6914 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6915 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6916 <context-group purpose="location">
6917 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6918 <context context-type="linenumber">3</context>
6919 </context-group>
6875 </trans-unit> 6920 </trans-unit>
6876 <trans-unit id="4245720728052819482" datatype="html"> 6921 <trans-unit id="4245720728052819482" datatype="html">
6877 <source>Do you really want to unfollow <x id="PH"/>?</source> 6922 <source>Do you really want to unfollow <x id="PH"/>?</source>
6878 <target state="translated">Справді відписатися від <x id="PH"/>?</target> 6923 <target state="translated">Справді відписатися від <x id="PH"/>?</target>
6879 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6924
6880 </trans-unit> 6925 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6881 <trans-unit id="9160510009013134726" datatype="html"> 6926 <trans-unit id="9160510009013134726" datatype="html">
6882 <source>Unfollow</source> 6927 <source>Unfollow</source>
6883 <target state="new">Unfollow</target> 6928 <target state="new">Unfollow</target>
@@ -6886,8 +6931,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
6886 <trans-unit id="3935234189109112926" datatype="html"> 6931 <trans-unit id="3935234189109112926" datatype="html">
6887 <source>You are not following <x id="PH"/> anymore.</source> 6932 <source>You are not following <x id="PH"/> anymore.</source>
6888 <target state="translated">Ви більше не стежите за <x id="PH"/>.</target> 6933 <target state="translated">Ви більше не стежите за <x id="PH"/>.</target>
6889 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6934
6890 </trans-unit> 6935 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6891 <trans-unit id="2593763089859685916" datatype="html"> 6936 <trans-unit id="2593763089859685916" datatype="html">
6892 <source>enabled</source> 6937 <source>enabled</source>
6893 <target state="new">enabled</target> 6938 <target state="new">enabled</target>
@@ -7345,9 +7390,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7345 <trans-unit id="1519954996184640001" datatype="html"> 7390 <trans-unit id="1519954996184640001" datatype="html">
7346 <source>Error</source> 7391 <source>Error</source>
7347 <target state="new">Error</target> 7392 <target state="new">Error</target>
7348 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7393
7349 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7394
7350 </trans-unit> 7395 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7351 <trans-unit id="5076187961693950167" datatype="html"> 7396 <trans-unit id="5076187961693950167" datatype="html">
7352 <source>Standard logs</source> 7397 <source>Standard logs</source>
7353 <target state="new">Standard logs</target> 7398 <target state="new">Standard logs</target>
@@ -7388,16 +7433,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7388 <target state="new">Update user password</target> 7433 <target state="new">Update user password</target>
7389 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7434 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7390 </trans-unit> 7435 </trans-unit>
7391 <trans-unit id="177544274549739411" datatype="html"> 7436
7392 <source>Following list</source> 7437
7393 <target state="new">Following list</target>
7394 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7395 </trans-unit>
7396 <trans-unit id="8092429110007204784" datatype="html">
7397 <source>Followers list</source>
7398 <target state="new">Followers list</target>
7399 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7400 </trans-unit>
7401 <trans-unit id="780323526182667308" datatype="html"> 7438 <trans-unit id="780323526182667308" datatype="html">
7402 <source>User <x id="PH"/> updated.</source> 7439 <source>User <x id="PH"/> updated.</source>
7403 <target state="translated">Користувача <x id="PH"/> онвлено.</target> 7440 <target state="translated">Користувача <x id="PH"/> онвлено.</target>
@@ -7435,16 +7472,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7435 <target state="new">Federation</target> 7472 <target state="new">Federation</target>
7436 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7473 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7437 </trans-unit> 7474 </trans-unit>
7438 <trans-unit id="4682675125751819107" datatype="html"> 7475
7439 <source>Instances you follow</source> 7476
7440 <target state="new">Instances you follow</target>
7441 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7442 </trans-unit>
7443 <trans-unit id="8899833753704589712" datatype="html">
7444 <source>Instances following you</source>
7445 <target state="new">Instances following you</target>
7446 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7447 </trans-unit>
7448 <trans-unit id="3767259920053407667" datatype="html"> 7477 <trans-unit id="3767259920053407667" datatype="html">
7449 <source>Videos will be deleted, comments will be tombstoned.</source> 7478 <source>Videos will be deleted, comments will be tombstoned.</source>
7450 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7479 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7475,7 +7504,25 @@ channel with the same name (<x id="PH_2"/>)!</target>
7475 <target state="new">Set Email as Verified</target> 7504 <target state="new">Set Email as Verified</target>
7476 7505
7477 7506
7478 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit> 7507 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group></trans-unit><trans-unit id="4207916966377787111" datatype="html">
7508 <source>Created</source><target state="new">Created</target>
7509 <context-group purpose="location">
7510 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7511 <context context-type="linenumber">115</context>
7512 </context-group>
7513 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7514 <source>Daily quota</source><target state="new">Daily quota</target>
7515 <context-group purpose="location">
7516 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7517 <context context-type="linenumber">120</context>
7518 </context-group>
7519 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7520 <source>Last login</source><target state="new">Last login</target>
7521 <context-group purpose="location">
7522 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7523 <context context-type="linenumber">122</context>
7524 </context-group>
7525 </trans-unit>
7479 <trans-unit id="3403978719736970622" datatype="html"> 7526 <trans-unit id="3403978719736970622" datatype="html">
7480 <source>You cannot ban root.</source> 7527 <source>You cannot ban root.</source>
7481 <target state="new">You cannot ban root.</target> 7528 <target state="new">You cannot ban root.</target>
@@ -7783,12 +7830,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
7783 <source>Video channel <x id="PH"/> created.</source> 7830 <source>Video channel <x id="PH"/> created.</source>
7784 <target state="translated">Відеоканал <x id="PH"/> створено.</target> 7831 <target state="translated">Відеоканал <x id="PH"/> створено.</target>
7785 7832
7786 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group></trans-unit> 7833 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7787 <trans-unit id="8723777130353305761" datatype="html"> 7834 <trans-unit id="8723777130353305761" datatype="html">
7788 <source>This name already exists on this instance.</source> 7835 <source>This name already exists on this instance.</source>
7789 <target state="new">This name already exists on this instance.</target> 7836 <target state="new">This name already exists on this instance.</target>
7790 7837
7791 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group></trans-unit> 7838 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7792 <trans-unit id="7589345916094713536" datatype="html"> 7839 <trans-unit id="7589345916094713536" datatype="html">
7793 <source>Video channel <x id="PH"/> updated.</source> 7840 <source>Video channel <x id="PH"/> updated.</source>
7794 <target state="translated">Відеоканал <x id="PH"/> оновлено.</target> 7841 <target state="translated">Відеоканал <x id="PH"/> оновлено.</target>
@@ -7803,13 +7850,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7803 <source>Banner deleted.</source><target state="new">Banner deleted.</target> 7850 <source>Banner deleted.</source><target state="new">Banner deleted.</target>
7804 7851
7805 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit> 7852 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group></trans-unit>
7806 <trans-unit id="2575302837003821736" datatype="html"> 7853
7807 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7808 <target state="new">Please type the display name of the video channel (
7809 <x id="PH"/>) to confirm
7810 </target>
7811
7812 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
7813 <trans-unit id="624066830180032195" datatype="html"> 7854 <trans-unit id="624066830180032195" datatype="html">
7814 <source>Video channel <x id="PH"/> deleted.</source> 7855 <source>Video channel <x id="PH"/> deleted.</source>
7815 <target state="translated">Відеоканал <x id="PH"/> видалено.</target> 7856 <target state="translated">Відеоканал <x id="PH"/> видалено.</target>
@@ -7954,6 +7995,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
7954 <source>Ownership change request sent.</source> 7995 <source>Ownership change request sent.</source>
7955 <target state="new">Ownership change request sent.</target> 7996 <target state="new">Ownership change request sent.</target>
7956 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 7997 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
7998 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
7999 <source>Sort by</source><target state="new">Sort by</target>
8000 <context-group purpose="location">
8001 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8002 <context context-type="linenumber">26</context>
8003 </context-group>
7957 </trans-unit> 8004 </trans-unit>
7958 <trans-unit id="3245220240937722814" datatype="html"> 8005 <trans-unit id="3245220240937722814" datatype="html">
7959 <source>My channels</source> 8006 <source>My channels</source>
@@ -8057,7 +8104,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8057 <target state="new">Subscribe to the account</target> 8104 <target state="new">Subscribe to the account</target>
8058 8105
8059 8106
8060 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html"> 8107 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit><trans-unit id="3131904093925601441" datatype="html">
8061 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target> 8108 <source>PLAYLISTS</source><target state="new">PLAYLISTS</target>
8062 <context-group purpose="location"> 8109 <context-group purpose="location">
8063 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 8110 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -8103,35 +8150,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8103 <trans-unit id="3779524668013120370" datatype="html"> 8150 <trans-unit id="3779524668013120370" datatype="html">
8104 <source>Go to my subscriptions</source> 8151 <source>Go to my subscriptions</source>
8105 <target state="new">Go to my subscriptions</target> 8152 <target state="new">Go to my subscriptions</target>
8106 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8153
8107 </trans-unit> 8154 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8108 <trans-unit id="1136469849928650779" datatype="html"> 8155 <trans-unit id="1136469849928650779" datatype="html">
8109 <source>Go to my videos</source> 8156 <source>Go to my videos</source>
8110 <target state="new">Go to my videos</target> 8157 <target state="new">Go to my videos</target>
8111 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8158
8112 </trans-unit> 8159 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8113 <trans-unit id="7836683738999600376" datatype="html"> 8160 <trans-unit id="7836683738999600376" datatype="html">
8114 <source>Go to my imports</source> 8161 <source>Go to my imports</source>
8115 <target state="new">Go to my imports</target> 8162 <target state="new">Go to my imports</target>
8116 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8163
8117 </trans-unit> 8164 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8118 <trans-unit id="7511292153332773503" datatype="html"> 8165 <trans-unit id="7511292153332773503" datatype="html">
8119 <source>Go to my channels</source> 8166 <source>Go to my channels</source>
8120 <target state="new">Go to my channels</target> 8167 <target state="new">Go to my channels</target>
8121 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8168
8122 </trans-unit> 8169 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8123 <trans-unit id="2013324644839511073" datatype="html"> 8170 <trans-unit id="2013324644839511073" datatype="html">
8124 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8171 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8125Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8172Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8126 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8173 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8127Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8174Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8128 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8175
8129 </trans-unit> 8176 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8130 <trans-unit id="375263728166936544" datatype="html"> 8177 <trans-unit id="375263728166936544" datatype="html">
8131 <source>You need to reconnect.</source> 8178 <source>You need to reconnect.</source>
8132 <target state="new">You need to reconnect.</target> 8179 <target state="new">You need to reconnect.</target>
8133 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8180
8134 </trans-unit> 8181 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8135 <trans-unit id="2206638022166154361" datatype="html"> 8182 <trans-unit id="2206638022166154361" datatype="html">
8136 <source>Keyboard Shortcuts:</source> 8183 <source>Keyboard Shortcuts:</source>
8137 <target state="new">Keyboard Shortcuts:</target> 8184 <target state="new">Keyboard Shortcuts:</target>
@@ -8142,6 +8189,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8142 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8189 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8143 <context context-type="linenumber">98</context> 8190 <context context-type="linenumber">98</context>
8144 </context-group> 8191 </context-group>
8192 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8193 <source>In my library</source><target state="new">In my library</target>
8194 <context-group purpose="location">
8195 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8196 <context context-type="linenumber">104</context>
8197 </context-group>
8145 </trans-unit><trans-unit id="232050922346936574" datatype="html"> 8198 </trans-unit><trans-unit id="232050922346936574" datatype="html">
8146 <source>Trending</source><target state="new">Trending</target> 8199 <source>Trending</source><target state="new">Trending</target>
8147 8200
@@ -8165,38 +8218,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8165 <source>Incorrect username or password.</source> 8218 <source>Incorrect username or password.</source>
8166 <target state="new">Incorrect username or password.</target> 8219 <target state="new">Incorrect username or password.</target>
8167 8220
8168 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group></trans-unit> 8221 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8169 <trans-unit id="6974874606619467663" datatype="html"> 8222 <trans-unit id="6974874606619467663" datatype="html">
8170 <source>Your account is blocked.</source> 8223 <source>Your account is blocked.</source>
8171 <target state="new">Your account is blocked.</target> 8224 <target state="new">Your account is blocked.</target>
8172 8225
8173 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group></trans-unit> 8226 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8174 <trans-unit id="7939914198003891823" datatype="html"> 8227 <trans-unit id="7939914198003891823" datatype="html">
8175 <source>any language</source> 8228 <source>any language</source>
8176 <target state="new">any language</target> 8229 <target state="new">any language</target>
8177 8230
8178 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group></trans-unit> 8231 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8179 8232
8180 <trans-unit id="5633144232269377096" datatype="html"> 8233 <trans-unit id="5633144232269377096" datatype="html">
8181 <source>hide</source> 8234 <source>hide</source>
8182 <target state="new">hide</target> 8235 <target state="new">hide</target>
8183 8236
8184 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group></trans-unit> 8237 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8185 <trans-unit id="8603861867909474404" datatype="html"> 8238 <trans-unit id="8603861867909474404" datatype="html">
8186 <source>blur</source> 8239 <source>blur</source>
8187 <target state="new">blur</target> 8240 <target state="new">blur</target>
8188 8241
8189 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group></trans-unit> 8242 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8190 <trans-unit id="4534458451100881847" datatype="html"> 8243 <trans-unit id="4534458451100881847" datatype="html">
8191 <source>display</source> 8244 <source>display</source>
8192 <target state="new">display</target> 8245 <target state="new">display</target>
8193 8246
8194 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group></trans-unit> 8247 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8195 <trans-unit id="4467323362722952678" datatype="html"> 8248 <trans-unit id="4467323362722952678" datatype="html">
8196 <source>Unknown</source> 8249 <source>Unknown</source>
8197 <target state="new">Unknown</target> 8250 <target state="new">Unknown</target>
8198 8251
8199 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 8252 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8200 <trans-unit id="8781423666414310853" datatype="html"> 8253 <trans-unit id="8781423666414310853" datatype="html">
8201 <source>Your password has been successfully reset!</source> 8254 <source>Your password has been successfully reset!</source>
8202 <target state="new">Your password has been successfully reset!</target> 8255 <target state="new">Your password has been successfully reset!</target>
@@ -9775,18 +9828,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9775 <trans-unit id="968295009933361070" datatype="html"> 9828 <trans-unit id="968295009933361070" datatype="html">
9776 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9829 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9777 <target state="translated">Забагато спроб, повторіть спробу через <x id="PH"/> хвилин.</target> 9830 <target state="translated">Забагато спроб, повторіть спробу через <x id="PH"/> хвилин.</target>
9778 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9831
9779 </trans-unit> 9832 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9780 <trans-unit id="4965472196059235310" datatype="html"> 9833 <trans-unit id="4965472196059235310" datatype="html">
9781 <source>Too many attempts, please try again later.</source> 9834 <source>Too many attempts, please try again later.</source>
9782 <target state="new">Too many attempts, please try again later.</target> 9835 <target state="new">Too many attempts, please try again later.</target>
9783 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9836
9784 </trans-unit> 9837 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9785 <trans-unit id="1693549688987384699" datatype="html"> 9838 <trans-unit id="1693549688987384699" datatype="html">
9786 <source>Server error. Please retry later.</source> 9839 <source>Server error. Please retry later.</source>
9787 <target state="new">Server error. Please retry later.</target> 9840 <target state="new">Server error. Please retry later.</target>
9788 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9841
9789 </trans-unit> 9842 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9790 <trans-unit id="5927402622550505067" datatype="html"> 9843 <trans-unit id="5927402622550505067" datatype="html">
9791 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9844 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9792 <target state="new">Subscribed to all current channels of 9845 <target state="new">Subscribed to all current channels of
@@ -10378,35 +10431,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10378 <source>Your video was uploaded to your account and is private.</source> 10431 <source>Your video was uploaded to your account and is private.</source>
10379 <target state="new">Your video was uploaded to your account and is private.</target> 10432 <target state="new">Your video was uploaded to your account and is private.</target>
10380 10433
10381 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit> 10434 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10382 <trans-unit id="5699822024600815733" datatype="html"> 10435 <trans-unit id="5699822024600815733" datatype="html">
10383 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10436 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10384 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target> 10437 <target state="new">But associated data (tags, description...) will be lost, are you sure you want to leave this page?</target>
10385 10438
10386 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit> 10439 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10387 <trans-unit id="1219739004043110649" datatype="html"> 10440 <trans-unit id="1219739004043110649" datatype="html">
10388 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10441 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10389 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target> 10442 <target state="new">Your video is not uploaded yet, are you sure you want to leave this page?</target>
10390 10443
10391 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group></trans-unit> 10444 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10392 <trans-unit id="6932865105766151309" datatype="html"> 10445 <trans-unit id="6932865105766151309" datatype="html">
10393 <source>Upload</source> 10446 <source>Upload</source>
10394 <target state="new">Upload</target> 10447 <target state="new">Upload</target>
10395 10448
10396 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group></trans-unit> 10449 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10397 <trans-unit id="8278735427925094503" datatype="html"> 10450 <trans-unit id="8278735427925094503" datatype="html">
10398 <source>Upload <x id="PH"/> </source> 10451 <source>Upload <x id="PH"/> </source>
10399 <target state="new">Upload 10452 <target state="new">Upload
10400 <x id="PH"/> 10453 <x id="PH"/>
10401 </target> 10454 </target>
10402 10455
10403 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group></trans-unit> 10456 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10404 10457
10405 <trans-unit id="5981816353437801748" datatype="html"> 10458 <trans-unit id="5981816353437801748" datatype="html">
10406 <source>Video published.</source> 10459 <source>Video published.</source>
10407 <target state="new">Video published.</target> 10460 <target state="new">Video published.</target>
10408 10461
10409 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group></trans-unit> 10462 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10410 10463
10411 10464
10412 <trans-unit id="764164089183618119" datatype="html"> 10465 <trans-unit id="764164089183618119" datatype="html">
@@ -10456,27 +10509,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10456 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10509 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10457 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10510 <target state="new">This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10458 10511
10459 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10512 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10460 <trans-unit id="5761611056224181752" datatype="html"> 10513 <trans-unit id="5761611056224181752" datatype="html">
10461 <source>Redirection</source> 10514 <source>Redirection</source>
10462 <target state="new">Redirection</target> 10515 <target state="new">Redirection</target>
10463 10516
10464 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10517 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10465 <trans-unit id="8858527736400081688" datatype="html"> 10518 <trans-unit id="8858527736400081688" datatype="html">
10466 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10519 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10467 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target> 10520 <target state="new">This video contains mature or explicit content. Are you sure you want to watch it?</target>
10468 10521
10469 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10522 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10470 <trans-unit id="3937119019020041049" datatype="html"> 10523 <trans-unit id="3937119019020041049" datatype="html">
10471 <source>Mature or explicit content</source> 10524 <source>Mature or explicit content</source>
10472 <target state="new">Mature or explicit content</target> 10525 <target state="new">Mature or explicit content</target>
10473 10526
10474 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10527 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10475 <trans-unit id="1755474755114288376" datatype="html"> 10528 <trans-unit id="1755474755114288376" datatype="html">
10476 <source>Up Next</source> 10529 <source>Up Next</source>
10477 <target state="new">Up Next</target> 10530 <target state="new">Up Next</target>
10478 10531
10479 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10532 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10480 <trans-unit id="2159130950882492111" datatype="html"> 10533 <trans-unit id="2159130950882492111" datatype="html">
10481 <source>Cancel</source> 10534 <source>Cancel</source>
10482 <target state="translated">Скасувати</target> 10535 <target state="translated">Скасувати</target>
@@ -10486,62 +10539,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10486 <source>Autoplay is suspended</source> 10539 <source>Autoplay is suspended</source>
10487 <target state="translated">Автовідтворення зупинено</target> 10540 <target state="translated">Автовідтворення зупинено</target>
10488 10541
10489 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10542 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10490 <trans-unit id="7895294730547405228" datatype="html"> 10543 <trans-unit id="7895294730547405228" datatype="html">
10491 <source>Enter/exit fullscreen (requires player focus)</source> 10544 <source>Enter/exit fullscreen (requires player focus)</source>
10492 <target state="translated">Вхід/вихід до повноекранного режиму (фокус повинен бути на програвачі)</target> 10545 <target state="translated">Вхід/вихід до повноекранного режиму (фокус повинен бути на програвачі)</target>
10493 10546
10494 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10547 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10495 <trans-unit id="7618388257165864759" datatype="html"> 10548 <trans-unit id="7618388257165864759" datatype="html">
10496 <source>Play/Pause the video (requires player focus)</source> 10549 <source>Play/Pause the video (requires player focus)</source>
10497 <target state="translated">Відтворення/пауза відео (фокус повинен бути на програвачі)</target> 10550 <target state="translated">Відтворення/пауза відео (фокус повинен бути на програвачі)</target>
10498 10551
10499 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10552 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10500 <trans-unit id="7761890399634216630" datatype="html"> 10553 <trans-unit id="7761890399634216630" datatype="html">
10501 <source>Mute/unmute the video (requires player focus)</source> 10554 <source>Mute/unmute the video (requires player focus)</source>
10502 <target state="translated">Увімкнути/вимкнути звук відео (фокус повинен бути на програвачі)</target> 10555 <target state="translated">Увімкнути/вимкнути звук відео (фокус повинен бути на програвачі)</target>
10503 10556
10504 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10557 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10505 <trans-unit id="5996585232248234904" datatype="html"> 10558 <trans-unit id="5996585232248234904" datatype="html">
10506 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10559 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10507 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target> 10560 <target state="new">Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</target>
10508 10561
10509 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10562 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10510 <trans-unit id="3748765405903319998" datatype="html"> 10563 <trans-unit id="3748765405903319998" datatype="html">
10511 <source>Increase the volume (requires player focus)</source> 10564 <source>Increase the volume (requires player focus)</source>
10512 <target state="new">Increase the volume (requires player focus)</target> 10565 <target state="new">Increase the volume (requires player focus)</target>
10513 10566
10514 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10567 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10515 <trans-unit id="5810704036407159982" datatype="html"> 10568 <trans-unit id="5810704036407159982" datatype="html">
10516 <source>Decrease the volume (requires player focus)</source> 10569 <source>Decrease the volume (requires player focus)</source>
10517 <target state="new">Decrease the volume (requires player focus)</target> 10570 <target state="new">Decrease the volume (requires player focus)</target>
10518 10571
10519 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10572 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10520 <trans-unit id="2622048822548065691" datatype="html"> 10573 <trans-unit id="2622048822548065691" datatype="html">
10521 <source>Seek the video forward (requires player focus)</source> 10574 <source>Seek the video forward (requires player focus)</source>
10522 <target state="new">Seek the video forward (requires player focus)</target> 10575 <target state="new">Seek the video forward (requires player focus)</target>
10523 10576
10524 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10577 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10525 <trans-unit id="6540078205109221153" datatype="html"> 10578 <trans-unit id="6540078205109221153" datatype="html">
10526 <source>Seek the video backward (requires player focus)</source> 10579 <source>Seek the video backward (requires player focus)</source>
10527 <target state="new">Seek the video backward (requires player focus)</target> 10580 <target state="new">Seek the video backward (requires player focus)</target>
10528 10581
10529 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10582 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10530 <trans-unit id="1956491957766210808" datatype="html"> 10583 <trans-unit id="1956491957766210808" datatype="html">
10531 <source>Increase playback rate (requires player focus)</source> 10584 <source>Increase playback rate (requires player focus)</source>
10532 <target state="new">Increase playback rate (requires player focus)</target> 10585 <target state="new">Increase playback rate (requires player focus)</target>
10533 10586
10534 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10587 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10535 <trans-unit id="5495529997674803186" datatype="html"> 10588 <trans-unit id="5495529997674803186" datatype="html">
10536 <source>Decrease playback rate (requires player focus)</source> 10589 <source>Decrease playback rate (requires player focus)</source>
10537 <target state="new">Decrease playback rate (requires player focus)</target> 10590 <target state="new">Decrease playback rate (requires player focus)</target>
10538 10591
10539 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10592 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10540 <trans-unit id="3178343147230721210" datatype="html"> 10593 <trans-unit id="3178343147230721210" datatype="html">
10541 <source>Navigate in the video frame by frame (requires player focus)</source> 10594 <source>Navigate in the video frame by frame (requires player focus)</source>
10542 <target state="new">Navigate in the video frame by frame (requires player focus)</target> 10595 <target state="new">Navigate in the video frame by frame (requires player focus)</target>
10543 10596
10544 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10597 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10545 <trans-unit id="8025996572234182184" datatype="html"> 10598 <trans-unit id="8025996572234182184" datatype="html">
10546 <source>Like the video</source> 10599 <source>Like the video</source>
10547 <target state="new">Like the video</target> 10600 <target state="new">Like the video</target>
diff --git a/client/src/locale/angular.vi-VN.xlf b/client/src/locale/angular.vi-VN.xlf
index 301570458..4736e3107 100644
--- a/client/src/locale/angular.vi-VN.xlf
+++ b/client/src/locale/angular.vi-VN.xlf
@@ -160,19 +160,19 @@
160 <trans-unit id="187187500641108332" datatype="html"> 160 <trans-unit id="187187500641108332" datatype="html">
161 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source> 161 <source><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </source>
162 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target> 162 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action.label }}"/> </target>
163 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group> 163
164 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group> 164
165 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group> 165
166 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group> 166
167 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group> 167
168 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 168
169 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group> 169
170 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group> 170
171 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group> 171
172 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group> 172
173 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group> 173
174 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group> 174
175 </trans-unit> 175 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
176 <trans-unit id="1486537403020619891" datatype="html"> 176 <trans-unit id="1486537403020619891" datatype="html">
177 <source>My watch history</source> 177 <source>My watch history</source>
178 <target state="translated">Lịch sử xem</target> 178 <target state="translated">Lịch sử xem</target>
@@ -241,22 +241,22 @@
241 <trans-unit id="5674286808255988565"> 241 <trans-unit id="5674286808255988565">
242 <source>Create</source> 242 <source>Create</source>
243 <target>Tạo</target> 243 <target>Tạo</target>
244 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 244
245 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 245
246 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 246
247 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 247
248 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 248
249 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 249
250 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 250
251 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 251
252 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 252
253 </trans-unit> 253 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
254 <trans-unit id="1006562256968398209" datatype="html"> 254 <trans-unit id="1006562256968398209" datatype="html">
255 <source>video</source> 255 <source>video</source>
256 <target state="translated">video</target> 256 <target state="translated">video</target>
257 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group> 257
258 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group> 258
259 </trans-unit> 259 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
260 <trans-unit id="6438815964972582865" datatype="html"> 260 <trans-unit id="6438815964972582865" datatype="html">
261 <source>The following link contains a private token and should not be shared with anyone.</source> 261 <source>The following link contains a private token and should not be shared with anyone.</source>
262 <target state="translated">Đường dẫn chứa một token riêng tư và không nên chia sẻ với bất cứ ai.</target> 262 <target state="translated">Đường dẫn chứa một token riêng tư và không nên chia sẻ với bất cứ ai.</target>
@@ -326,13 +326,13 @@
326 <trans-unit id="6995024616159044376" datatype="html"> 326 <trans-unit id="6995024616159044376" datatype="html">
327 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 327 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
328 <target state="translated">Bạn đã dùng hết dung lượng cho phép với video này (dung lượng video: <x id="PH" equiv-text="videoSizeBytes"/>, đã dùng: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, dung lượng cho phép: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 328 <target state="translated">Bạn đã dùng hết dung lượng cho phép với video này (dung lượng video: <x id="PH" equiv-text="videoSizeBytes"/>, đã dùng: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, dung lượng cho phép: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
329 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 329
330 </trans-unit> 330 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
331 <trans-unit id="7873395933409147217" datatype="html"> 331 <trans-unit id="7873395933409147217" datatype="html">
332 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 332 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
333 <target state="translated">Bạn đã dùng hết dung lượng hàng ngày cho phép với video này (dung lượng video: <x id="PH" equiv-text="videoSizeBytes"/>, đã dùng: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, dung lượng cho phép: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 333 <target state="translated">Bạn đã dùng hết dung lượng hàng ngày cho phép với video này (dung lượng video: <x id="PH" equiv-text="videoSizeBytes"/>, đã dùng: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, dung lượng cho phép: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
334 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 334
335 </trans-unit> 335 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
336 <trans-unit id="5235042777215655908" datatype="html"> 336 <trans-unit id="5235042777215655908" datatype="html">
337 <source>subtitles</source> 337 <source>subtitles</source>
338 <target state="translated">phụ đề</target> 338 <target state="translated">phụ đề</target>
@@ -774,10 +774,10 @@
774 <trans-unit id="2602586221576511475"> 774 <trans-unit id="2602586221576511475">
775 <source>Video quota</source> 775 <source>Video quota</source>
776 <target>Dung lượng</target> 776 <target>Dung lượng</target>
777 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 777
778 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 778
779 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 779
780 </trans-unit> 780 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
781 <trans-unit id="1502595455339510144" datatype="html"> 781 <trans-unit id="1502595455339510144" datatype="html">
782 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 782 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
783 <target state="translated">Không giới hạn <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> mỗi ngày)<x id="CLOSE_TAG_NG_CONTAINER"/></target> 783 <target state="translated">Không giới hạn <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> mỗi ngày)<x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -857,6 +857,30 @@
857 <target state="translated">Liên hợp</target> 857 <target state="translated">Liên hợp</target>
858 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 858 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
859 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 859 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
860 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
861 <source>Following</source><target state="new">Following</target>
862 <context-group purpose="location">
863 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
864 <context context-type="linenumber">29</context>
865 </context-group>
866 <context-group purpose="location">
867 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
868 <context context-type="linenumber">31</context>
869 </context-group>
870 <context-group purpose="location">
871 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
872 <context context-type="linenumber">28</context>
873 </context-group>
874 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
875 <source>Followers</source><target state="new">Followers</target>
876 <context-group purpose="location">
877 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
878 <context context-type="linenumber">34</context>
879 </context-group>
880 <context-group purpose="location">
881 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
882 <context context-type="linenumber">37</context>
883 </context-group>
860 </trans-unit> 884 </trans-unit>
861 <trans-unit id="3541687134897970106" datatype="html"> 885 <trans-unit id="3541687134897970106" datatype="html">
862 <source>followers</source> 886 <source>followers</source>
@@ -920,25 +944,25 @@
920 <trans-unit id="2159130950882492111"> 944 <trans-unit id="2159130950882492111">
921 <source>Cancel</source> 945 <source>Cancel</source>
922 <target>Huỷ</target> 946 <target>Huỷ</target>
923 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group> 947
924 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group> 948
925 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group> 949
926 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group> 950
927 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group> 951
928 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group> 952
929 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group> 953
930 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group> 954
931 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group> 955
932 <context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group> 956
933 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group> 957
934 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group> 958
935 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 959
936 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 960
937 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group> 961
938 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group> 962
939 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group> 963
940 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group> 964
941 </trans-unit> 965 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
942 <trans-unit id="3616223838716839702"> 966 <trans-unit id="3616223838716839702">
943 <source>Ban this user</source> 967 <source>Ban this user</source>
944 <target>Chặn người dùng này</target> 968 <target>Chặn người dùng này</target>
@@ -1004,19 +1028,13 @@
1004 <trans-unit id="7252854992688790751" datatype="html"> 1028 <trans-unit id="7252854992688790751" datatype="html">
1005 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1029 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1006 <target state="translated">Máy chủ này cho phép đăng ký. Tuy nhiên, hãy cẩn thận đọc kỹ <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Điều khoản dịch vụ<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Điều khoản dịch vụ<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> trước khi tạo tài khoản. Bạn cũng có thể tham khảo thêm một số máy chủ khác tại: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1030 <target state="translated">Máy chủ này cho phép đăng ký. Tuy nhiên, hãy cẩn thận đọc kỹ <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Điều khoản dịch vụ<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Điều khoản dịch vụ<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> trước khi tạo tài khoản. Bạn cũng có thể tham khảo thêm một số máy chủ khác tại: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1007 <context-group purpose="location"> 1031
1008 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1032 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1009 <context context-type="linenumber">60,62</context>
1010 </context-group>
1011 </trans-unit>
1012 <trans-unit id="7215649348148521605" datatype="html"> 1033 <trans-unit id="7215649348148521605" datatype="html">
1013 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1034 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1014 <target state="translated">Máy chủ này đã tắt đăng ký, bạn hãy đọc <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Điều khoản dịch vụ<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> để tìm hiểu thêm hoặc tìm một máy chủ khác cho phép bạn tạo tài khoản và đăng video. Danh sách những máy chủ khác: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1035 <target state="translated">Máy chủ này đã tắt đăng ký, bạn hãy đọc <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Điều khoản dịch vụ<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> để tìm hiểu thêm hoặc tìm một máy chủ khác cho phép bạn tạo tài khoản và đăng video. Danh sách những máy chủ khác: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1015 <context-group purpose="location"> 1036
1016 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1037 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1017 <context context-type="linenumber">65,67</context>
1018 </context-group>
1019 </trans-unit>
1020 <trans-unit id="2392488717875840729"> 1038 <trans-unit id="2392488717875840729">
1021 <source>User</source> 1039 <source>User</source>
1022 <target>Tài khoản</target> 1040 <target>Tài khoản</target>
@@ -1027,67 +1045,67 @@
1027 <source>Username or email address</source> 1045 <source>Username or email address</source>
1028 <target>Tên người dùng hoặc địa chỉ email</target> 1046 <target>Tên người dùng hoặc địa chỉ email</target>
1029 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1047 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1048 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1049 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1050 <context-group purpose="location">
1051 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1052 <context context-type="linenumber">33,34</context>
1053 </context-group>
1030 </trans-unit> 1054 </trans-unit>
1031 <trans-unit id="1431416938026210429"> 1055 <trans-unit id="1431416938026210429">
1032 <source>Password</source> 1056 <source>Password</source>
1033 <target>Mật khẩu</target> 1057 <target>Mật khẩu</target>
1034 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1058
1035 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1059
1036 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1060
1037 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1061
1038 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1062
1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1063
1040 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1064
1041 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1065
1042 </trans-unit> 1066 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1043 <trans-unit id="8715156686857791956" datatype="html"> 1067 <trans-unit id="8715156686857791956" datatype="html">
1044 <source>Click here to reset your password</source> 1068 <source>Click here to reset your password</source>
1045 <target state="translated">Click vào đây để reset mật khẩu</target> 1069 <target state="translated">Click vào đây để reset mật khẩu</target>
1046 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1070
1047 </trans-unit> 1071 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1048 <trans-unit id="892063502898494584" datatype="html"> 1072 <trans-unit id="892063502898494584" datatype="html">
1049 <source>I forgot my password</source> 1073 <source>I forgot my password</source>
1050 <target state="translated">Quên mật khẩu</target> 1074 <target state="translated">Quên mật khẩu</target>
1051 <context-group purpose="location"> 1075
1052 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1076 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1053 <context context-type="linenumber">47</context>
1054 </context-group>
1055 </trans-unit>
1056 <trans-unit id="2101170466365500913" datatype="html"> 1077 <trans-unit id="2101170466365500913" datatype="html">
1057 <source>Logging into an account lets you publish content</source> 1078 <source>Logging into an account lets you publish content</source>
1058 <target state="translated">Chỉ có thể đăng video sau khi đăng nhập</target> 1079 <target state="translated">Chỉ có thể đăng video sau khi đăng nhập</target>
1059 <context-group purpose="location"> 1080
1060 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1081 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1061 <context context-type="linenumber">56,57</context>
1062 </context-group>
1063 </trans-unit>
1064 <trans-unit id="2454050363478003966"> 1082 <trans-unit id="2454050363478003966">
1065 <source>Login</source> 1083 <source>Login</source>
1066 <target>Đăng nhập</target> 1084 <target>Đăng nhập</target>
1067 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1085
1068 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1086
1069 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1087
1070 </trans-unit> 1088 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1071 <trans-unit id="3183213940445113677" datatype="html"> 1089 <trans-unit id="3183213940445113677" datatype="html">
1072 <source>Or sign in with</source> 1090 <source>Or sign in with</source>
1073 <target state="translated">Hoặc đăng nhập bằng</target> 1091 <target state="translated">Hoặc đăng nhập bằng</target>
1074 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1092
1075 </trans-unit> 1093 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1076 <trans-unit id="3238209155172574367"> 1094 <trans-unit id="3238209155172574367">
1077 <source>Forgot your password</source> 1095 <source>Forgot your password</source>
1078 <target>Quên mật khẩu</target> 1096 <target>Quên mật khẩu</target>
1079 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1097
1080 </trans-unit> 1098 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1081 <trans-unit id="87327320394367488" datatype="html"> 1099 <trans-unit id="87327320394367488" datatype="html">
1082 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1100 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1083 <target state="translated">Rất tiếc, bạn không thể reset mật khẩu bởi vì quản trị viên máy chủ không thiết lập hệ thống email PeerTube.</target> 1101 <target state="translated">Rất tiếc, bạn không thể reset mật khẩu bởi vì quản trị viên máy chủ không thiết lập hệ thống email PeerTube.</target>
1084 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1102
1085 </trans-unit> 1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1086 <trans-unit id="3188014010833256853" datatype="html"> 1104 <trans-unit id="3188014010833256853" datatype="html">
1087 <source>Enter your email address and we will send you a link to reset your password.</source> 1105 <source>Enter your email address and we will send you a link to reset your password.</source>
1088 <target state="translated">Nhập email của bạn và chúng tôi sẽ gửi một đường link reset mật khẩu.</target> 1106 <target state="translated">Nhập email của bạn và chúng tôi sẽ gửi một đường link reset mật khẩu.</target>
1089 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1107
1090 </trans-unit> 1108 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1091 <trans-unit id="1190256911880544559" datatype="html"> 1109 <trans-unit id="1190256911880544559" datatype="html">
1092 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1110 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1093The link will expire within 1 hour.</source> 1111The link will expire within 1 hour.</source>
@@ -1097,26 +1115,26 @@ The link will expire within 1 hour.</source>
1097 <trans-unit id="4768749765465246664"> 1115 <trans-unit id="4768749765465246664">
1098 <source>Email</source> 1116 <source>Email</source>
1099 <target>Thư điện tử</target> 1117 <target>Thư điện tử</target>
1100 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1118
1101 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1119
1102 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1120
1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1121
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1122
1105 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1123
1106 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1124
1107 </trans-unit> 1125 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1108 <trans-unit id="3967269098753656610"> 1126 <trans-unit id="3967269098753656610">
1109 <source>Email address</source> 1127 <source>Email address</source>
1110 <target>Địa chỉ thư điện tử</target> 1128 <target>Địa chỉ thư điện tử</target>
1111 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1129
1112 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1130
1113 </trans-unit> 1131 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1114 <trans-unit id="7808756054397155068" datatype="html"> 1132 <trans-unit id="7808756054397155068" datatype="html">
1115 <source>Reset</source> 1133 <source>Reset</source>
1116 <target state="translated">Reset</target> 1134 <target state="translated">Reset</target>
1117 <note priority="1" from="description">Password reset button</note> 1135 <note priority="1" from="description">Password reset button</note>
1118 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1136
1119 </trans-unit> 1137 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1120 <trans-unit id="4319634264526091601" datatype="html"> 1138 <trans-unit id="4319634264526091601" datatype="html">
1121 <source>on this instance</source> 1139 <source>on this instance</source>
1122 <target state="translated">trên máy chủ này</target> 1140 <target state="translated">trên máy chủ này</target>
@@ -1452,9 +1470,9 @@ The link will expire within 1 hour.</source>
1452 <trans-unit id="2308975396733519902"> 1470 <trans-unit id="2308975396733519902">
1453 <source>Create an account</source> 1471 <source>Create an account</source>
1454 <target>Tạo tài khoản</target> 1472 <target>Tạo tài khoản</target>
1455 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1473
1456 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1474
1457 </trans-unit> 1475 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1458 <trans-unit id="3058024914967508975" datatype="html"> 1476 <trans-unit id="3058024914967508975" datatype="html">
1459 <source>My videos</source> 1477 <source>My videos</source>
1460 <target state="translated">Video của tôi</target> 1478 <target state="translated">Video của tôi</target>
@@ -1521,10 +1539,10 @@ The link will expire within 1 hour.</source>
1521 <trans-unit id="1504521795586863905" datatype="html"> 1539 <trans-unit id="1504521795586863905" datatype="html">
1522 <source>VIDEOS</source> 1540 <source>VIDEOS</source>
1523 <target state="translated">VIDEO</target> 1541 <target state="translated">VIDEO</target>
1524 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1542
1525 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1543
1526 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1544
1527 </trans-unit> 1545 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1528 <trans-unit id="667372110624203230" datatype="html"> 1546 <trans-unit id="667372110624203230" datatype="html">
1529 <source>Import jobs concurrency</source> 1547 <source>Import jobs concurrency</source>
1530 <target state="translated">Nhập công việc đồng thời</target> 1548 <target state="translated">Nhập công việc đồng thời</target>
@@ -1603,8 +1621,8 @@ The link will expire within 1 hour.</source>
1603 <trans-unit id="4424964105331349857" datatype="html"> 1621 <trans-unit id="4424964105331349857" datatype="html">
1604 <source>I'm a teapot</source> 1622 <source>I'm a teapot</source>
1605 <target state="translated">Tôi là ấm trà</target> 1623 <target state="translated">Tôi là ấm trà</target>
1606 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1624
1607 </trans-unit> 1625 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1608 <trans-unit id="1597262876035959248" datatype="html"> 1626 <trans-unit id="1597262876035959248" datatype="html">
1609 <source>That's an error.</source> 1627 <source>That's an error.</source>
1610 <target state="translated">Đây là lỗi.</target> 1628 <target state="translated">Đây là lỗi.</target>
@@ -1697,8 +1715,8 @@ The link will expire within 1 hour.</source>
1697 <trans-unit id="2971365540217107489" datatype="html"> 1715 <trans-unit id="2971365540217107489" datatype="html">
1698 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1716 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1699 <target state="translated">Video có dung lượng quá lớn. Hãy liên hệ quản trị viên nếu bạn muốn tăng giới hạn dung lượng.</target> 1717 <target state="translated">Video có dung lượng quá lớn. Hãy liên hệ quản trị viên nếu bạn muốn tăng giới hạn dung lượng.</target>
1700 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1718
1701 </trans-unit> 1719 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1702 <trans-unit id="5131854469652959713" datatype="html"> 1720 <trans-unit id="5131854469652959713" datatype="html">
1703 <source>GLOBAL SEARCH</source> 1721 <source>GLOBAL SEARCH</source>
1704 <target state="translated">TÌM KIẾM TOÀN CẦU</target> 1722 <target state="translated">TÌM KIẾM TOÀN CẦU</target>
@@ -2438,8 +2456,8 @@ The link will expire within 1 hour.</source>
2438 <trans-unit id="6161604372916832458" datatype="html"> 2456 <trans-unit id="6161604372916832458" datatype="html">
2439 <source>Upload on hold</source> 2457 <source>Upload on hold</source>
2440 <target state="translated">Đang tiếp tục tải lên</target> 2458 <target state="translated">Đang tiếp tục tải lên</target>
2441 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2459
2442 </trans-unit> 2460 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2443 <trans-unit id="285180972645018518" datatype="html"> 2461 <trans-unit id="285180972645018518" datatype="html">
2444 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2462 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2445 <target state="translated">Xin lỗi, tài khoản của bạn đã bị cấm tải lên. Nếu bạn muốn đăng thêm video, bạn phải liên hệ một quản trị viên để mở khóa dung lượng cho phép.</target> 2463 <target state="translated">Xin lỗi, tài khoản của bạn đã bị cấm tải lên. Nếu bạn muốn đăng thêm video, bạn phải liên hệ một quản trị viên để mở khóa dung lượng cho phép.</target>
@@ -3115,11 +3133,7 @@ The link will expire within 1 hour.</source>
3115 <target>Mã</target> 3133 <target>Mã</target>
3116 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3134 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3117 </trans-unit> 3135 </trans-unit>
3118 <trans-unit id="2265605798180116441" datatype="html"> 3136
3119 <source>Follower handle</source>
3120 <target state="translated">Người theo dõi handle</target>
3121 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3122 </trans-unit>
3123 <trans-unit id="5911214550882917183"> 3137 <trans-unit id="5911214550882917183">
3124 <source>State</source> 3138 <source>State</source>
3125 <target>Trạng thái</target> 3139 <target>Trạng thái</target>
@@ -3185,11 +3199,7 @@ The link will expire within 1 hour.</source>
3185 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target> 3199 <target state="translated"><x id="INTERPOLATION" equiv-text="{{ action }}"/> </target>
3186 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3200 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3187 </trans-unit> 3201 </trans-unit>
3188 <trans-unit id="6641024648411549335" datatype="html"> 3202
3189 <source>Host</source>
3190 <target state="translated">Host</target>
3191 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3192 </trans-unit>
3193 <trans-unit id="6571718060636962350" datatype="html"> 3203 <trans-unit id="6571718060636962350" datatype="html">
3194 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3204 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3195 <target state="translated">Đã cho phép dư thừa <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 3205 <target state="translated">Đã cho phép dư thừa <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -3198,9 +3208,9 @@ The link will expire within 1 hour.</source>
3198 <trans-unit id="9160510009013134726" datatype="html"> 3208 <trans-unit id="9160510009013134726" datatype="html">
3199 <source>Unfollow</source> 3209 <source>Unfollow</source>
3200 <target state="translated">Ngưng theo dõi</target> 3210 <target state="translated">Ngưng theo dõi</target>
3201 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3211
3202 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3212
3203 </trans-unit> 3213 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3204 <trans-unit id="8246779176913476983" datatype="html"> 3214 <trans-unit id="8246779176913476983" datatype="html">
3205 <source>Open instance in a new tab</source> 3215 <source>Open instance in a new tab</source>
3206 <target state="translated">Mở máy chủ trong tab mới</target> 3216 <target state="translated">Mở máy chủ trong tab mới</target>
@@ -3211,28 +3221,20 @@ The link will expire within 1 hour.</source>
3211 <trans-unit id="9132918641931433659" datatype="html"> 3221 <trans-unit id="9132918641931433659" datatype="html">
3212 <source>No host found matching current filters.</source> 3222 <source>No host found matching current filters.</source>
3213 <target state="translated">Không tìm thấy host trùng khớp với bộ lọc.</target> 3223 <target state="translated">Không tìm thấy host trùng khớp với bộ lọc.</target>
3214 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3224
3215 </trans-unit> 3225 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3216 <trans-unit id="7274241885665071790" datatype="html"> 3226 <trans-unit id="7274241885665071790" datatype="html">
3217 <source>Your instance is not following anyone.</source> 3227 <source>Your instance is not following anyone.</source>
3218 <target state="translated">Máy chủ của bạn không theo dõi bất kỳ ai.</target> 3228 <target state="translated">Máy chủ của bạn không theo dõi bất kỳ ai.</target>
3219 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3229
3220 </trans-unit> 3230 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3221 <trans-unit id="4774348799569692380" datatype="html"> 3231 <trans-unit id="4774348799569692380" datatype="html">
3222 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3232 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3223 <target state="translated">Đang hiện <x id="INTERPOLATION"/> tới <x id="INTERPOLATION_1"/> của <x id="INTERPOLATION_2"/> hosts</target> 3233 <target state="translated">Đang hiện <x id="INTERPOLATION"/> tới <x id="INTERPOLATION_1"/> của <x id="INTERPOLATION_2"/> hosts</target>
3224 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3234 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3225 </trans-unit> 3235 </trans-unit>
3226 <trans-unit id="6275803119759621687" datatype="html"> 3236
3227 <source>Follow domains</source> 3237
3228 <target state="translated">Theo dõi tên miền</target>
3229 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3230 </trans-unit>
3231 <trans-unit id="1268699198448750610" datatype="html">
3232 <source>Follow instances</source>
3233 <target state="translated">Theo dõi những máy chủ</target>
3234 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3235 </trans-unit>
3236 <trans-unit id="9216117865911519658" datatype="html"> 3238 <trans-unit id="9216117865911519658" datatype="html">
3237 <source>Action</source> 3239 <source>Action</source>
3238 <target state="translated">Hành động</target> 3240 <target state="translated">Hành động</target>
@@ -3282,11 +3284,11 @@ The link will expire within 1 hour.</source>
3282 <trans-unit id="5248717555542428023"> 3284 <trans-unit id="5248717555542428023">
3283 <source>Username</source> 3285 <source>Username</source>
3284 <target>Tên đăng nhập</target> 3286 <target>Tên đăng nhập</target>
3285 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3287
3286 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3288
3287 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3289
3288 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3290
3289 </trans-unit> 3291 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3290 <trans-unit id="5428411040014095392" datatype="html"> 3292 <trans-unit id="5428411040014095392" datatype="html">
3291 <source>e.g. jane_doe</source> 3293 <source>e.g. jane_doe</source>
3292 <target state="translated">e.g. jane_doe</target> 3294 <target state="translated">e.g. jane_doe</target>
@@ -3314,9 +3316,9 @@ The link will expire within 1 hour.</source>
3314 <trans-unit id="4145496584631696119"> 3316 <trans-unit id="4145496584631696119">
3315 <source>Role</source> 3317 <source>Role</source>
3316 <target>Vai trò</target> 3318 <target>Vai trò</target>
3317 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3319
3318 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3320
3319 </trans-unit> 3321 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3320 <trans-unit id="7046347992315328430" datatype="html"> 3322 <trans-unit id="7046347992315328430" datatype="html">
3321 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3323 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3322 <target state="translated">Đã bật chuyển đổi độ phân giải. Giới hạn dung lượng chỉ áp dụng vào tài khoản <x id="START_TAG_STRONG"/>gốc<x id="CLOSE_TAG_STRONG"/> thước video. <x id="LINE_BREAK"/> Dù vậy, tài khoản vẫn có thể upload ~ <x id="INTERPOLATION"/>. </target> 3324 <target state="translated">Đã bật chuyển đổi độ phân giải. Giới hạn dung lượng chỉ áp dụng vào tài khoản <x id="START_TAG_STRONG"/>gốc<x id="CLOSE_TAG_STRONG"/> thước video. <x id="LINE_BREAK"/> Dù vậy, tài khoản vẫn có thể upload ~ <x id="INTERPOLATION"/>. </target>
@@ -3333,15 +3335,9 @@ The link will expire within 1 hour.</source>
3333 <trans-unit id="2622255144026150901" datatype="html"> 3335 <trans-unit id="2622255144026150901" datatype="html">
3334 <source>Auth plugin</source> 3336 <source>Auth plugin</source>
3335 <target state="translated">Plugin cho phép</target> 3337 <target state="translated">Plugin cho phép</target>
3336 <context-group purpose="location"> 3338
3337 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3339
3338 <context context-type="linenumber">188</context> 3340 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3339 </context-group>
3340 <context-group purpose="location">
3341 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3342 <context context-type="linenumber">188</context>
3343 </context-group>
3344 </trans-unit>
3345 <trans-unit id="588099657508661970" datatype="html"> 3341 <trans-unit id="588099657508661970" datatype="html">
3346 <source>None (local authentication)</source> 3342 <source>None (local authentication)</source>
3347 <target state="translated">Không (xác thực cục bộ)</target> 3343 <target state="translated">Không (xác thực cục bộ)</target>
@@ -3595,6 +3591,12 @@ The link will expire within 1 hour.</source>
3595 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3591 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3596 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3592 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3597 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3593 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3594 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3595 <source>Follower</source><target state="new">Follower</target>
3596 <context-group purpose="location">
3597 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3598 <context context-type="linenumber">24</context>
3599 </context-group>
3598 </trans-unit> 3600 </trans-unit>
3599 <trans-unit id="4691552465058437520" datatype="html"> 3601 <trans-unit id="4691552465058437520" datatype="html">
3600 <source>Commented video</source> 3602 <source>Commented video</source>
@@ -3896,8 +3898,8 @@ The link will expire within 1 hour.</source>
3896 <trans-unit id="4917252294930256268" datatype="html"> 3898 <trans-unit id="4917252294930256268" datatype="html">
3897 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3899 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3898 <target state="translated">Bạn đang ở trên một máy chủ không hỗ trợ HTTPS. Cần phải kích hoạt TLS trước khi theo dõi những máy khác.</target> 3900 <target state="translated">Bạn đang ở trên một máy chủ không hỗ trợ HTTPS. Cần phải kích hoạt TLS trước khi theo dõi những máy khác.</target>
3899 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 3901
3900 </trans-unit> 3902 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3901 <trans-unit id="4058814854824495833" datatype="html"> 3903 <trans-unit id="4058814854824495833" datatype="html">
3902 <source>Mute domains</source> 3904 <source>Mute domains</source>
3903 <target state="translated">Ẩn máy chủ</target> 3905 <target state="translated">Ẩn máy chủ</target>
@@ -5832,11 +5834,8 @@ color: red;
5832 <trans-unit id="5512878593724620692" datatype="html"> 5834 <trans-unit id="5512878593724620692" datatype="html">
5833 <source>CHANNELS</source> 5835 <source>CHANNELS</source>
5834 <target state="translated">KÊNH</target> 5836 <target state="translated">KÊNH</target>
5835 <context-group purpose="location"> 5837
5836 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5838 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5837 <context context-type="linenumber">82</context>
5838 </context-group>
5839 </trans-unit>
5840 <trans-unit id="3666829335406793239" datatype="html"> 5839 <trans-unit id="3666829335406793239" datatype="html">
5841 <source>This account does not have channels.</source> 5840 <source>This account does not have channels.</source>
5842 <target state="translated">Tài khoản này không mở kênh.</target> 5841 <target state="translated">Tài khoản này không mở kênh.</target>
@@ -5875,6 +5874,12 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
5875channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 5874channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
5876 <target state="translated">Bạn có chắc chắn muốn xóa <x id="PH" equiv-text="videoChannel.displayName"/>? Điều này sẽ xóa hết <x id="PH_1" equiv-text="videoChannel.videosCount"/> video đã đăng trên kênh này, và bạn sẽ không thể tạo kênh khác có cùng tên (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target> 5875 <target state="translated">Bạn có chắc chắn muốn xóa <x id="PH" equiv-text="videoChannel.displayName"/>? Điều này sẽ xóa hết <x id="PH_1" equiv-text="videoChannel.videosCount"/> video đã đăng trên kênh này, và bạn sẽ không thể tạo kênh khác có cùng tên (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target>
5877 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 5876 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
5877 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
5878 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5879 <context-group purpose="location">
5880 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5881 <context context-type="linenumber">48</context>
5882 </context-group>
5878 </trans-unit> 5883 </trans-unit>
5879 <trans-unit id="5387007581996837469" datatype="html"> 5884 <trans-unit id="5387007581996837469" datatype="html">
5880 <source>My Channels</source> 5885 <source>My Channels</source>
@@ -6388,13 +6393,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6388 <trans-unit id="6979021199788941693" datatype="html"> 6393 <trans-unit id="6979021199788941693" datatype="html">
6389 <source>Your message has been sent.</source> 6394 <source>Your message has been sent.</source>
6390 <target state="translated">Tin nhắn của bạn đã được gửi đi.</target> 6395 <target state="translated">Tin nhắn của bạn đã được gửi đi.</target>
6391 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group> 6396
6392 </trans-unit> 6397 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6393 <trans-unit id="2072135752262464360" datatype="html"> 6398 <trans-unit id="2072135752262464360" datatype="html">
6394 <source>You already sent this form recently</source> 6399 <source>You already sent this form recently</source>
6395 <target state="translated">Bạn đã gửi rồi gần đây</target> 6400 <target state="translated">Bạn đã gửi rồi gần đây</target>
6396 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group> 6401
6397 </trans-unit> 6402 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6398 <trans-unit id="819067926858619041" datatype="html"> 6403 <trans-unit id="819067926858619041" datatype="html">
6399 <source>Account videos</source> 6404 <source>Account videos</source>
6400 <target state="translated">Video tài khoản</target> 6405 <target state="translated">Video tài khoản</target>
@@ -6437,13 +6442,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6437 <trans-unit id="4856575356061361269" datatype="html"> 6442 <trans-unit id="4856575356061361269" datatype="html">
6438 <source><x id="PH"/> direct account followers </source> 6443 <source><x id="PH"/> direct account followers </source>
6439 <target state="translated"><x id="PH"/> người theo dõi tài khoản trực tiếp </target> 6444 <target state="translated"><x id="PH"/> người theo dõi tài khoản trực tiếp </target>
6440 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6445
6441 </trans-unit> 6446 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6442 <trans-unit id="6250999352462648289" datatype="html"> 6447 <trans-unit id="6250999352462648289" datatype="html">
6443 <source>Report this account</source> 6448 <source>Report this account</source>
6444 <target state="translated">Báo cáo tài khoản này</target> 6449 <target state="translated">Báo cáo tài khoản này</target>
6445 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6450
6446 </trans-unit> 6451 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6447 <trans-unit id="1504521795586863905" datatype="html"> 6452 <trans-unit id="1504521795586863905" datatype="html">
6448 <source>VIDEOS</source> 6453 <source>VIDEOS</source>
6449 <target state="translated">VIDEO</target> 6454 <target state="translated">VIDEO</target>
@@ -6453,31 +6458,21 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6453 <trans-unit id="25349740244798533" datatype="html"> 6458 <trans-unit id="25349740244798533" datatype="html">
6454 <source>Username copied</source> 6459 <source>Username copied</source>
6455 <target state="translated">Đã chép tên tài khoản</target> 6460 <target state="translated">Đã chép tên tài khoản</target>
6456 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6461
6457 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6462
6458 </trans-unit> 6463 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6459 <trans-unit id="9221735175659318025" datatype="html"> 6464 <trans-unit id="9221735175659318025" datatype="html">
6460 <source>1 subscriber</source> 6465 <source>1 subscriber</source>
6461 <target state="translated">1 người đăng ký</target> 6466 <target state="translated">1 người đăng ký</target>
6462 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6467
6463 </trans-unit> 6468 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6464 <trans-unit id="4097331874769079975" datatype="html"> 6469 <trans-unit id="4097331874769079975" datatype="html">
6465 <source><x id="PH"/> subscribers</source> 6470 <source><x id="PH"/> subscribers</source>
6466 <target state="translated"><x id="PH"/> người đăng ký</target> 6471 <target state="translated"><x id="PH"/> người đăng ký</target>
6467 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6472
6468 </trans-unit> 6473 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6469 <trans-unit id="4682675125751819107" datatype="html"> 6474
6470 <source>Instances you follow</source> 6475
6471 <target state="translated">Những máy chủ bạn theo dõi</target>
6472 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6473 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6474 </trans-unit>
6475 <trans-unit id="8899833753704589712" datatype="html">
6476 <source>Instances following you</source>
6477 <target state="translated">Những máy chủ đang theo dõi bạn</target>
6478 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6479 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6480 </trans-unit>
6481 <trans-unit id="1035838766454786107" datatype="html"> 6476 <trans-unit id="1035838766454786107" datatype="html">
6482 <source>Audio-only</source> 6477 <source>Audio-only</source>
6483 <target state="translated">Chỉ có âm thanh</target> 6478 <target state="translated">Chỉ có âm thanh</target>
@@ -6527,6 +6522,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6527 <source>Auto (via ffmpeg)</source> 6522 <source>Auto (via ffmpeg)</source>
6528 <target state="translated">Tự động (ffmpeg)</target> 6523 <target state="translated">Tự động (ffmpeg)</target>
6529 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6524 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6525 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6526 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6527 <context-group purpose="location">
6528 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6529 <context context-type="linenumber">3</context>
6530 </context-group>
6530 </trans-unit> 6531 </trans-unit>
6531 <trans-unit id="931255636742351800" datatype="html"> 6532 <trans-unit id="931255636742351800" datatype="html">
6532 <source>No limit</source> 6533 <source>No limit</source>
@@ -6675,18 +6676,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6675 <trans-unit id="2127446333083057097" datatype="html"> 6676 <trans-unit id="2127446333083057097" datatype="html">
6676 <source>Domain is required.</source> 6677 <source>Domain is required.</source>
6677 <target state="translated">Yêu cầu tên miền.</target> 6678 <target state="translated">Yêu cầu tên miền.</target>
6678 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6679
6679 </trans-unit> 6680 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6680 <trans-unit id="6780793142903080663" datatype="html"> 6681 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6681 <source>Domains entered are invalid.</source> 6682 <context-group purpose="location">
6682 <target state="translated">Tên miền đã nhập không đúng.</target> 6683 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6683 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6684 <context context-type="linenumber">93</context>
6684 </trans-unit> 6685 </context-group>
6685 <trans-unit id="5886492514458202177" datatype="html"> 6686 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6686 <source>Domains entered contain duplicates.</source> 6687 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6687 <target state="translated">Những tên miền đã nhập bị trùng lặp.</target> 6688 <context-group purpose="location">
6688 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6689 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6690 <context context-type="linenumber">94</context>
6691 </context-group>
6692 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6693 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6694 <context-group purpose="location">
6695 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6696 <context context-type="linenumber">102</context>
6697 </context-group>
6698 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6699 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6700 <context-group purpose="location">
6701 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6702 <context context-type="linenumber">103</context>
6703 </context-group>
6689 </trans-unit> 6704 </trans-unit>
6705
6706
6690 <trans-unit id="240806681889331244" datatype="html"> 6707 <trans-unit id="240806681889331244" datatype="html">
6691 <source>Unlimited</source> 6708 <source>Unlimited</source>
6692 <target state="translated">Không giới hạn</target> 6709 <target state="translated">Không giới hạn</target>
@@ -6840,22 +6857,48 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6840 <source><x id="PH"/> removed from instance followers </source> 6857 <source><x id="PH"/> removed from instance followers </source>
6841 <target state="translated"><x id="PH"/> đã bị xóa khỏi người theo dõi máy chủ </target> 6858 <target state="translated"><x id="PH"/> đã bị xóa khỏi người theo dõi máy chủ </target>
6842 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6859 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6860 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6861 <source>Follow</source><target state="new">Follow</target>
6862 <context-group purpose="location">
6863 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6864 <context context-type="linenumber">3</context>
6865 </context-group>
6866 <context-group purpose="location">
6867 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6868 <context context-type="linenumber">37</context>
6869 </context-group>
6870 <context-group purpose="location">
6871 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6872 <context context-type="linenumber">18</context>
6873 </context-group>
6874 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6875 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6876 <context-group purpose="location">
6877 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6878 <context context-type="linenumber">11</context>
6879 </context-group>
6843 </trans-unit> 6880 </trans-unit>
6844 <trans-unit id="2740793005745065895" datatype="html"> 6881 <trans-unit id="2740793005745065895" datatype="html">
6845 <source><x id="PH"/> is not valid </source> 6882 <source><x id="PH"/> is not valid </source>
6846 <target state="translated"><x id="PH"/> vô giá trị </target> 6883 <target state="translated"><x id="PH"/> vô giá trị </target>
6847 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6884
6848 </trans-unit> 6885 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6849 <trans-unit id="2355066641781598196" datatype="html"> 6886 <trans-unit id="2355066641781598196" datatype="html">
6850 <source>Follow request(s) sent!</source> 6887 <source>Follow request(s) sent!</source>
6851 <target state="translated">Đã gửi yêu cầu theo dõi!</target> 6888 <target state="translated">Đã gửi yêu cầu theo dõi!</target>
6852 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6889
6890 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6891 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6892 <context-group purpose="location">
6893 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6894 <context context-type="linenumber">3</context>
6895 </context-group>
6853 </trans-unit> 6896 </trans-unit>
6854 <trans-unit id="4245720728052819482" datatype="html"> 6897 <trans-unit id="4245720728052819482" datatype="html">
6855 <source>Do you really want to unfollow <x id="PH"/>?</source> 6898 <source>Do you really want to unfollow <x id="PH"/>?</source>
6856 <target state="translated">Bạn có chắc muốn ngưng theo dõi <x id="PH"/>?</target> 6899 <target state="translated">Bạn có chắc muốn ngưng theo dõi <x id="PH"/>?</target>
6857 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6900
6858 </trans-unit> 6901 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6859 <trans-unit id="9160510009013134726" datatype="html"> 6902 <trans-unit id="9160510009013134726" datatype="html">
6860 <source>Unfollow</source> 6903 <source>Unfollow</source>
6861 <target state="translated">Ngưng theo dõi</target> 6904 <target state="translated">Ngưng theo dõi</target>
@@ -6864,8 +6907,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6864 <trans-unit id="3935234189109112926" datatype="html"> 6907 <trans-unit id="3935234189109112926" datatype="html">
6865 <source>You are not following <x id="PH"/> anymore.</source> 6908 <source>You are not following <x id="PH"/> anymore.</source>
6866 <target state="translated">Bạn không còn theo dõi <x id="PH"/> nữa.</target> 6909 <target state="translated">Bạn không còn theo dõi <x id="PH"/> nữa.</target>
6867 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6910
6868 </trans-unit> 6911 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6869 <trans-unit id="2593763089859685916" datatype="html"> 6912 <trans-unit id="2593763089859685916" datatype="html">
6870 <source>enabled</source> 6913 <source>enabled</source>
6871 <target state="translated">đã bật</target> 6914 <target state="translated">đã bật</target>
@@ -7329,9 +7372,9 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7329 <trans-unit id="1519954996184640001" datatype="html"> 7372 <trans-unit id="1519954996184640001" datatype="html">
7330 <source>Error</source> 7373 <source>Error</source>
7331 <target state="translated">Lỗi</target> 7374 <target state="translated">Lỗi</target>
7332 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7375
7333 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7376
7334 </trans-unit> 7377 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7335 <trans-unit id="5076187961693950167" datatype="html"> 7378 <trans-unit id="5076187961693950167" datatype="html">
7336 <source>Standard logs</source> 7379 <source>Standard logs</source>
7337 <target state="translated">Nhật trình chuẩn</target> 7380 <target state="translated">Nhật trình chuẩn</target>
@@ -7372,16 +7415,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7372 <target state="translated">Đổi mật khẩu người dùng</target> 7415 <target state="translated">Đổi mật khẩu người dùng</target>
7373 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7416 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7374 </trans-unit> 7417 </trans-unit>
7375 <trans-unit id="177544274549739411" datatype="html"> 7418
7376 <source>Following list</source> 7419
7377 <target state="translated">Danh sách đang theo dõi</target>
7378 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7379 </trans-unit>
7380 <trans-unit id="8092429110007204784" datatype="html">
7381 <source>Followers list</source>
7382 <target state="translated">Danh sách người theo dõi</target>
7383 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7384 </trans-unit>
7385 <trans-unit id="780323526182667308" datatype="html"> 7420 <trans-unit id="780323526182667308" datatype="html">
7386 <source>User <x id="PH"/> updated.</source> 7421 <source>User <x id="PH"/> updated.</source>
7387 <target state="translated">Người dùng <x id="PH"/> đã cập nhật.</target> 7422 <target state="translated">Người dùng <x id="PH"/> đã cập nhật.</target>
@@ -7417,16 +7452,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7417 <target state="translated">Liên hợp</target> 7452 <target state="translated">Liên hợp</target>
7418 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7453 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7419 </trans-unit> 7454 </trans-unit>
7420 <trans-unit id="4682675125751819107" datatype="html"> 7455
7421 <source>Instances you follow</source> 7456
7422 <target state="translated">Những máy chủ bạn theo dõi</target>
7423 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7424 </trans-unit>
7425 <trans-unit id="8899833753704589712" datatype="html">
7426 <source>Instances following you</source>
7427 <target state="translated">Những máy chủ theo dõi bạn</target>
7428 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7429 </trans-unit>
7430 <trans-unit id="3767259920053407667" datatype="html"> 7457 <trans-unit id="3767259920053407667" datatype="html">
7431 <source>Videos will be deleted, comments will be tombstoned.</source> 7458 <source>Videos will be deleted, comments will be tombstoned.</source>
7432 <target state="translated">Video sẽ bị xóa, còn bình luận bị hóa đá.</target> 7459 <target state="translated">Video sẽ bị xóa, còn bình luận bị hóa đá.</target>
@@ -7457,6 +7484,24 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7457 <target state="translated">Cài Email như Xác Thực</target> 7484 <target state="translated">Cài Email như Xác Thực</target>
7458 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7485 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7459 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7486 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7487 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7488 <source>Created</source><target state="new">Created</target>
7489 <context-group purpose="location">
7490 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7491 <context context-type="linenumber">115</context>
7492 </context-group>
7493 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7494 <source>Daily quota</source><target state="new">Daily quota</target>
7495 <context-group purpose="location">
7496 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7497 <context context-type="linenumber">120</context>
7498 </context-group>
7499 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7500 <source>Last login</source><target state="new">Last login</target>
7501 <context-group purpose="location">
7502 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7503 <context context-type="linenumber">122</context>
7504 </context-group>
7460 </trans-unit> 7505 </trans-unit>
7461 <trans-unit id="3403978719736970622"> 7506 <trans-unit id="3403978719736970622">
7462 <source>You cannot ban root.</source> 7507 <source>You cannot ban root.</source>
@@ -7759,13 +7804,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7759 <trans-unit id="1137937154872046253" datatype="html"> 7804 <trans-unit id="1137937154872046253" datatype="html">
7760 <source>Video channel <x id="PH"/> created.</source> 7805 <source>Video channel <x id="PH"/> created.</source>
7761 <target state="translated">Kênh video <x id="PH"/> đã tạo.</target> 7806 <target state="translated">Kênh video <x id="PH"/> đã tạo.</target>
7762 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 7807
7763 </trans-unit> 7808 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7764 <trans-unit id="8723777130353305761" datatype="html"> 7809 <trans-unit id="8723777130353305761" datatype="html">
7765 <source>This name already exists on this instance.</source> 7810 <source>This name already exists on this instance.</source>
7766 <target state="translated">Tên này đã có người đăng ký.</target> 7811 <target state="translated">Tên này đã có người đăng ký.</target>
7767 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 7812
7768 </trans-unit> 7813 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7769 <trans-unit id="7589345916094713536" datatype="html"> 7814 <trans-unit id="7589345916094713536" datatype="html">
7770 <source>Video channel <x id="PH"/> updated.</source> 7815 <source>Video channel <x id="PH"/> updated.</source>
7771 <target state="translated">Kênh video <x id="PH"/> đã cập nhật.</target> 7816 <target state="translated">Kênh video <x id="PH"/> đã cập nhật.</target>
@@ -7786,11 +7831,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7786 <target state="translated">Đã xóa ảnh bìa.</target> 7831 <target state="translated">Đã xóa ảnh bìa.</target>
7787 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 7832 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
7788 </trans-unit> 7833 </trans-unit>
7789 <trans-unit id="2575302837003821736" datatype="html"> 7834
7790 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7791 <target state="translated">Hãy nhập tên hiển thị của kênh video ( <x id="PH"/>) để xác nhận</target>
7792 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
7793 </trans-unit>
7794 <trans-unit id="624066830180032195" datatype="html"> 7835 <trans-unit id="624066830180032195" datatype="html">
7795 <source>Video channel <x id="PH"/> deleted.</source> 7836 <source>Video channel <x id="PH"/> deleted.</source>
7796 <target state="translated">Kênh video <x id="PH"/> đã xóa.</target> 7837 <target state="translated">Kênh video <x id="PH"/> đã xóa.</target>
@@ -7938,6 +7979,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7938 <source>Ownership change request sent.</source> 7979 <source>Ownership change request sent.</source>
7939 <target state="translated">Đã gửi yêu cầu thay đổi chủ sở hữu.</target> 7980 <target state="translated">Đã gửi yêu cầu thay đổi chủ sở hữu.</target>
7940 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 7981 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
7982 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
7983 <source>Sort by</source><target state="new">Sort by</target>
7984 <context-group purpose="location">
7985 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
7986 <context context-type="linenumber">26</context>
7987 </context-group>
7941 </trans-unit> 7988 </trans-unit>
7942 <trans-unit id="3245220240937722814" datatype="html"> 7989 <trans-unit id="3245220240937722814" datatype="html">
7943 <source>My channels</source> 7990 <source>My channels</source>
@@ -8036,7 +8083,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8036 <target state="translated">Theo dõi tài khoản này</target> 8083 <target state="translated">Theo dõi tài khoản này</target>
8037 8084
8038 8085
8039 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8086 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8040 <trans-unit id="3131904093925601441" datatype="html"> 8087 <trans-unit id="3131904093925601441" datatype="html">
8041 <source>PLAYLISTS</source> 8088 <source>PLAYLISTS</source>
8042 <target state="translated">DANH SÁCH PHÁT</target> 8089 <target state="translated">DANH SÁCH PHÁT</target>
@@ -8083,34 +8130,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8083 <trans-unit id="3779524668013120370" datatype="html"> 8130 <trans-unit id="3779524668013120370" datatype="html">
8084 <source>Go to my subscriptions</source> 8131 <source>Go to my subscriptions</source>
8085 <target state="translated">Đến lượt đăng ký của tôi</target> 8132 <target state="translated">Đến lượt đăng ký của tôi</target>
8086 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8133
8087 </trans-unit> 8134 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8088 <trans-unit id="1136469849928650779" datatype="html"> 8135 <trans-unit id="1136469849928650779" datatype="html">
8089 <source>Go to my videos</source> 8136 <source>Go to my videos</source>
8090 <target state="translated">Đến trang video của tôi</target> 8137 <target state="translated">Đến trang video của tôi</target>
8091 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8138
8092 </trans-unit> 8139 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8093 <trans-unit id="7836683738999600376" datatype="html"> 8140 <trans-unit id="7836683738999600376" datatype="html">
8094 <source>Go to my imports</source> 8141 <source>Go to my imports</source>
8095 <target state="translated">Đến trang video tôi nhập</target> 8142 <target state="translated">Đến trang video tôi nhập</target>
8096 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8143
8097 </trans-unit> 8144 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8098 <trans-unit id="7511292153332773503" datatype="html"> 8145 <trans-unit id="7511292153332773503" datatype="html">
8099 <source>Go to my channels</source> 8146 <source>Go to my channels</source>
8100 <target state="translated">Đến kênh của tôi</target> 8147 <target state="translated">Đến kênh của tôi</target>
8101 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8148
8102 </trans-unit> 8149 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8103 <trans-unit id="2013324644839511073" datatype="html"> 8150 <trans-unit id="2013324644839511073" datatype="html">
8104 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8151 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8105Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8152Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8106 <target state="translated">Không thể truy xuất thông tin đăng nhập ứng dụng khách OAuth: <x id="PH"/>. Hãy chắc rằng bạn đã cấu hình đúng PeerTube (config/ directory), đặc biệt là phần "webserver".</target> 8153 <target state="translated">Không thể truy xuất thông tin đăng nhập ứng dụng khách OAuth: <x id="PH"/>. Hãy chắc rằng bạn đã cấu hình đúng PeerTube (config/ directory), đặc biệt là phần "webserver".</target>
8107 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8154
8108 </trans-unit> 8155 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8109 <trans-unit id="375263728166936544" datatype="html"> 8156 <trans-unit id="375263728166936544" datatype="html">
8110 <source>You need to reconnect.</source> 8157 <source>You need to reconnect.</source>
8111 <target state="translated">Bạn cần kết nối lại.</target> 8158 <target state="translated">Bạn cần kết nối lại.</target>
8112 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8159
8113 </trans-unit> 8160 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8114 <trans-unit id="2206638022166154361" datatype="html"> 8161 <trans-unit id="2206638022166154361" datatype="html">
8115 <source>Keyboard Shortcuts:</source> 8162 <source>Keyboard Shortcuts:</source>
8116 <target state="translated">Phím tắt:</target> 8163 <target state="translated">Phím tắt:</target>
@@ -8123,6 +8170,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8123 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8170 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8124 <context context-type="linenumber">98</context> 8171 <context context-type="linenumber">98</context>
8125 </context-group> 8172 </context-group>
8173 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8174 <source>In my library</source><target state="new">In my library</target>
8175 <context-group purpose="location">
8176 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8177 <context context-type="linenumber">104</context>
8178 </context-group>
8126 </trans-unit> 8179 </trans-unit>
8127 <trans-unit id="232050922346936574" datatype="html"> 8180 <trans-unit id="232050922346936574" datatype="html">
8128 <source>Trending</source> 8181 <source>Trending</source>
@@ -8151,38 +8204,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8151 <trans-unit id="1266887509445371246" datatype="html"> 8204 <trans-unit id="1266887509445371246" datatype="html">
8152 <source>Incorrect username or password.</source> 8205 <source>Incorrect username or password.</source>
8153 <target state="translated">Sai tên hoặc mật khẩu.</target> 8206 <target state="translated">Sai tên hoặc mật khẩu.</target>
8154 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8207
8155 </trans-unit> 8208 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8156 <trans-unit id="6974874606619467663" datatype="html"> 8209 <trans-unit id="6974874606619467663" datatype="html">
8157 <source>Your account is blocked.</source> 8210 <source>Your account is blocked.</source>
8158 <target state="translated">Tài khoản của bạn đã bị khóa.</target> 8211 <target state="translated">Tài khoản của bạn đã bị khóa.</target>
8159 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8212
8160 </trans-unit> 8213 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8161 <trans-unit id="7939914198003891823" datatype="html"> 8214 <trans-unit id="7939914198003891823" datatype="html">
8162 <source>any language</source> 8215 <source>any language</source>
8163 <target state="translated">ngôn ngữ bất kỳ</target> 8216 <target state="translated">ngôn ngữ bất kỳ</target>
8164 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8217
8165 </trans-unit> 8218 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8166 <trans-unit id="5633144232269377096" datatype="html"> 8219 <trans-unit id="5633144232269377096" datatype="html">
8167 <source>hide</source> 8220 <source>hide</source>
8168 <target state="translated">ẩn</target> 8221 <target state="translated">ẩn</target>
8169 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8222
8170 </trans-unit> 8223 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8171 <trans-unit id="8603861867909474404" datatype="html"> 8224 <trans-unit id="8603861867909474404" datatype="html">
8172 <source>blur</source> 8225 <source>blur</source>
8173 <target state="translated">làm mờ</target> 8226 <target state="translated">làm mờ</target>
8174 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8227
8175 </trans-unit> 8228 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8176 <trans-unit id="4534458451100881847" datatype="html"> 8229 <trans-unit id="4534458451100881847" datatype="html">
8177 <source>display</source> 8230 <source>display</source>
8178 <target state="translated">hiển thị</target> 8231 <target state="translated">hiển thị</target>
8179 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8232
8180 </trans-unit> 8233 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8181 <trans-unit id="4467323362722952678" datatype="html"> 8234 <trans-unit id="4467323362722952678" datatype="html">
8182 <source>Unknown</source> 8235 <source>Unknown</source>
8183 <target state="translated">—</target> 8236 <target state="translated">—</target>
8184 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8237
8185 </trans-unit> 8238 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8186 <trans-unit id="8781423666414310853" datatype="html"> 8239 <trans-unit id="8781423666414310853" datatype="html">
8187 <source>Your password has been successfully reset!</source> 8240 <source>Your password has been successfully reset!</source>
8188 <target state="translated">Bạn đã đổi mật khẩu thành công!</target> 8241 <target state="translated">Bạn đã đổi mật khẩu thành công!</target>
@@ -9760,18 +9813,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9760 <target>Bạn đã thực hiện điều này quá nhiều lần, xin thử lại sau 9813 <target>Bạn đã thực hiện điều này quá nhiều lần, xin thử lại sau
9761 <x id="PH"/> phút. 9814 <x id="PH"/> phút.
9762 </target> 9815 </target>
9763 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9816
9764 </trans-unit> 9817 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9765 <trans-unit id="4965472196059235310"> 9818 <trans-unit id="4965472196059235310">
9766 <source>Too many attempts, please try again later.</source> 9819 <source>Too many attempts, please try again later.</source>
9767 <target>Quá nhiều lần thực hiện, vui lòng thử lại sau.</target> 9820 <target>Quá nhiều lần thực hiện, vui lòng thử lại sau.</target>
9768 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9821
9769 </trans-unit> 9822 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9770 <trans-unit id="1693549688987384699"> 9823 <trans-unit id="1693549688987384699">
9771 <source>Server error. Please retry later.</source> 9824 <source>Server error. Please retry later.</source>
9772 <target>Lỗi máy chủ. Xin thử lại sau.</target> 9825 <target>Lỗi máy chủ. Xin thử lại sau.</target>
9773 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9826
9774 </trans-unit> 9827 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9775 <trans-unit id="5927402622550505067" datatype="html"> 9828 <trans-unit id="5927402622550505067" datatype="html">
9776 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9829 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9777 <target state="translated">Đã đăng toàn bộ kênh gần đây của <x id="PH"/>. Bạn sẽ nhận được thông báo về video mới của họ.</target> 9830 <target state="translated">Đã đăng toàn bộ kênh gần đây của <x id="PH"/>. Bạn sẽ nhận được thông báo về video mới của họ.</target>
@@ -10363,35 +10416,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10363 <trans-unit id="3284171506518522275"> 10416 <trans-unit id="3284171506518522275">
10364 <source>Your video was uploaded to your account and is private.</source> 10417 <source>Your video was uploaded to your account and is private.</source>
10365 <target>Video đã được tải lên riêng tư vào tài khoản của bạn.</target> 10418 <target>Video đã được tải lên riêng tư vào tài khoản của bạn.</target>
10366 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10419
10367 </trans-unit> 10420 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10368 <trans-unit id="5699822024600815733"> 10421 <trans-unit id="5699822024600815733">
10369 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10422 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10370 <target>Nhưng các dữ liệu liên quan (thẻ, mô tả,...) sẽ bị mất. Bạn có chắc muốn rời khỏi trang không?</target> 10423 <target>Nhưng các dữ liệu liên quan (thẻ, mô tả,...) sẽ bị mất. Bạn có chắc muốn rời khỏi trang không?</target>
10371 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10424
10372 </trans-unit> 10425 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10373 <trans-unit id="1219739004043110649"> 10426 <trans-unit id="1219739004043110649">
10374 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10427 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10375 <target>Video của bạn vẫn chưa được tải lên, bạn có chắc muốn rời trang?</target> 10428 <target>Video của bạn vẫn chưa được tải lên, bạn có chắc muốn rời trang?</target>
10376 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10429
10377 </trans-unit> 10430 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10378 <trans-unit id="6932865105766151309" datatype="html"> 10431 <trans-unit id="6932865105766151309" datatype="html">
10379 <source>Upload</source> 10432 <source>Upload</source>
10380 <target state="translated">Tải lên</target> 10433 <target state="translated">Tải lên</target>
10381 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10434
10382 </trans-unit> 10435 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10383 <trans-unit id="8278735427925094503"> 10436 <trans-unit id="8278735427925094503">
10384 <source>Upload <x id="PH"/> </source> 10437 <source>Upload <x id="PH"/> </source>
10385 <target>Tải lên 10438 <target>Tải lên
10386 <x id="PH"/> 10439 <x id="PH"/>
10387 </target> 10440 </target>
10388 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10441
10389 </trans-unit> 10442 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10390 <trans-unit id="5981816353437801748"> 10443 <trans-unit id="5981816353437801748">
10391 <source>Video published.</source> 10444 <source>Video published.</source>
10392 <target>Đã xuất bản video.</target> 10445 <target>Đã xuất bản video.</target>
10393 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10446
10394 </trans-unit> 10447 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10395 <trans-unit id="764164089183618119"> 10448 <trans-unit id="764164089183618119">
10396 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10449 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10397 <target>Bạn có sửa đổi chưa lưu! Nếu rời đi, những sửa đổi này sẽ bị mất.</target> 10450 <target>Bạn có sửa đổi chưa lưu! Nếu rời đi, những sửa đổi này sẽ bị mất.</target>
@@ -10458,28 +10511,28 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10458 <trans-unit id="961774488937452220" datatype="html"> 10511 <trans-unit id="961774488937452220" datatype="html">
10459 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10512 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10460 <target state="translated">Video không khả dụng trên máy chủ này. Bạn có muốn chuyển tới máy chủ gốc: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10513 <target state="translated">Video không khả dụng trên máy chủ này. Bạn có muốn chuyển tới máy chủ gốc: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10461 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group> 10514
10462 </trans-unit> 10515 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10463 <trans-unit id="5761611056224181752" datatype="html"> 10516 <trans-unit id="5761611056224181752" datatype="html">
10464 <source>Redirection</source> 10517 <source>Redirection</source>
10465 <target state="translated">Chuyển hướng</target> 10518 <target state="translated">Chuyển hướng</target>
10466 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group> 10519
10467 </trans-unit> 10520 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10468 <trans-unit id="8858527736400081688"> 10521 <trans-unit id="8858527736400081688">
10469 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10522 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10470 <target>Video này chứa nội dung cho người lớn hoặc nhạy cảm. Bạn có chắc chắn muốn xem không?</target> 10523 <target>Video này chứa nội dung cho người lớn hoặc nhạy cảm. Bạn có chắc chắn muốn xem không?</target>
10471 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group> 10524
10472 </trans-unit> 10525 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10473 <trans-unit id="3937119019020041049"> 10526 <trans-unit id="3937119019020041049">
10474 <source>Mature or explicit content</source> 10527 <source>Mature or explicit content</source>
10475 <target>Nội dung người lớn hoặc nhạy cảm</target> 10528 <target>Nội dung người lớn hoặc nhạy cảm</target>
10476 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group> 10529
10477 </trans-unit> 10530 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10478 <trans-unit id="1755474755114288376" datatype="html"> 10531 <trans-unit id="1755474755114288376" datatype="html">
10479 <source>Up Next</source> 10532 <source>Up Next</source>
10480 <target state="translated">Tiếp Theo</target> 10533 <target state="translated">Tiếp Theo</target>
10481 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group> 10534
10482 </trans-unit> 10535 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10483 <trans-unit id="2159130950882492111" datatype="html"> 10536 <trans-unit id="2159130950882492111" datatype="html">
10484 <source>Cancel</source> 10537 <source>Cancel</source>
10485 <target state="translated">Hủy bỏ</target> 10538 <target state="translated">Hủy bỏ</target>
@@ -10488,63 +10541,63 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10488 <trans-unit id="3354816756665089864" datatype="html"> 10541 <trans-unit id="3354816756665089864" datatype="html">
10489 <source>Autoplay is suspended</source> 10542 <source>Autoplay is suspended</source>
10490 <target state="translated">Tạm ngừng tự phát</target> 10543 <target state="translated">Tạm ngừng tự phát</target>
10491 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group> 10544
10492 </trans-unit> 10545 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10493 <trans-unit id="7895294730547405228" datatype="html"> 10546 <trans-unit id="7895294730547405228" datatype="html">
10494 <source>Enter/exit fullscreen (requires player focus)</source> 10547 <source>Enter/exit fullscreen (requires player focus)</source>
10495 <target state="translated">Mở/thoát toàn màn hình (yêu cầu trọng tâm trình phát)</target> 10548 <target state="translated">Mở/thoát toàn màn hình (yêu cầu trọng tâm trình phát)</target>
10496 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group> 10549
10497 </trans-unit> 10550 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10498 <trans-unit id="7618388257165864759" datatype="html"> 10551 <trans-unit id="7618388257165864759" datatype="html">
10499 <source>Play/Pause the video (requires player focus)</source> 10552 <source>Play/Pause the video (requires player focus)</source>
10500 <target state="translated">Phát/Ngừng video (yêu cầu trọng tâm trình phát)</target> 10553 <target state="translated">Phát/Ngừng video (yêu cầu trọng tâm trình phát)</target>
10501 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group> 10554
10502 </trans-unit> 10555 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10503 <trans-unit id="7761890399634216630" datatype="html"> 10556 <trans-unit id="7761890399634216630" datatype="html">
10504 <source>Mute/unmute the video (requires player focus)</source> 10557 <source>Mute/unmute the video (requires player focus)</source>
10505 <target state="translated">Ẩn/bỏ ẩn video (yêu cầu trọng tâm trình phát)</target> 10558 <target state="translated">Ẩn/bỏ ẩn video (yêu cầu trọng tâm trình phát)</target>
10506 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group> 10559
10507 </trans-unit> 10560 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10508 <trans-unit id="5996585232248234904" datatype="html"> 10561 <trans-unit id="5996585232248234904" datatype="html">
10509 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10562 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10510 <target state="translated">Chuyển đến một mức phần trăm của video: 0 là 0% và 9 là 90% (yêu cầu trọng tâm trình phát)</target> 10563 <target state="translated">Chuyển đến một mức phần trăm của video: 0 là 0% và 9 là 90% (yêu cầu trọng tâm trình phát)</target>
10511 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group> 10564
10512 </trans-unit> 10565 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10513 <trans-unit id="3748765405903319998" datatype="html"> 10566 <trans-unit id="3748765405903319998" datatype="html">
10514 <source>Increase the volume (requires player focus)</source> 10567 <source>Increase the volume (requires player focus)</source>
10515 <target state="translated">Tăng âm lượng (yêu cầu trọng tâm trình phát)</target> 10568 <target state="translated">Tăng âm lượng (yêu cầu trọng tâm trình phát)</target>
10516 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group> 10569
10517 </trans-unit> 10570 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10518 <trans-unit id="5810704036407159982" datatype="html"> 10571 <trans-unit id="5810704036407159982" datatype="html">
10519 <source>Decrease the volume (requires player focus)</source> 10572 <source>Decrease the volume (requires player focus)</source>
10520 <target state="translated">Giảm âm lượng (yêu cầu trọng tâm trình phát)</target> 10573 <target state="translated">Giảm âm lượng (yêu cầu trọng tâm trình phát)</target>
10521 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group> 10574
10522 </trans-unit> 10575 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10523 <trans-unit id="2622048822548065691" datatype="html"> 10576 <trans-unit id="2622048822548065691" datatype="html">
10524 <source>Seek the video forward (requires player focus)</source> 10577 <source>Seek the video forward (requires player focus)</source>
10525 <target state="translated">Tua tới video (yêu cầu trọng tâm trình phát)</target> 10578 <target state="translated">Tua tới video (yêu cầu trọng tâm trình phát)</target>
10526 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group> 10579
10527 </trans-unit> 10580 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10528 <trans-unit id="6540078205109221153" datatype="html"> 10581 <trans-unit id="6540078205109221153" datatype="html">
10529 <source>Seek the video backward (requires player focus)</source> 10582 <source>Seek the video backward (requires player focus)</source>
10530 <target state="translated">Tua lùi video (yêu cầu trọng tâm video)</target> 10583 <target state="translated">Tua lùi video (yêu cầu trọng tâm video)</target>
10531 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group> 10584
10532 </trans-unit> 10585 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10533 <trans-unit id="1956491957766210808" datatype="html"> 10586 <trans-unit id="1956491957766210808" datatype="html">
10534 <source>Increase playback rate (requires player focus)</source> 10587 <source>Increase playback rate (requires player focus)</source>
10535 <target state="translated">Tăng tốc độ phát (yêu cầu trọng tâm trình phát)</target> 10588 <target state="translated">Tăng tốc độ phát (yêu cầu trọng tâm trình phát)</target>
10536 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group> 10589
10537 </trans-unit> 10590 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10538 <trans-unit id="5495529997674803186" datatype="html"> 10591 <trans-unit id="5495529997674803186" datatype="html">
10539 <source>Decrease playback rate (requires player focus)</source> 10592 <source>Decrease playback rate (requires player focus)</source>
10540 <target state="translated">Giảm tốc độ phát (yêu cầu trọng tâm trình phát)</target> 10593 <target state="translated">Giảm tốc độ phát (yêu cầu trọng tâm trình phát)</target>
10541 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group> 10594
10542 </trans-unit> 10595 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10543 <trans-unit id="3178343147230721210" datatype="html"> 10596 <trans-unit id="3178343147230721210" datatype="html">
10544 <source>Navigate in the video frame by frame (requires player focus)</source> 10597 <source>Navigate in the video frame by frame (requires player focus)</source>
10545 <target state="translated">Điều hướng trong từng khung hình video (yêu cầu trọng tâm trình phát)</target> 10598 <target state="translated">Điều hướng trong từng khung hình video (yêu cầu trọng tâm trình phát)</target>
10546 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group> 10599
10547 </trans-unit> 10600 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10548 <trans-unit id="8025996572234182184"> 10601 <trans-unit id="8025996572234182184">
10549 <source>Like the video</source> 10602 <source>Like the video</source>
10550 <target>Thích video</target> 10603 <target>Thích video</target>
diff --git a/client/src/locale/angular.xlf b/client/src/locale/angular.xlf
index 39012827d..418d2fe96 100644
--- a/client/src/locale/angular.xlf
+++ b/client/src/locale/angular.xlf
@@ -522,8 +522,12 @@
522 <context context-type="linenumber">48</context> 522 <context context-type="linenumber">48</context>
523 </context-group> 523 </context-group>
524 <context-group purpose="location"> 524 <context-group purpose="location">
525 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
526 <context context-type="linenumber">33</context>
527 </context-group>
528 <context-group purpose="location">
525 <context context-type="sourcefile">src/app/+login/login.component.html</context> 529 <context context-type="sourcefile">src/app/+login/login.component.html</context>
526 <context context-type="linenumber">117</context> 530 <context context-type="linenumber">121</context>
527 </context-group> 531 </context-group>
528 <context-group purpose="location"> 532 <context-group purpose="location">
529 <context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context> 533 <context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context>
@@ -551,7 +555,7 @@
551 </context-group> 555 </context-group>
552 <context-group purpose="location"> 556 <context-group purpose="location">
553 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 557 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
554 <context context-type="linenumber">408</context> 558 <context context-type="linenumber">415</context>
555 </context-group> 559 </context-group>
556 <context-group purpose="location"> 560 <context-group purpose="location">
557 <context context-type="sourcefile">src/app/modal/confirm.component.html</context> 561 <context context-type="sourcefile">src/app/modal/confirm.component.html</context>
@@ -628,14 +632,14 @@
628 <source>Your message has been sent.</source> 632 <source>Your message has been sent.</source>
629 <context-group purpose="location"> 633 <context-group purpose="location">
630 <context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context> 634 <context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context>
631 <context context-type="linenumber">89</context> 635 <context context-type="linenumber">88</context>
632 </context-group> 636 </context-group>
633 </trans-unit> 637 </trans-unit>
634 <trans-unit id="2072135752262464360" datatype="html"> 638 <trans-unit id="2072135752262464360" datatype="html">
635 <source>You already sent this form recently</source> 639 <source>You already sent this form recently</source>
636 <context-group purpose="location"> 640 <context-group purpose="location">
637 <context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context> 641 <context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context>
638 <context context-type="linenumber">95</context> 642 <context context-type="linenumber">94</context>
639 </context-group> 643 </context-group>
640 </trans-unit> 644 </trans-unit>
641 <trans-unit id="1045244999981860085" datatype="html"> 645 <trans-unit id="1045244999981860085" datatype="html">
@@ -1185,14 +1189,14 @@
1185 <source>CHANNELS</source> 1189 <source>CHANNELS</source>
1186 <context-group purpose="location"> 1190 <context-group purpose="location">
1187 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 1191 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context>
1188 <context context-type="linenumber">82</context> 1192 <context context-type="linenumber">81</context>
1189 </context-group> 1193 </context-group>
1190 </trans-unit> 1194 </trans-unit>
1191 <trans-unit id="1504521795586863905" datatype="html"> 1195 <trans-unit id="1504521795586863905" datatype="html">
1192 <source>VIDEOS</source> 1196 <source>VIDEOS</source>
1193 <context-group purpose="location"> 1197 <context-group purpose="location">
1194 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 1198 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context>
1195 <context context-type="linenumber">83</context> 1199 <context context-type="linenumber">82</context>
1196 </context-group> 1200 </context-group>
1197 <context-group purpose="location"> 1201 <context-group purpose="location">
1198 <context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context> 1202 <context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context>
@@ -1207,7 +1211,7 @@
1207 <source>Username copied</source> 1211 <source>Username copied</source>
1208 <context-group purpose="location"> 1212 <context-group purpose="location">
1209 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 1213 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context>
1210 <context context-type="linenumber">121</context> 1214 <context context-type="linenumber">120</context>
1211 </context-group> 1215 </context-group>
1212 <context-group purpose="location"> 1216 <context-group purpose="location">
1213 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context> 1217 <context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context>
@@ -1218,28 +1222,28 @@
1218 <source>1 subscriber</source> 1222 <source>1 subscriber</source>
1219 <context-group purpose="location"> 1223 <context-group purpose="location">
1220 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 1224 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context>
1221 <context context-type="linenumber">125</context> 1225 <context context-type="linenumber">124</context>
1222 </context-group> 1226 </context-group>
1223 </trans-unit> 1227 </trans-unit>
1224 <trans-unit id="4097331874769079975" datatype="html"> 1228 <trans-unit id="4097331874769079975" datatype="html">
1225 <source><x id="PH" equiv-text="count"/> subscribers</source> 1229 <source><x id="PH" equiv-text="count"/> subscribers</source>
1226 <context-group purpose="location"> 1230 <context-group purpose="location">
1227 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 1231 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context>
1228 <context context-type="linenumber">127</context> 1232 <context context-type="linenumber">126</context>
1229 </context-group> 1233 </context-group>
1230 </trans-unit> 1234 </trans-unit>
1231 <trans-unit id="4856575356061361269" datatype="html"> 1235 <trans-unit id="4856575356061361269" datatype="html">
1232 <source><x id="PH" equiv-text="account.followersCount"/> direct account followers</source> 1236 <source><x id="PH" equiv-text="account.followersCount"/> direct account followers</source>
1233 <context-group purpose="location"> 1237 <context-group purpose="location">
1234 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 1238 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context>
1235 <context context-type="linenumber">155</context> 1239 <context context-type="linenumber">154</context>
1236 </context-group> 1240 </context-group>
1237 </trans-unit> 1241 </trans-unit>
1238 <trans-unit id="6250999352462648289" datatype="html"> 1242 <trans-unit id="6250999352462648289" datatype="html">
1239 <source>Report this account</source> 1243 <source>Report this account</source>
1240 <context-group purpose="location"> 1244 <context-group purpose="location">
1241 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 1245 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context>
1242 <context context-type="linenumber">196</context> 1246 <context context-type="linenumber">195</context>
1243 </context-group> 1247 </context-group>
1244 </trans-unit> 1248 </trans-unit>
1245 <trans-unit id="8564701209009684429" datatype="html"> 1249 <trans-unit id="8564701209009684429" datatype="html">
@@ -1253,26 +1257,30 @@
1253 <context context-type="linenumber">58</context> 1257 <context context-type="linenumber">58</context>
1254 </context-group> 1258 </context-group>
1255 </trans-unit> 1259 </trans-unit>
1256 <trans-unit id="4682675125751819107" datatype="html"> 1260 <trans-unit id="8726138323871139597" datatype="html">
1257 <source>Instances you follow</source> 1261 <source>Following</source>
1258 <context-group purpose="location"> 1262 <context-group purpose="location">
1259 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context> 1263 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
1260 <context context-type="linenumber">29</context> 1264 <context context-type="linenumber">29</context>
1261 </context-group> 1265 </context-group>
1262 <context-group purpose="location"> 1266 <context-group purpose="location">
1263 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context> 1267 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
1264 <context context-type="linenumber">3</context> 1268 <context context-type="linenumber">31</context>
1269 </context-group>
1270 <context-group purpose="location">
1271 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
1272 <context context-type="linenumber">28</context>
1265 </context-group> 1273 </context-group>
1266 </trans-unit> 1274 </trans-unit>
1267 <trans-unit id="8899833753704589712" datatype="html"> 1275 <trans-unit id="4914577418256256836" datatype="html">
1268 <source>Instances following you</source> 1276 <source>Followers</source>
1269 <context-group purpose="location"> 1277 <context-group purpose="location">
1270 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context> 1278 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
1271 <context context-type="linenumber">34</context> 1279 <context context-type="linenumber">34</context>
1272 </context-group> 1280 </context-group>
1273 <context-group purpose="location"> 1281 <context-group purpose="location">
1274 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context> 1282 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
1275 <context context-type="linenumber">3</context> 1283 <context context-type="linenumber">37</context>
1276 </context-group> 1284 </context-group>
1277 </trans-unit> 1285 </trans-unit>
1278 <trans-unit id="9031514421077169181" datatype="html"> 1286 <trans-unit id="9031514421077169181" datatype="html">
@@ -2968,6 +2976,13 @@ color: red;
2968 <context context-type="linenumber">50</context> 2976 <context context-type="linenumber">50</context>
2969 </context-group> 2977 </context-group>
2970 </trans-unit> 2978 </trans-unit>
2979 <trans-unit id="3642770981085338761" datatype="html">
2980 <source>Followers of your instance</source>
2981 <context-group purpose="location">
2982 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
2983 <context context-type="linenumber">3</context>
2984 </context-group>
2985 </trans-unit>
2971 <trans-unit id="2845798909207198924" datatype="html"> 2986 <trans-unit id="2845798909207198924" datatype="html">
2972 <source>Showing <x id="INTERPOLATION" equiv-text="{{'{first}'}}"/> to <x id="INTERPOLATION_1" equiv-text="{{'{last}'}}"/> of <x id="INTERPOLATION_2" equiv-text="{{'{totalRecords}'}}"/> followers</source> 2987 <source>Showing <x id="INTERPOLATION" equiv-text="{{'{first}'}}"/> to <x id="INTERPOLATION_1" equiv-text="{{'{last}'}}"/> of <x id="INTERPOLATION_2" equiv-text="{{'{totalRecords}'}}"/> followers</source>
2973 <context-group purpose="location"> 2988 <context-group purpose="location">
@@ -2998,8 +3013,8 @@ color: red;
2998 <context context-type="linenumber">41</context> 3013 <context context-type="linenumber">41</context>
2999 </context-group> 3014 </context-group>
3000 </trans-unit> 3015 </trans-unit>
3001 <trans-unit id="2265605798180116441" datatype="html"> 3016 <trans-unit id="8390803680962035202" datatype="html">
3002 <source>Follower handle</source> 3017 <source>Follower</source>
3003 <context-group purpose="location"> 3018 <context-group purpose="location">
3004 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context> 3019 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3005 <context context-type="linenumber">24</context> 3020 <context context-type="linenumber">24</context>
@@ -3260,18 +3275,54 @@ color: red;
3260 <context context-type="linenumber">81</context> 3275 <context context-type="linenumber">81</context>
3261 </context-group> 3276 </context-group>
3262 </trans-unit> 3277 </trans-unit>
3263 <trans-unit id="4774348799569692380" datatype="html"> 3278 <trans-unit id="6018246591673612412" datatype="html">
3264 <source>Showing <x id="INTERPOLATION" equiv-text="{{'{first}'}}"/> to <x id="INTERPOLATION_1" equiv-text="{{'{last}'}}"/> of <x id="INTERPOLATION_2" equiv-text="{{'{totalRecords}'}}"/> hosts</source> 3279 <source>Follow</source>
3280 <context-group purpose="location">
3281 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
3282 <context context-type="linenumber">3</context>
3283 </context-group>
3284 <context-group purpose="location">
3285 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
3286 <context context-type="linenumber">37</context>
3287 </context-group>
3265 <context-group purpose="location"> 3288 <context-group purpose="location">
3266 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context> 3289 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
3290 <context context-type="linenumber">18</context>
3291 </context-group>
3292 </trans-unit>
3293 <trans-unit id="3596798855644241001" datatype="html">
3294 <source>1 host (without "http://"), account handle or channel handle per line</source>
3295 <context-group purpose="location">
3296 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
3267 <context context-type="linenumber">11</context> 3297 <context context-type="linenumber">11</context>
3268 </context-group> 3298 </context-group>
3269 </trans-unit> 3299 </trans-unit>
3270 <trans-unit id="1268699198448750610" datatype="html"> 3300 <trans-unit id="4917252294930256268" datatype="html">
3271 <source>Follow instances</source> 3301 <source> It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. </source>
3302 <context-group purpose="location">
3303 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
3304 <context context-type="linenumber">28,29</context>
3305 </context-group>
3306 </trans-unit>
3307 <trans-unit id="2355066641781598196" datatype="html">
3308 <source>Follow request(s) sent!</source>
3309 <context-group purpose="location">
3310 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context>
3311 <context context-type="linenumber">62</context>
3312 </context-group>
3313 </trans-unit>
3314 <trans-unit id="3459358413436264734" datatype="html">
3315 <source>Your instance subscriptions</source>
3272 <context-group purpose="location"> 3316 <context-group purpose="location">
3273 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context> 3317 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
3274 <context context-type="linenumber">18</context> 3318 <context context-type="linenumber">3</context>
3319 </context-group>
3320 </trans-unit>
3321 <trans-unit id="4774348799569692380" datatype="html">
3322 <source>Showing <x id="INTERPOLATION" equiv-text="{{'{first}'}}"/> to <x id="INTERPOLATION_1" equiv-text="{{'{last}'}}"/> of <x id="INTERPOLATION_2" equiv-text="{{'{totalRecords}'}}"/> hosts</source>
3323 <context-group purpose="location">
3324 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
3325 <context context-type="linenumber">11</context>
3275 </context-group> 3326 </context-group>
3276 </trans-unit> 3327 </trans-unit>
3277 <trans-unit id="9216117865911519658" datatype="html"> 3328 <trans-unit id="9216117865911519658" datatype="html">
@@ -3301,13 +3352,6 @@ color: red;
3301 <context context-type="linenumber">30</context> 3352 <context context-type="linenumber">30</context>
3302 </context-group> 3353 </context-group>
3303 </trans-unit> 3354 </trans-unit>
3304 <trans-unit id="6641024648411549335" datatype="html">
3305 <source>Host</source>
3306 <context-group purpose="location">
3307 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
3308 <context context-type="linenumber">31</context>
3309 </context-group>
3310 </trans-unit>
3311 <trans-unit id="6571718060636962350" datatype="html"> 3355 <trans-unit id="6571718060636962350" datatype="html">
3312 <source>Redundancy allowed <x id="START_TAG_P_SORTICON" ctype="x-p_sorticon" equiv-text="&lt;p-sortIcon field=&quot;redundancyAllowed&quot;>"/><x id="CLOSE_TAG_P_SORTICON" ctype="x-p_sorticon" equiv-text="&lt;/p-sortIcon>"/></source> 3356 <source>Redundancy allowed <x id="START_TAG_P_SORTICON" ctype="x-p_sorticon" equiv-text="&lt;p-sortIcon field=&quot;redundancyAllowed&quot;>"/><x id="CLOSE_TAG_P_SORTICON" ctype="x-p_sorticon" equiv-text="&lt;/p-sortIcon>"/></source>
3313 <context-group purpose="location"> 3357 <context-group purpose="location">
@@ -3323,7 +3367,7 @@ color: red;
3323 </context-group> 3367 </context-group>
3324 <context-group purpose="location"> 3368 <context-group purpose="location">
3325 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context> 3369 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context>
3326 <context context-type="linenumber">58</context> 3370 <context context-type="linenumber">48</context>
3327 </context-group> 3371 </context-group>
3328 </trans-unit> 3372 </trans-unit>
3329 <trans-unit id="8246779176913476983" datatype="html"> 3373 <trans-unit id="8246779176913476983" datatype="html">
@@ -3345,63 +3389,28 @@ color: red;
3345 <source>No host found matching current filters.</source> 3389 <source>No host found matching current filters.</source>
3346 <context-group purpose="location"> 3390 <context-group purpose="location">
3347 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context> 3391 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
3348 <context context-type="linenumber">70</context> 3392 <context context-type="linenumber">71</context>
3349 </context-group> 3393 </context-group>
3350 </trans-unit> 3394 </trans-unit>
3351 <trans-unit id="7274241885665071790" datatype="html"> 3395 <trans-unit id="7274241885665071790" datatype="html">
3352 <source>Your instance is not following anyone.</source> 3396 <source>Your instance is not following anyone.</source>
3353 <context-group purpose="location"> 3397 <context-group purpose="location">
3354 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context> 3398 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
3355 <context context-type="linenumber">71</context> 3399 <context context-type="linenumber">72</context>
3356 </context-group>
3357 </trans-unit>
3358 <trans-unit id="6275803119759621687" datatype="html">
3359 <source>Follow domains</source>
3360 <context-group purpose="location">
3361 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
3362 <context context-type="linenumber">78</context>
3363 </context-group>
3364 </trans-unit>
3365 <trans-unit id="4917252294930256268" datatype="html">
3366 <source> It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. </source>
3367 <context-group purpose="location">
3368 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
3369 <context context-type="linenumber">81,82</context>
3370 </context-group>
3371 </trans-unit>
3372 <trans-unit id="2355066641781598196" datatype="html">
3373 <source>Follow request(s) sent!</source>
3374 <context-group purpose="location">
3375 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context>
3376 <context context-type="linenumber">47</context>
3377 </context-group> 3400 </context-group>
3378 </trans-unit> 3401 </trans-unit>
3379 <trans-unit id="4245720728052819482" datatype="html"> 3402 <trans-unit id="4245720728052819482" datatype="html">
3380 <source>Do you really want to unfollow <x id="PH" equiv-text="follow.following.host"/>?</source> 3403 <source>Do you really want to unfollow <x id="PH" equiv-text="follow.following.host"/>?</source>
3381 <context-group purpose="location"> 3404 <context-group purpose="location">
3382 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context> 3405 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context>
3383 <context context-type="linenumber">57</context> 3406 <context context-type="linenumber">47</context>
3384 </context-group> 3407 </context-group>
3385 </trans-unit> 3408 </trans-unit>
3386 <trans-unit id="3935234189109112926" datatype="html"> 3409 <trans-unit id="3935234189109112926" datatype="html">
3387 <source>You are not following <x id="PH" equiv-text="follow.following.host"/> anymore.</source> 3410 <source>You are not following <x id="PH" equiv-text="follow.following.host"/> anymore.</source>
3388 <context-group purpose="location"> 3411 <context-group purpose="location">
3389 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context> 3412 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context>
3390 <context context-type="linenumber">64</context> 3413 <context context-type="linenumber">54</context>
3391 </context-group>
3392 </trans-unit>
3393 <trans-unit id="177544274549739411" datatype="html">
3394 <source>Following list</source>
3395 <context-group purpose="location">
3396 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
3397 <context context-type="linenumber">28</context>
3398 </context-group>
3399 </trans-unit>
3400 <trans-unit id="8092429110007204784" datatype="html">
3401 <source>Followers list</source>
3402 <context-group purpose="location">
3403 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
3404 <context context-type="linenumber">37</context>
3405 </context-group> 3414 </context-group>
3406 </trans-unit> 3415 </trans-unit>
3407 <trans-unit id="2593763089859685916" datatype="html"> 3416 <trans-unit id="2593763089859685916" datatype="html">
@@ -4578,7 +4587,7 @@ color: red;
4578 </context-group> 4587 </context-group>
4579 <context-group purpose="location"> 4588 <context-group purpose="location">
4580 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context> 4589 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context>
4581 <context context-type="linenumber">103</context> 4590 <context context-type="linenumber">102</context>
4582 </context-group> 4591 </context-group>
4583 <context-group purpose="location"> 4592 <context-group purpose="location">
4584 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context> 4593 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context>
@@ -4736,6 +4745,10 @@ color: red;
4736 <context context-type="linenumber">83</context> 4745 <context context-type="linenumber">83</context>
4737 </context-group> 4746 </context-group>
4738 <context-group purpose="location"> 4747 <context-group purpose="location">
4748 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
4749 <context context-type="linenumber">111</context>
4750 </context-group>
4751 <context-group purpose="location">
4739 <context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context> 4752 <context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context>
4740 <context context-type="linenumber">6</context> 4753 <context context-type="linenumber">6</context>
4741 </context-group> 4754 </context-group>
@@ -4792,8 +4805,12 @@ color: red;
4792 <context context-type="linenumber">105</context> 4805 <context context-type="linenumber">105</context>
4793 </context-group> 4806 </context-group>
4794 <context-group purpose="location"> 4807 <context-group purpose="location">
4808 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
4809 <context context-type="linenumber">112</context>
4810 </context-group>
4811 <context-group purpose="location">
4795 <context context-type="sourcefile">src/app/+login/login.component.html</context> 4812 <context context-type="sourcefile">src/app/+login/login.component.html</context>
4796 <context context-type="linenumber">107</context> 4813 <context context-type="linenumber">111</context>
4797 </context-group> 4814 </context-group>
4798 <context-group purpose="location"> 4815 <context-group purpose="location">
4799 <context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context> 4816 <context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context>
@@ -4835,11 +4852,11 @@ color: red;
4835 </context-group> 4852 </context-group>
4836 <context-group purpose="location"> 4853 <context-group purpose="location">
4837 <context context-type="sourcefile">src/app/+login/login.component.html</context> 4854 <context context-type="sourcefile">src/app/+login/login.component.html</context>
4838 <context context-type="linenumber">34</context> 4855 <context context-type="linenumber">38</context>
4839 </context-group> 4856 </context-group>
4840 <context-group purpose="location"> 4857 <context-group purpose="location">
4841 <context context-type="sourcefile">src/app/+login/login.component.html</context> 4858 <context context-type="sourcefile">src/app/+login/login.component.html</context>
4842 <context context-type="linenumber">36</context> 4859 <context context-type="linenumber">40</context>
4843 </context-group> 4860 </context-group>
4844 <context-group purpose="location"> 4861 <context-group purpose="location">
4845 <context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context> 4862 <context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context>
@@ -4879,6 +4896,10 @@ color: red;
4879 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 4896 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
4880 <context context-type="linenumber">136</context> 4897 <context context-type="linenumber">136</context>
4881 </context-group> 4898 </context-group>
4899 <context-group purpose="location">
4900 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
4901 <context context-type="linenumber">114</context>
4902 </context-group>
4882 </trans-unit> 4903 </trans-unit>
4883 <trans-unit id="2602586221576511475" datatype="html"> 4904 <trans-unit id="2602586221576511475" datatype="html">
4884 <source>Video quota</source> 4905 <source>Video quota</source>
@@ -4891,6 +4912,10 @@ color: red;
4891 <context context-type="linenumber">151</context> 4912 <context context-type="linenumber">151</context>
4892 </context-group> 4913 </context-group>
4893 <context-group purpose="location"> 4914 <context-group purpose="location">
4915 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
4916 <context context-type="linenumber">113</context>
4917 </context-group>
4918 <context-group purpose="location">
4894 <context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context> 4919 <context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context>
4895 <context context-type="linenumber">47</context> 4920 <context context-type="linenumber">47</context>
4896 </context-group> 4921 </context-group>
@@ -4931,6 +4956,10 @@ color: red;
4931 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 4956 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
4932 <context context-type="linenumber">188</context> 4957 <context context-type="linenumber">188</context>
4933 </context-group> 4958 </context-group>
4959 <context-group purpose="location">
4960 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
4961 <context context-type="linenumber">121</context>
4962 </context-group>
4934 </trans-unit> 4963 </trans-unit>
4935 <trans-unit id="588099657508661970" datatype="html"> 4964 <trans-unit id="588099657508661970" datatype="html">
4936 <source>None (local authentication)</source> 4965 <source>None (local authentication)</source>
@@ -5192,6 +5221,27 @@ color: red;
5192 <context context-type="linenumber">281</context> 5221 <context context-type="linenumber">281</context>
5193 </context-group> 5222 </context-group>
5194 </trans-unit> 5223 </trans-unit>
5224 <trans-unit id="4207916966377787111" datatype="html">
5225 <source>Created</source>
5226 <context-group purpose="location">
5227 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
5228 <context context-type="linenumber">115</context>
5229 </context-group>
5230 </trans-unit>
5231 <trans-unit id="8140268298586972139" datatype="html">
5232 <source>Daily quota</source>
5233 <context-group purpose="location">
5234 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
5235 <context context-type="linenumber">120</context>
5236 </context-group>
5237 </trans-unit>
5238 <trans-unit id="7910076708497708162" datatype="html">
5239 <source>Last login</source>
5240 <context-group purpose="location">
5241 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
5242 <context context-type="linenumber">122</context>
5243 </context-group>
5244 </trans-unit>
5195 <trans-unit id="3403978719736970622" datatype="html"> 5245 <trans-unit id="3403978719736970622" datatype="html">
5196 <source>You cannot ban root.</source> 5246 <source>You cannot ban root.</source>
5197 <context-group purpose="location"> 5247 <context-group purpose="location">
@@ -5278,7 +5328,7 @@ color: red;
5278 </context-group> 5328 </context-group>
5279 <context-group purpose="location"> 5329 <context-group purpose="location">
5280 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5330 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5281 <context context-type="linenumber">44</context> 5331 <context context-type="linenumber">48</context>
5282 </context-group> 5332 </context-group>
5283 <context-group purpose="location"> 5333 <context-group purpose="location">
5284 <context context-type="sourcefile">src/app/menu/menu.component.html</context> 5334 <context context-type="sourcefile">src/app/menu/menu.component.html</context>
@@ -5332,25 +5382,32 @@ color: red;
5332 <context context-type="linenumber">23</context> 5382 <context context-type="linenumber">23</context>
5333 </context-group> 5383 </context-group>
5334 </trans-unit> 5384 </trans-unit>
5385 <trans-unit id="1758058452376026925" datatype="html">
5386 <source> ⚠️ Most email addresses do not include capital letters. </source>
5387 <context-group purpose="location">
5388 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5389 <context context-type="linenumber">33,34</context>
5390 </context-group>
5391 </trans-unit>
5335 <trans-unit id="8715156686857791956" datatype="html"> 5392 <trans-unit id="8715156686857791956" datatype="html">
5336 <source>Click here to reset your password</source> 5393 <source>Click here to reset your password</source>
5337 <context-group purpose="location"> 5394 <context-group purpose="location">
5338 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5395 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5339 <context context-type="linenumber">47</context> 5396 <context context-type="linenumber">51</context>
5340 </context-group> 5397 </context-group>
5341 </trans-unit> 5398 </trans-unit>
5342 <trans-unit id="892063502898494584" datatype="html"> 5399 <trans-unit id="892063502898494584" datatype="html">
5343 <source>I forgot my password</source> 5400 <source>I forgot my password</source>
5344 <context-group purpose="location"> 5401 <context-group purpose="location">
5345 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5402 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5346 <context context-type="linenumber">47</context> 5403 <context context-type="linenumber">51</context>
5347 </context-group> 5404 </context-group>
5348 </trans-unit> 5405 </trans-unit>
5349 <trans-unit id="2308975396733519902" datatype="html"> 5406 <trans-unit id="2308975396733519902" datatype="html">
5350 <source>Create an account</source> 5407 <source>Create an account</source>
5351 <context-group purpose="location"> 5408 <context-group purpose="location">
5352 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5409 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5353 <context context-type="linenumber">50</context> 5410 <context context-type="linenumber">54</context>
5354 </context-group> 5411 </context-group>
5355 <context-group purpose="location"> 5412 <context-group purpose="location">
5356 <context context-type="sourcefile">src/app/menu/menu.component.html</context> 5413 <context context-type="sourcefile">src/app/menu/menu.component.html</context>
@@ -5361,56 +5418,56 @@ color: red;
5361 <source> Logging into an account lets you publish content </source> 5418 <source> Logging into an account lets you publish content </source>
5362 <context-group purpose="location"> 5419 <context-group purpose="location">
5363 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5420 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5364 <context context-type="linenumber">56,57</context> 5421 <context context-type="linenumber">60,61</context>
5365 </context-group> 5422 </context-group>
5366 </trans-unit> 5423 </trans-unit>
5367 <trans-unit id="7252854992688790751" datatype="html"> 5424 <trans-unit id="7252854992688790751" datatype="html">
5368 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 5425 <source> This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
5369 <context-group purpose="location"> 5426 <context-group purpose="location">
5370 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5427 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5371 <context context-type="linenumber">60,62</context> 5428 <context context-type="linenumber">64,66</context>
5372 </context-group> 5429 </context-group>
5373 </trans-unit> 5430 </trans-unit>
5374 <trans-unit id="7215649348148521605" datatype="html"> 5431 <trans-unit id="7215649348148521605" datatype="html">
5375 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 5432 <source> Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
5376 <context-group purpose="location"> 5433 <context-group purpose="location">
5377 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5434 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5378 <context context-type="linenumber">65,67</context> 5435 <context context-type="linenumber">69,71</context>
5379 </context-group> 5436 </context-group>
5380 </trans-unit> 5437 </trans-unit>
5381 <trans-unit id="3183213940445113677" datatype="html"> 5438 <trans-unit id="3183213940445113677" datatype="html">
5382 <source>Or sign in with</source> 5439 <source>Or sign in with</source>
5383 <context-group purpose="location"> 5440 <context-group purpose="location">
5384 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5441 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5385 <context context-type="linenumber">72</context> 5442 <context context-type="linenumber">76</context>
5386 </context-group> 5443 </context-group>
5387 </trans-unit> 5444 </trans-unit>
5388 <trans-unit id="3238209155172574367" datatype="html"> 5445 <trans-unit id="3238209155172574367" datatype="html">
5389 <source>Forgot your password</source> 5446 <source>Forgot your password</source>
5390 <context-group purpose="location"> 5447 <context-group purpose="location">
5391 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5448 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5392 <context context-type="linenumber">91</context> 5449 <context context-type="linenumber">95</context>
5393 </context-group> 5450 </context-group>
5394 </trans-unit> 5451 </trans-unit>
5395 <trans-unit id="87327320394367488" datatype="html"> 5452 <trans-unit id="87327320394367488" datatype="html">
5396 <source> We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. </source> 5453 <source> We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system. </source>
5397 <context-group purpose="location"> 5454 <context-group purpose="location">
5398 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5455 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5399 <context context-type="linenumber">99,100</context> 5456 <context context-type="linenumber">103,104</context>
5400 </context-group> 5457 </context-group>
5401 </trans-unit> 5458 </trans-unit>
5402 <trans-unit id="3188014010833256853" datatype="html"> 5459 <trans-unit id="3188014010833256853" datatype="html">
5403 <source> Enter your email address and we will send you a link to reset your password. </source> 5460 <source> Enter your email address and we will send you a link to reset your password. </source>
5404 <context-group purpose="location"> 5461 <context-group purpose="location">
5405 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5462 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5406 <context context-type="linenumber">103,104</context> 5463 <context context-type="linenumber">107,108</context>
5407 </context-group> 5464 </context-group>
5408 </trans-unit> 5465 </trans-unit>
5409 <trans-unit id="3967269098753656610" datatype="html"> 5466 <trans-unit id="3967269098753656610" datatype="html">
5410 <source>Email address</source> 5467 <source>Email address</source>
5411 <context-group purpose="location"> 5468 <context-group purpose="location">
5412 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5469 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5413 <context context-type="linenumber">109</context> 5470 <context context-type="linenumber">113</context>
5414 </context-group> 5471 </context-group>
5415 <context-group purpose="location"> 5472 <context-group purpose="location">
5416 <context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context> 5473 <context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context>
@@ -5421,7 +5478,7 @@ color: red;
5421 <source>Reset</source> 5478 <source>Reset</source>
5422 <context-group purpose="location"> 5479 <context-group purpose="location">
5423 <context context-type="sourcefile">src/app/+login/login.component.html</context> 5480 <context context-type="sourcefile">src/app/+login/login.component.html</context>
5424 <context context-type="linenumber">122</context> 5481 <context context-type="linenumber">126</context>
5425 </context-group> 5482 </context-group>
5426 <note priority="1" from="description">Password reset button</note> 5483 <note priority="1" from="description">Password reset button</note>
5427 </trans-unit> 5484 </trans-unit>
@@ -5437,14 +5494,14 @@ The link will expire within 1 hour.</source>
5437 <source>Incorrect username or password.</source> 5494 <source>Incorrect username or password.</source>
5438 <context-group purpose="location"> 5495 <context-group purpose="location">
5439 <context context-type="sourcefile">src/app/+login/login.component.ts</context> 5496 <context context-type="sourcefile">src/app/+login/login.component.ts</context>
5440 <context context-type="linenumber">159</context> 5497 <context context-type="linenumber">163</context>
5441 </context-group> 5498 </context-group>
5442 </trans-unit> 5499 </trans-unit>
5443 <trans-unit id="6974874606619467663" datatype="html"> 5500 <trans-unit id="6974874606619467663" datatype="html">
5444 <source>Your account is blocked.</source> 5501 <source>Your account is blocked.</source>
5445 <context-group purpose="location"> 5502 <context-group purpose="location">
5446 <context context-type="sourcefile">src/app/+login/login.component.ts</context> 5503 <context context-type="sourcefile">src/app/+login/login.component.ts</context>
5447 <context context-type="linenumber">160</context> 5504 <context context-type="linenumber">164</context>
5448 </context-group> 5505 </context-group>
5449 </trans-unit> 5506 </trans-unit>
5450 <trans-unit id="6658000829978978023" datatype="html"> 5507 <trans-unit id="6658000829978978023" datatype="html">
@@ -6021,14 +6078,14 @@ The link will expire within 1 hour.</source>
6021 <source>Video channel <x id="PH" equiv-text="videoChannelCreate.displayName"/> created.</source> 6078 <source>Video channel <x id="PH" equiv-text="videoChannelCreate.displayName"/> created.</source>
6022 <context-group purpose="location"> 6079 <context-group purpose="location">
6023 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context> 6080 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context>
6024 <context context-type="linenumber">67</context> 6081 <context context-type="linenumber">66</context>
6025 </context-group> 6082 </context-group>
6026 </trans-unit> 6083 </trans-unit>
6027 <trans-unit id="8723777130353305761" datatype="html"> 6084 <trans-unit id="8723777130353305761" datatype="html">
6028 <source>This name already exists on this instance.</source> 6085 <source>This name already exists on this instance.</source>
6029 <context-group purpose="location"> 6086 <context-group purpose="location">
6030 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context> 6087 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context>
6031 <context context-type="linenumber">73</context> 6088 <context context-type="linenumber">72</context>
6032 </context-group> 6089 </context-group>
6033 </trans-unit> 6090 </trans-unit>
6034 <trans-unit id="5387007581996837469" datatype="html"> 6091 <trans-unit id="5387007581996837469" datatype="html">
@@ -6257,8 +6314,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6257 <context context-type="linenumber">44,46</context> 6314 <context context-type="linenumber">44,46</context>
6258 </context-group> 6315 </context-group>
6259 </trans-unit> 6316 </trans-unit>
6260 <trans-unit id="2575302837003821736" datatype="html"> 6317 <trans-unit id="4433306639366959484" datatype="html">
6261 <source>Please type the display name of the video channel (<x id="PH" equiv-text="videoChannel.displayName"/>) to confirm</source> 6318 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source>
6262 <context-group purpose="location"> 6319 <context-group purpose="location">
6263 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context> 6320 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
6264 <context context-type="linenumber">48</context> 6321 <context context-type="linenumber">48</context>
@@ -6866,6 +6923,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6866 <context context-type="linenumber">64</context> 6923 <context context-type="linenumber">64</context>
6867 </context-group> 6924 </context-group>
6868 </trans-unit> 6925 </trans-unit>
6926 <trans-unit id="7699622144571229146" datatype="html">
6927 <source>Sort by</source>
6928 <context-group purpose="location">
6929 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
6930 <context context-type="linenumber">26</context>
6931 </context-group>
6932 </trans-unit>
6869 <trans-unit id="1788875035518441092" datatype="html"> 6933 <trans-unit id="1788875035518441092" datatype="html">
6870 <source>Last published first</source> 6934 <source>Last published first</source>
6871 <context-group purpose="location"> 6935 <context-group purpose="location">
@@ -7049,7 +7113,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7049 <source>I'm a teapot</source> 7113 <source>I'm a teapot</source>
7050 <context-group purpose="location"> 7114 <context-group purpose="location">
7051 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context> 7115 <context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context>
7052 <context context-type="linenumber">26</context> 7116 <context context-type="linenumber">27</context>
7053 </context-group> 7117 </context-group>
7054 </trans-unit> 7118 </trans-unit>
7055 <trans-unit id="3851357780293085233" datatype="html"> 7119 <trans-unit id="3851357780293085233" datatype="html">
@@ -7854,7 +7918,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7854 </context-group> 7918 </context-group>
7855 <context-group purpose="location"> 7919 <context-group purpose="location">
7856 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 7920 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
7857 <context context-type="linenumber">704</context> 7921 <context context-type="linenumber">711</context>
7858 </context-group> 7922 </context-group>
7859 </trans-unit> 7923 </trans-unit>
7860 <trans-unit id="3131904093925601441" datatype="html"> 7924 <trans-unit id="3131904093925601441" datatype="html">
@@ -8787,56 +8851,56 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8787 <source>Upload on hold</source> 8851 <source>Upload on hold</source>
8788 <context-group purpose="location"> 8852 <context-group purpose="location">
8789 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context> 8853 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context>
8790 <context context-type="linenumber">124</context> 8854 <context context-type="linenumber">123</context>
8791 </context-group> 8855 </context-group>
8792 </trans-unit> 8856 </trans-unit>
8793 <trans-unit id="3284171506518522275" datatype="html"> 8857 <trans-unit id="3284171506518522275" datatype="html">
8794 <source>Your video was uploaded to your account and is private.</source> 8858 <source>Your video was uploaded to your account and is private.</source>
8795 <context-group purpose="location"> 8859 <context-group purpose="location">
8796 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context> 8860 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context>
8797 <context context-type="linenumber">162</context> 8861 <context context-type="linenumber">161</context>
8798 </context-group> 8862 </context-group>
8799 </trans-unit> 8863 </trans-unit>
8800 <trans-unit id="5699822024600815733" datatype="html"> 8864 <trans-unit id="5699822024600815733" datatype="html">
8801 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 8865 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
8802 <context-group purpose="location"> 8866 <context-group purpose="location">
8803 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context> 8867 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context>
8804 <context context-type="linenumber">163</context> 8868 <context context-type="linenumber">162</context>
8805 </context-group> 8869 </context-group>
8806 </trans-unit> 8870 </trans-unit>
8807 <trans-unit id="1219739004043110649" datatype="html"> 8871 <trans-unit id="1219739004043110649" datatype="html">
8808 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 8872 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
8809 <context-group purpose="location"> 8873 <context-group purpose="location">
8810 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context> 8874 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context>
8811 <context context-type="linenumber">165</context> 8875 <context context-type="linenumber">164</context>
8812 </context-group> 8876 </context-group>
8813 </trans-unit> 8877 </trans-unit>
8814 <trans-unit id="6932865105766151309" datatype="html"> 8878 <trans-unit id="6932865105766151309" datatype="html">
8815 <source>Upload</source> 8879 <source>Upload</source>
8816 <context-group purpose="location"> 8880 <context-group purpose="location">
8817 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context> 8881 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context>
8818 <context context-type="linenumber">222</context> 8882 <context context-type="linenumber">221</context>
8819 </context-group> 8883 </context-group>
8820 </trans-unit> 8884 </trans-unit>
8821 <trans-unit id="8278735427925094503" datatype="html"> 8885 <trans-unit id="8278735427925094503" datatype="html">
8822 <source>Upload <x id="PH" equiv-text="videofile.name"/></source> 8886 <source>Upload <x id="PH" equiv-text="videofile.name"/></source>
8823 <context-group purpose="location"> 8887 <context-group purpose="location">
8824 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context> 8888 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context>
8825 <context context-type="linenumber">224</context> 8889 <context context-type="linenumber">223</context>
8826 </context-group> 8890 </context-group>
8827 </trans-unit> 8891 </trans-unit>
8828 <trans-unit id="5981816353437801748" datatype="html"> 8892 <trans-unit id="5981816353437801748" datatype="html">
8829 <source>Video published.</source> 8893 <source>Video published.</source>
8830 <context-group purpose="location"> 8894 <context-group purpose="location">
8831 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context> 8895 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context>
8832 <context context-type="linenumber">245</context> 8896 <context context-type="linenumber">244</context>
8833 </context-group> 8897 </context-group>
8834 </trans-unit> 8898 </trans-unit>
8835 <trans-unit id="1006562256968398209" datatype="html"> 8899 <trans-unit id="1006562256968398209" datatype="html">
8836 <source>video</source> 8900 <source>video</source>
8837 <context-group purpose="location"> 8901 <context-group purpose="location">
8838 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context> 8902 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context>
8839 <context context-type="linenumber">288</context> 8903 <context context-type="linenumber">287</context>
8840 </context-group> 8904 </context-group>
8841 <context-group purpose="location"> 8905 <context-group purpose="location">
8842 <context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context> 8906 <context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context>
@@ -8847,14 +8911,14 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8847 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 8911 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
8848 <context-group purpose="location"> 8912 <context-group purpose="location">
8849 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context> 8913 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context>
8850 <context context-type="linenumber">323</context> 8914 <context context-type="linenumber">322</context>
8851 </context-group> 8915 </context-group>
8852 </trans-unit> 8916 </trans-unit>
8853 <trans-unit id="7873395933409147217" datatype="html"> 8917 <trans-unit id="7873395933409147217" datatype="html">
8854 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 8918 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
8855 <context-group purpose="location"> 8919 <context-group purpose="location">
8856 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context> 8920 <context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context>
8857 <context context-type="linenumber">341</context> 8921 <context context-type="linenumber">340</context>
8858 </context-group> 8922 </context-group>
8859 </trans-unit> 8923 </trans-unit>
8860 <trans-unit id="285180972645018518" datatype="html"> 8924 <trans-unit id="285180972645018518" datatype="html">
@@ -9597,10 +9661,6 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
9597 <context context-type="linenumber">24</context> 9661 <context context-type="linenumber">24</context>
9598 </context-group> 9662 </context-group>
9599 <context-group purpose="location"> 9663 <context-group purpose="location">
9600 <context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context>
9601 <context context-type="linenumber">3</context>
9602 </context-group>
9603 <context-group purpose="location">
9604 <context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context> 9664 <context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context>
9605 <context context-type="linenumber">27</context> 9665 <context context-type="linenumber">27</context>
9606 </context-group> 9666 </context-group>
@@ -9629,119 +9689,119 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
9629 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH" equiv-text="originUrl"/>"><x id="PH_1" equiv-text="originUrl"/>&lt;/a>?</source> 9689 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH" equiv-text="originUrl"/>"><x id="PH_1" equiv-text="originUrl"/>&lt;/a>?</source>
9630 <context-group purpose="location"> 9690 <context-group purpose="location">
9631 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9691 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9632 <context context-type="linenumber">288</context> 9692 <context context-type="linenumber">295</context>
9633 </context-group> 9693 </context-group>
9634 </trans-unit> 9694 </trans-unit>
9635 <trans-unit id="5761611056224181752" datatype="html"> 9695 <trans-unit id="5761611056224181752" datatype="html">
9636 <source>Redirection</source> 9696 <source>Redirection</source>
9637 <context-group purpose="location"> 9697 <context-group purpose="location">
9638 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9698 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9639 <context context-type="linenumber">289</context> 9699 <context context-type="linenumber">296</context>
9640 </context-group> 9700 </context-group>
9641 </trans-unit> 9701 </trans-unit>
9642 <trans-unit id="8858527736400081688" datatype="html"> 9702 <trans-unit id="8858527736400081688" datatype="html">
9643 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 9703 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
9644 <context-group purpose="location"> 9704 <context-group purpose="location">
9645 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9705 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9646 <context context-type="linenumber">335</context> 9706 <context context-type="linenumber">342</context>
9647 </context-group> 9707 </context-group>
9648 </trans-unit> 9708 </trans-unit>
9649 <trans-unit id="3937119019020041049" datatype="html"> 9709 <trans-unit id="3937119019020041049" datatype="html">
9650 <source>Mature or explicit content</source> 9710 <source>Mature or explicit content</source>
9651 <context-group purpose="location"> 9711 <context-group purpose="location">
9652 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9712 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9653 <context context-type="linenumber">336</context> 9713 <context context-type="linenumber">343</context>
9654 </context-group> 9714 </context-group>
9655 </trans-unit> 9715 </trans-unit>
9656 <trans-unit id="1755474755114288376" datatype="html"> 9716 <trans-unit id="1755474755114288376" datatype="html">
9657 <source>Up Next</source> 9717 <source>Up Next</source>
9658 <context-group purpose="location"> 9718 <context-group purpose="location">
9659 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9719 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9660 <context context-type="linenumber">407</context> 9720 <context context-type="linenumber">414</context>
9661 </context-group> 9721 </context-group>
9662 </trans-unit> 9722 </trans-unit>
9663 <trans-unit id="3354816756665089864" datatype="html"> 9723 <trans-unit id="3354816756665089864" datatype="html">
9664 <source>Autoplay is suspended</source> 9724 <source>Autoplay is suspended</source>
9665 <context-group purpose="location"> 9725 <context-group purpose="location">
9666 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9726 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9667 <context context-type="linenumber">409</context> 9727 <context context-type="linenumber">416</context>
9668 </context-group> 9728 </context-group>
9669 </trans-unit> 9729 </trans-unit>
9670 <trans-unit id="7895294730547405228" datatype="html"> 9730 <trans-unit id="7895294730547405228" datatype="html">
9671 <source>Enter/exit fullscreen (requires player focus)</source> 9731 <source>Enter/exit fullscreen (requires player focus)</source>
9672 <context-group purpose="location"> 9732 <context-group purpose="location">
9673 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9733 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9674 <context context-type="linenumber">678</context> 9734 <context context-type="linenumber">685</context>
9675 </context-group> 9735 </context-group>
9676 </trans-unit> 9736 </trans-unit>
9677 <trans-unit id="7618388257165864759" datatype="html"> 9737 <trans-unit id="7618388257165864759" datatype="html">
9678 <source>Play/Pause the video (requires player focus)</source> 9738 <source>Play/Pause the video (requires player focus)</source>
9679 <context-group purpose="location"> 9739 <context-group purpose="location">
9680 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9740 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9681 <context context-type="linenumber">679</context> 9741 <context context-type="linenumber">686</context>
9682 </context-group> 9742 </context-group>
9683 </trans-unit> 9743 </trans-unit>
9684 <trans-unit id="7761890399634216630" datatype="html"> 9744 <trans-unit id="7761890399634216630" datatype="html">
9685 <source>Mute/unmute the video (requires player focus)</source> 9745 <source>Mute/unmute the video (requires player focus)</source>
9686 <context-group purpose="location"> 9746 <context-group purpose="location">
9687 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9747 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9688 <context context-type="linenumber">680</context> 9748 <context context-type="linenumber">687</context>
9689 </context-group> 9749 </context-group>
9690 </trans-unit> 9750 </trans-unit>
9691 <trans-unit id="5996585232248234904" datatype="html"> 9751 <trans-unit id="5996585232248234904" datatype="html">
9692 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 9752 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
9693 <context-group purpose="location"> 9753 <context-group purpose="location">
9694 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9754 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9695 <context context-type="linenumber">682</context> 9755 <context context-type="linenumber">689</context>
9696 </context-group> 9756 </context-group>
9697 </trans-unit> 9757 </trans-unit>
9698 <trans-unit id="3748765405903319998" datatype="html"> 9758 <trans-unit id="3748765405903319998" datatype="html">
9699 <source>Increase the volume (requires player focus)</source> 9759 <source>Increase the volume (requires player focus)</source>
9700 <context-group purpose="location"> 9760 <context-group purpose="location">
9701 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9761 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9702 <context context-type="linenumber">684</context> 9762 <context context-type="linenumber">691</context>
9703 </context-group> 9763 </context-group>
9704 </trans-unit> 9764 </trans-unit>
9705 <trans-unit id="5810704036407159982" datatype="html"> 9765 <trans-unit id="5810704036407159982" datatype="html">
9706 <source>Decrease the volume (requires player focus)</source> 9766 <source>Decrease the volume (requires player focus)</source>
9707 <context-group purpose="location"> 9767 <context-group purpose="location">
9708 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9768 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9709 <context context-type="linenumber">685</context> 9769 <context context-type="linenumber">692</context>
9710 </context-group> 9770 </context-group>
9711 </trans-unit> 9771 </trans-unit>
9712 <trans-unit id="2622048822548065691" datatype="html"> 9772 <trans-unit id="2622048822548065691" datatype="html">
9713 <source>Seek the video forward (requires player focus)</source> 9773 <source>Seek the video forward (requires player focus)</source>
9714 <context-group purpose="location"> 9774 <context-group purpose="location">
9715 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9775 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9716 <context context-type="linenumber">687</context> 9776 <context context-type="linenumber">694</context>
9717 </context-group> 9777 </context-group>
9718 </trans-unit> 9778 </trans-unit>
9719 <trans-unit id="6540078205109221153" datatype="html"> 9779 <trans-unit id="6540078205109221153" datatype="html">
9720 <source>Seek the video backward (requires player focus)</source> 9780 <source>Seek the video backward (requires player focus)</source>
9721 <context-group purpose="location"> 9781 <context-group purpose="location">
9722 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9782 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9723 <context context-type="linenumber">688</context> 9783 <context context-type="linenumber">695</context>
9724 </context-group> 9784 </context-group>
9725 </trans-unit> 9785 </trans-unit>
9726 <trans-unit id="1956491957766210808" datatype="html"> 9786 <trans-unit id="1956491957766210808" datatype="html">
9727 <source>Increase playback rate (requires player focus)</source> 9787 <source>Increase playback rate (requires player focus)</source>
9728 <context-group purpose="location"> 9788 <context-group purpose="location">
9729 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9789 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9730 <context context-type="linenumber">690</context> 9790 <context context-type="linenumber">697</context>
9731 </context-group> 9791 </context-group>
9732 </trans-unit> 9792 </trans-unit>
9733 <trans-unit id="5495529997674803186" datatype="html"> 9793 <trans-unit id="5495529997674803186" datatype="html">
9734 <source>Decrease playback rate (requires player focus)</source> 9794 <source>Decrease playback rate (requires player focus)</source>
9735 <context-group purpose="location"> 9795 <context-group purpose="location">
9736 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9796 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9737 <context context-type="linenumber">691</context> 9797 <context context-type="linenumber">698</context>
9738 </context-group> 9798 </context-group>
9739 </trans-unit> 9799 </trans-unit>
9740 <trans-unit id="3178343147230721210" datatype="html"> 9800 <trans-unit id="3178343147230721210" datatype="html">
9741 <source>Navigate in the video frame by frame (requires player focus)</source> 9801 <source>Navigate in the video frame by frame (requires player focus)</source>
9742 <context-group purpose="location"> 9802 <context-group purpose="location">
9743 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context> 9803 <context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context>
9744 <context context-type="linenumber">693</context> 9804 <context context-type="linenumber">700</context>
9745 </context-group> 9805 </context-group>
9746 </trans-unit> 9806 </trans-unit>
9747 <trans-unit id="7627544798203088407" datatype="html"> 9807 <trans-unit id="7627544798203088407" datatype="html">
@@ -10018,28 +10078,28 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
10018 <source>Go to my subscriptions</source> 10078 <source>Go to my subscriptions</source>
10019 <context-group purpose="location"> 10079 <context-group purpose="location">
10020 <context context-type="sourcefile">src/app/core/auth/auth.service.ts</context> 10080 <context context-type="sourcefile">src/app/core/auth/auth.service.ts</context>
10021 <context context-type="linenumber">64</context> 10081 <context context-type="linenumber">63</context>
10022 </context-group> 10082 </context-group>
10023 </trans-unit> 10083 </trans-unit>
10024 <trans-unit id="1136469849928650779" datatype="html"> 10084 <trans-unit id="1136469849928650779" datatype="html">
10025 <source>Go to my videos</source> 10085 <source>Go to my videos</source>
10026 <context-group purpose="location"> 10086 <context-group purpose="location">
10027 <context context-type="sourcefile">src/app/core/auth/auth.service.ts</context> 10087 <context context-type="sourcefile">src/app/core/auth/auth.service.ts</context>
10028 <context context-type="linenumber">68</context> 10088 <context context-type="linenumber">67</context>
10029 </context-group> 10089 </context-group>
10030 </trans-unit> 10090 </trans-unit>
10031 <trans-unit id="7836683738999600376" datatype="html"> 10091 <trans-unit id="7836683738999600376" datatype="html">
10032 <source>Go to my imports</source> 10092 <source>Go to my imports</source>
10033 <context-group purpose="location"> 10093 <context-group purpose="location">
10034 <context context-type="sourcefile">src/app/core/auth/auth.service.ts</context> 10094 <context context-type="sourcefile">src/app/core/auth/auth.service.ts</context>
10035 <context context-type="linenumber">72</context> 10095 <context context-type="linenumber">71</context>
10036 </context-group> 10096 </context-group>
10037 </trans-unit> 10097 </trans-unit>
10038 <trans-unit id="7511292153332773503" datatype="html"> 10098 <trans-unit id="7511292153332773503" datatype="html">
10039 <source>Go to my channels</source> 10099 <source>Go to my channels</source>
10040 <context-group purpose="location"> 10100 <context-group purpose="location">
10041 <context context-type="sourcefile">src/app/core/auth/auth.service.ts</context> 10101 <context context-type="sourcefile">src/app/core/auth/auth.service.ts</context>
10042 <context context-type="linenumber">76</context> 10102 <context context-type="linenumber">75</context>
10043 </context-group> 10103 </context-group>
10044 </trans-unit> 10104 </trans-unit>
10045 <trans-unit id="2013324644839511073" datatype="html"> 10105 <trans-unit id="2013324644839511073" datatype="html">
@@ -10047,14 +10107,14 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
10047Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 10107Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
10048 <context-group purpose="location"> 10108 <context-group purpose="location">
10049 <context context-type="sourcefile">src/app/core/auth/auth.service.ts</context> 10109 <context context-type="sourcefile">src/app/core/auth/auth.service.ts</context>
10050 <context context-type="linenumber">99,100</context> 10110 <context context-type="linenumber">98,99</context>
10051 </context-group> 10111 </context-group>
10052 </trans-unit> 10112 </trans-unit>
10053 <trans-unit id="1519954996184640001" datatype="html"> 10113 <trans-unit id="1519954996184640001" datatype="html">
10054 <source>Error</source> 10114 <source>Error</source>
10055 <context-group purpose="location"> 10115 <context-group purpose="location">
10056 <context context-type="sourcefile">src/app/core/auth/auth.service.ts</context> 10116 <context context-type="sourcefile">src/app/core/auth/auth.service.ts</context>
10057 <context context-type="linenumber">104</context> 10117 <context context-type="linenumber">103</context>
10058 </context-group> 10118 </context-group>
10059 <context-group purpose="location"> 10119 <context-group purpose="location">
10060 <context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context> 10120 <context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context>
@@ -10065,7 +10125,7 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10065 <source>You need to reconnect.</source> 10125 <source>You need to reconnect.</source>
10066 <context-group purpose="location"> 10126 <context-group purpose="location">
10067 <context context-type="sourcefile">src/app/core/auth/auth.service.ts</context> 10127 <context context-type="sourcefile">src/app/core/auth/auth.service.ts</context>
10068 <context context-type="linenumber">220</context> 10128 <context context-type="linenumber">219</context>
10069 </context-group> 10129 </context-group>
10070 </trans-unit> 10130 </trans-unit>
10071 <trans-unit id="2206638022166154361" datatype="html"> 10131 <trans-unit id="2206638022166154361" datatype="html">
@@ -10082,6 +10142,13 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10082 <context context-type="linenumber">98</context> 10142 <context context-type="linenumber">98</context>
10083 </context-group> 10143 </context-group>
10084 </trans-unit> 10144 </trans-unit>
10145 <trans-unit id="4024404994702813072" datatype="html">
10146 <source>In my library</source>
10147 <context-group purpose="location">
10148 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
10149 <context context-type="linenumber">104</context>
10150 </context-group>
10151 </trans-unit>
10085 <trans-unit id="2821179408673282599" datatype="html"> 10152 <trans-unit id="2821179408673282599" datatype="html">
10086 <source>Home</source> 10153 <source>Home</source>
10087 <context-group purpose="location"> 10154 <context-group purpose="location">
@@ -10125,28 +10192,28 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10125 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 10192 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
10126 <context-group purpose="location"> 10193 <context-group purpose="location">
10127 <context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context> 10194 <context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context>
10128 <context context-type="linenumber">62</context> 10195 <context context-type="linenumber">61</context>
10129 </context-group> 10196 </context-group>
10130 </trans-unit> 10197 </trans-unit>
10131 <trans-unit id="968295009933361070" datatype="html"> 10198 <trans-unit id="968295009933361070" datatype="html">
10132 <source>Too many attempts, please try again after <x id="PH" equiv-text="minutesLeft"/> minutes.</source> 10199 <source>Too many attempts, please try again after <x id="PH" equiv-text="minutesLeft"/> minutes.</source>
10133 <context-group purpose="location"> 10200 <context-group purpose="location">
10134 <context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context> 10201 <context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context>
10135 <context context-type="linenumber">67</context> 10202 <context context-type="linenumber">66</context>
10136 </context-group> 10203 </context-group>
10137 </trans-unit> 10204 </trans-unit>
10138 <trans-unit id="4965472196059235310" datatype="html"> 10205 <trans-unit id="4965472196059235310" datatype="html">
10139 <source>Too many attempts, please try again later.</source> 10206 <source>Too many attempts, please try again later.</source>
10140 <context-group purpose="location"> 10207 <context-group purpose="location">
10141 <context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context> 10208 <context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context>
10142 <context context-type="linenumber">69</context> 10209 <context context-type="linenumber">68</context>
10143 </context-group> 10210 </context-group>
10144 </trans-unit> 10211 </trans-unit>
10145 <trans-unit id="1693549688987384699" datatype="html"> 10212 <trans-unit id="1693549688987384699" datatype="html">
10146 <source>Server error. Please retry later.</source> 10213 <source>Server error. Please retry later.</source>
10147 <context-group purpose="location"> 10214 <context-group purpose="location">
10148 <context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context> 10215 <context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context>
10149 <context context-type="linenumber">72</context> 10216 <context context-type="linenumber">71</context>
10150 </context-group> 10217 </context-group>
10151 </trans-unit> 10218 </trans-unit>
10152 <trans-unit id="4670312387769733978" datatype="html"> 10219 <trans-unit id="4670312387769733978" datatype="html">
@@ -10487,35 +10554,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10487 <source>Unknown</source> 10554 <source>Unknown</source>
10488 <context-group purpose="location"> 10555 <context-group purpose="location">
10489 <context context-type="sourcefile">src/app/menu/menu.component.ts</context> 10556 <context context-type="sourcefile">src/app/menu/menu.component.ts</context>
10490 <context context-type="linenumber">193</context> 10557 <context context-type="linenumber">196</context>
10491 </context-group> 10558 </context-group>
10492 </trans-unit> 10559 </trans-unit>
10493 <trans-unit id="7939914198003891823" datatype="html"> 10560 <trans-unit id="7939914198003891823" datatype="html">
10494 <source>any language</source> 10561 <source>any language</source>
10495 <context-group purpose="location"> 10562 <context-group purpose="location">
10496 <context context-type="sourcefile">src/app/menu/menu.component.ts</context> 10563 <context context-type="sourcefile">src/app/menu/menu.component.ts</context>
10497 <context context-type="linenumber">263</context> 10564 <context context-type="linenumber">266</context>
10498 </context-group> 10565 </context-group>
10499 </trans-unit> 10566 </trans-unit>
10500 <trans-unit id="5633144232269377096" datatype="html"> 10567 <trans-unit id="5633144232269377096" datatype="html">
10501 <source>hide</source> 10568 <source>hide</source>
10502 <context-group purpose="location"> 10569 <context-group purpose="location">
10503 <context context-type="sourcefile">src/app/menu/menu.component.ts</context> 10570 <context context-type="sourcefile">src/app/menu/menu.component.ts</context>
10504 <context context-type="linenumber">298</context> 10571 <context context-type="linenumber">301</context>
10505 </context-group> 10572 </context-group>
10506 </trans-unit> 10573 </trans-unit>
10507 <trans-unit id="8603861867909474404" datatype="html"> 10574 <trans-unit id="8603861867909474404" datatype="html">
10508 <source>blur</source> 10575 <source>blur</source>
10509 <context-group purpose="location"> 10576 <context-group purpose="location">
10510 <context context-type="sourcefile">src/app/menu/menu.component.ts</context> 10577 <context context-type="sourcefile">src/app/menu/menu.component.ts</context>
10511 <context context-type="linenumber">302</context> 10578 <context context-type="linenumber">305</context>
10512 </context-group> 10579 </context-group>
10513 </trans-unit> 10580 </trans-unit>
10514 <trans-unit id="4534458451100881847" datatype="html"> 10581 <trans-unit id="4534458451100881847" datatype="html">
10515 <source>display</source> 10582 <source>display</source>
10516 <context-group purpose="location"> 10583 <context-group purpose="location">
10517 <context context-type="sourcefile">src/app/menu/menu.component.ts</context> 10584 <context context-type="sourcefile">src/app/menu/menu.component.ts</context>
10518 <context context-type="linenumber">306</context> 10585 <context context-type="linenumber">309</context>
10519 </context-group> 10586 </context-group>
10520 </trans-unit> 10587 </trans-unit>
10521 <trans-unit id="403762424689874454" datatype="html"> 10588 <trans-unit id="403762424689874454" datatype="html">
@@ -10842,34 +10909,6 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10842 <context context-type="linenumber">27</context> 10909 <context context-type="linenumber">27</context>
10843 </context-group> 10910 </context-group>
10844 </trans-unit> 10911 </trans-unit>
10845 <trans-unit id="2740793005745065895" datatype="html">
10846 <source><x id="PH" equiv-text="host"/> is not valid</source>
10847 <context-group purpose="location">
10848 <context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context>
10849 <context context-type="linenumber">19</context>
10850 </context-group>
10851 </trans-unit>
10852 <trans-unit id="2127446333083057097" datatype="html">
10853 <source>Domain is required.</source>
10854 <context-group purpose="location">
10855 <context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context>
10856 <context context-type="linenumber">56</context>
10857 </context-group>
10858 </trans-unit>
10859 <trans-unit id="6780793142903080663" datatype="html">
10860 <source>Domains entered are invalid.</source>
10861 <context-group purpose="location">
10862 <context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context>
10863 <context context-type="linenumber">57</context>
10864 </context-group>
10865 </trans-unit>
10866 <trans-unit id="5886492514458202177" datatype="html">
10867 <source>Domains entered contain duplicates.</source>
10868 <context-group purpose="location">
10869 <context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context>
10870 <context context-type="linenumber">58</context>
10871 </context-group>
10872 </trans-unit>
10873 <trans-unit id="7784486624424057376" datatype="html"> 10912 <trans-unit id="7784486624424057376" datatype="html">
10874 <source>Instance name is required.</source> 10913 <source>Instance name is required.</source>
10875 <context-group purpose="location"> 10914 <context-group purpose="location">
@@ -11073,6 +11112,56 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
11073 <context context-type="linenumber">119</context> 11112 <context context-type="linenumber">119</context>
11074 </context-group> 11113 </context-group>
11075 </trans-unit> 11114 </trans-unit>
11115 <trans-unit id="2740793005745065895" datatype="html">
11116 <source><x id="PH" equiv-text="host"/> is not valid</source>
11117 <context-group purpose="location">
11118 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
11119 <context context-type="linenumber">27</context>
11120 </context-group>
11121 <context-group purpose="location">
11122 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
11123 <context context-type="linenumber">50</context>
11124 </context-group>
11125 </trans-unit>
11126 <trans-unit id="2127446333083057097" datatype="html">
11127 <source>Domain is required.</source>
11128 <context-group purpose="location">
11129 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
11130 <context context-type="linenumber">92</context>
11131 </context-group>
11132 <context-group purpose="location">
11133 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
11134 <context context-type="linenumber">101</context>
11135 </context-group>
11136 </trans-unit>
11137 <trans-unit id="7951488350851416577" datatype="html">
11138 <source>Hosts entered are invalid.</source>
11139 <context-group purpose="location">
11140 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
11141 <context context-type="linenumber">93</context>
11142 </context-group>
11143 </trans-unit>
11144 <trans-unit id="1469559036084108672" datatype="html">
11145 <source>Hosts entered contain duplicates.</source>
11146 <context-group purpose="location">
11147 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
11148 <context context-type="linenumber">94</context>
11149 </context-group>
11150 </trans-unit>
11151 <trans-unit id="5991533283446904296" datatype="html">
11152 <source>Hosts or handles are invalid.</source>
11153 <context-group purpose="location">
11154 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
11155 <context context-type="linenumber">102</context>
11156 </context-group>
11157 </trans-unit>
11158 <trans-unit id="6759198394434886237" datatype="html">
11159 <source>Hosts or handles contain duplicates.</source>
11160 <context-group purpose="location">
11161 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
11162 <context context-type="linenumber">103</context>
11163 </context-group>
11164 </trans-unit>
11076 <trans-unit id="8602814243662345124" datatype="html"> 11165 <trans-unit id="8602814243662345124" datatype="html">
11077 <source>Email is required.</source> 11166 <source>Email is required.</source>
11078 <context-group purpose="location"> 11167 <context-group purpose="location">
diff --git a/client/src/locale/angular.zh-Hans-CN.xlf b/client/src/locale/angular.zh-Hans-CN.xlf
index 3a6c603f8..1f22387b7 100644
--- a/client/src/locale/angular.zh-Hans-CN.xlf
+++ b/client/src/locale/angular.zh-Hans-CN.xlf
@@ -173,7 +173,7 @@
173 173
174 174
175 175
176 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit> 176 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
177 <trans-unit id="1486537403020619891" datatype="html"> 177 <trans-unit id="1486537403020619891" datatype="html">
178 <source>My watch history</source> 178 <source>My watch history</source>
179 <target state="translated">我的历史记录</target> 179 <target state="translated">我的历史记录</target>
@@ -242,23 +242,23 @@
242 <trans-unit id="5674286808255988565"> 242 <trans-unit id="5674286808255988565">
243 <source>Create</source> 243 <source>Create</source>
244 <target>创建</target> 244 <target>创建</target>
245 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 245
246 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 246
247 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 247
248 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 248
249 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 249
250 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 250
251 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 251
252 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 252
253 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 253
254 </trans-unit> 254 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
255 <trans-unit id="1006562256968398209" datatype="html"> 255 <trans-unit id="1006562256968398209" datatype="html">
256 <source>video</source> 256 <source>video</source>
257 <target state="translated">视频</target> 257 <target state="translated">视频</target>
258 258
259 259
260 260
261 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit> 261 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
262 <trans-unit id="6438815964972582865" datatype="html"> 262 <trans-unit id="6438815964972582865" datatype="html">
263 <source>The following link contains a private token and should not be shared with anyone.</source> 263 <source>The following link contains a private token and should not be shared with anyone.</source>
264 <target state="new"> The following link contains a private token and should not be shared with anyone. </target> 264 <target state="new"> The following link contains a private token and should not be shared with anyone. </target>
@@ -331,13 +331,13 @@
331 <trans-unit id="6995024616159044376" datatype="html"> 331 <trans-unit id="6995024616159044376" datatype="html">
332 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 332 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
333 <target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 333 <target state="new">Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
334 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 334
335 </trans-unit> 335 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
336 <trans-unit id="7873395933409147217" datatype="html"> 336 <trans-unit id="7873395933409147217" datatype="html">
337 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 337 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
338 <target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 338 <target state="new">Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
339 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 339
340 </trans-unit> 340 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
341 <trans-unit id="5235042777215655908" datatype="html"> 341 <trans-unit id="5235042777215655908" datatype="html">
342 <source>subtitles</source> 342 <source>subtitles</source>
343 <target state="translated">字幕</target> 343 <target state="translated">字幕</target>
@@ -775,10 +775,10 @@
775 <trans-unit id="2602586221576511475"> 775 <trans-unit id="2602586221576511475">
776 <source>Video quota</source> 776 <source>Video quota</source>
777 <target>视频存储空间</target> 777 <target>视频存储空间</target>
778 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 778
779 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 779
780 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 780
781 </trans-unit> 781 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
782 <trans-unit id="1502595455339510144"> 782 <trans-unit id="1502595455339510144">
783 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 783 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
784 <target>无限制 <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> 每日)<x id="CLOSE_TAG_NG_CONTAINER"/></target> 784 <target>无限制 <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> 每日)<x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -858,6 +858,30 @@
858 <target state="translated">联盟</target> 858 <target state="translated">联盟</target>
859 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 859 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
860 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 860 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
861 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
862 <source>Following</source><target state="new">Following</target>
863 <context-group purpose="location">
864 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
865 <context context-type="linenumber">29</context>
866 </context-group>
867 <context-group purpose="location">
868 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
869 <context context-type="linenumber">31</context>
870 </context-group>
871 <context-group purpose="location">
872 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
873 <context context-type="linenumber">28</context>
874 </context-group>
875 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
876 <source>Followers</source><target state="new">Followers</target>
877 <context-group purpose="location">
878 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
879 <context context-type="linenumber">34</context>
880 </context-group>
881 <context-group purpose="location">
882 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
883 <context context-type="linenumber">37</context>
884 </context-group>
861 </trans-unit> 885 </trans-unit>
862 <trans-unit id="3541687134897970106" datatype="html"> 886 <trans-unit id="3541687134897970106" datatype="html">
863 <source>followers</source> 887 <source>followers</source>
@@ -939,7 +963,7 @@
939 963
940 964
941 965
942 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit> 966 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
943 <trans-unit id="3616223838716839702"> 967 <trans-unit id="3616223838716839702">
944 <source>Ban this user</source> 968 <source>Ban this user</source>
945 <target>封禁此用户</target> 969 <target>封禁此用户</target>
@@ -1005,19 +1029,13 @@
1005 <trans-unit id="7252854992688790751" datatype="html"> 1029 <trans-unit id="7252854992688790751" datatype="html">
1006 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1030 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1007 <target state="translated">此实例允许注册。但是,请小心检查 <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>条款<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>条款<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> 你也可以通过以下链接搜寻其他服务,以配合你的实际需要: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1031 <target state="translated">此实例允许注册。但是,请小心检查 <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>条款<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>条款<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> 你也可以通过以下链接搜寻其他服务,以配合你的实际需要: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1008 <context-group purpose="location"> 1032
1009 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1033 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1010 <context context-type="linenumber">60,62</context>
1011 </context-group>
1012 </trans-unit>
1013 <trans-unit id="7215649348148521605" datatype="html"> 1034 <trans-unit id="7215649348148521605" datatype="html">
1014 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1035 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1015 <target state="translated">目前此实例不允许用户注册,您可以检查 <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>条款<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> 以了解详情,或寻找别的站台,好让您注册帐号并上传您的视频。看看一众站台中有哪个合您心意: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target> 1036 <target state="translated">目前此实例不允许用户注册,您可以检查 <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>条款<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> 以了解详情,或寻找别的站台,好让您注册帐号并上传您的视频。看看一众站台中有哪个合您心意: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </target>
1016 <context-group purpose="location"> 1037
1017 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1038 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1018 <context context-type="linenumber">65,67</context>
1019 </context-group>
1020 </trans-unit>
1021 <trans-unit id="2392488717875840729"> 1039 <trans-unit id="2392488717875840729">
1022 <source>User</source> 1040 <source>User</source>
1023 <target>用户</target> 1041 <target>用户</target>
@@ -1028,67 +1046,67 @@
1028 <source>Username or email address</source> 1046 <source>Username or email address</source>
1029 <target>用户名或电子邮件地址</target> 1047 <target>用户名或电子邮件地址</target>
1030 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1048 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1049 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1050 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1051 <context-group purpose="location">
1052 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1053 <context context-type="linenumber">33,34</context>
1054 </context-group>
1031 </trans-unit> 1055 </trans-unit>
1032 <trans-unit id="1431416938026210429"> 1056 <trans-unit id="1431416938026210429">
1033 <source>Password</source> 1057 <source>Password</source>
1034 <target>密码</target> 1058 <target>密码</target>
1035 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1059
1036 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1060
1037 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1061
1038 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1062
1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1063
1040 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1064
1041 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1065
1042 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1066
1043 </trans-unit> 1067 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1044 <trans-unit id="8715156686857791956" datatype="html"> 1068 <trans-unit id="8715156686857791956" datatype="html">
1045 <source>Click here to reset your password</source> 1069 <source>Click here to reset your password</source>
1046 <target state="translated">点击此处重置您的密码</target> 1070 <target state="translated">点击此处重置您的密码</target>
1047 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1071
1048 </trans-unit> 1072 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1049 <trans-unit id="892063502898494584" datatype="html"> 1073 <trans-unit id="892063502898494584" datatype="html">
1050 <source>I forgot my password</source> 1074 <source>I forgot my password</source>
1051 <target state="translated">我忘记了我 密码</target> 1075 <target state="translated">我忘记了我 密码</target>
1052 <context-group purpose="location"> 1076
1053 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1077 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1054 <context context-type="linenumber">47</context>
1055 </context-group>
1056 </trans-unit>
1057 <trans-unit id="2101170466365500913" datatype="html"> 1078 <trans-unit id="2101170466365500913" datatype="html">
1058 <source>Logging into an account lets you publish content</source> 1079 <source>Logging into an account lets you publish content</source>
1059 <target state="translated">登录帐户就可以让您发布内容</target> 1080 <target state="translated">登录帐户就可以让您发布内容</target>
1060 <context-group purpose="location"> 1081
1061 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1082 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1062 <context context-type="linenumber">56,57</context>
1063 </context-group>
1064 </trans-unit>
1065 <trans-unit id="2454050363478003966"> 1083 <trans-unit id="2454050363478003966">
1066 <source>Login</source> 1084 <source>Login</source>
1067 <target>登录</target> 1085 <target>登录</target>
1068 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1086
1069 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1087
1070 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1088
1071 </trans-unit> 1089 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1072 <trans-unit id="3183213940445113677" datatype="html"> 1090 <trans-unit id="3183213940445113677" datatype="html">
1073 <source>Or sign in with</source> 1091 <source>Or sign in with</source>
1074 <target state="translated">或使用其他账户登入</target> 1092 <target state="translated">或使用其他账户登入</target>
1075 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1093
1076 </trans-unit> 1094 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1077 <trans-unit id="3238209155172574367"> 1095 <trans-unit id="3238209155172574367">
1078 <source>Forgot your password</source> 1096 <source>Forgot your password</source>
1079 <target>忘记密码</target> 1097 <target>忘记密码</target>
1080 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1098
1081 </trans-unit> 1099 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1082 <trans-unit id="87327320394367488" datatype="html"> 1100 <trans-unit id="87327320394367488" datatype="html">
1083 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1101 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1084 <target state="translated">对不起,您无法恢复您的密码,因为您的实例管理员没有配置 PeerTube 电子邮件系统。</target> 1102 <target state="translated">对不起,您无法恢复您的密码,因为您的实例管理员没有配置 PeerTube 电子邮件系统。</target>
1085 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1103
1086 </trans-unit> 1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1087 <trans-unit id="3188014010833256853" datatype="html"> 1105 <trans-unit id="3188014010833256853" datatype="html">
1088 <source>Enter your email address and we will send you a link to reset your password.</source> 1106 <source>Enter your email address and we will send you a link to reset your password.</source>
1089 <target state="translated">输入您的电子邮件地址,我们将发送一个链接,以重置您的密码。</target> 1107 <target state="translated">输入您的电子邮件地址,我们将发送一个链接,以重置您的密码。</target>
1090 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1108
1091 </trans-unit> 1109 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1092 <trans-unit id="1190256911880544559" datatype="html"> 1110 <trans-unit id="1190256911880544559" datatype="html">
1093 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1111 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1094The link will expire within 1 hour.</source> 1112The link will expire within 1 hour.</source>
@@ -1098,26 +1116,26 @@ The link will expire within 1 hour.</source>
1098 <trans-unit id="4768749765465246664"> 1116 <trans-unit id="4768749765465246664">
1099 <source>Email</source> 1117 <source>Email</source>
1100 <target>电子邮件地址</target> 1118 <target>电子邮件地址</target>
1101 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1119
1102 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1120
1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1121
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1122
1105 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1123
1106 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1124
1107 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1125
1108 </trans-unit> 1126 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1109 <trans-unit id="3967269098753656610"> 1127 <trans-unit id="3967269098753656610">
1110 <source>Email address</source> 1128 <source>Email address</source>
1111 <target>电子邮件地址</target> 1129 <target>电子邮件地址</target>
1112 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1130
1113 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1131
1114 </trans-unit> 1132 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1115 <trans-unit id="7808756054397155068" datatype="html"> 1133 <trans-unit id="7808756054397155068" datatype="html">
1116 <source>Reset</source> 1134 <source>Reset</source>
1117 <target state="translated">重设</target> 1135 <target state="translated">重设</target>
1118 <note priority="1" from="description">Password reset button</note> 1136 <note priority="1" from="description">Password reset button</note>
1119 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1137
1120 </trans-unit> 1138 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1121 <trans-unit id="4319634264526091601" datatype="html"> 1139 <trans-unit id="4319634264526091601" datatype="html">
1122 <source>on this instance</source> 1140 <source>on this instance</source>
1123 <target state="translated">在此网站</target> 1141 <target state="translated">在此网站</target>
@@ -1446,9 +1464,9 @@ The link will expire within 1 hour.</source>
1446 <trans-unit id="2308975396733519902"> 1464 <trans-unit id="2308975396733519902">
1447 <source>Create an account</source> 1465 <source>Create an account</source>
1448 <target>创建帐户</target> 1466 <target>创建帐户</target>
1449 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1467
1450 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1468
1451 </trans-unit> 1469 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1452 <trans-unit id="3058024914967508975" datatype="html"> 1470 <trans-unit id="3058024914967508975" datatype="html">
1453 <source>My videos</source> 1471 <source>My videos</source>
1454 <target state="translated">我的视频</target> 1472 <target state="translated">我的视频</target>
@@ -1515,10 +1533,10 @@ The link will expire within 1 hour.</source>
1515 <trans-unit id="1504521795586863905" datatype="html"> 1533 <trans-unit id="1504521795586863905" datatype="html">
1516 <source>VIDEOS</source> 1534 <source>VIDEOS</source>
1517 <target state="translated">视频</target> 1535 <target state="translated">视频</target>
1518 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1536
1519 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1537
1520 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1538
1521 </trans-unit> 1539 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1522 <trans-unit id="667372110624203230" datatype="html"> 1540 <trans-unit id="667372110624203230" datatype="html">
1523 <source>Import jobs concurrency</source> 1541 <source>Import jobs concurrency</source>
1524 <target state="translated">导入并发送</target> 1542 <target state="translated">导入并发送</target>
@@ -1596,8 +1614,8 @@ The link will expire within 1 hour.</source>
1596 <trans-unit id="4424964105331349857" datatype="html"> 1614 <trans-unit id="4424964105331349857" datatype="html">
1597 <source>I'm a teapot</source> 1615 <source>I'm a teapot</source>
1598 <target state="translated">我是茶壶</target> 1616 <target state="translated">我是茶壶</target>
1599 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1617
1600 </trans-unit> 1618 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1601 <trans-unit id="1597262876035959248" datatype="html"> 1619 <trans-unit id="1597262876035959248" datatype="html">
1602 <source>That's an error.</source> 1620 <source>That's an error.</source>
1603 <target state="translated">发生错误.</target> 1621 <target state="translated">发生错误.</target>
@@ -1690,8 +1708,8 @@ The link will expire within 1 hour.</source>
1690 <trans-unit id="2971365540217107489" datatype="html"> 1708 <trans-unit id="2971365540217107489" datatype="html">
1691 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1709 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1692 <target state="translated">媒体对于服务器太大。如果您想增加限制大小,请与管理员联系。</target> 1710 <target state="translated">媒体对于服务器太大。如果您想增加限制大小,请与管理员联系。</target>
1693 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1711
1694 </trans-unit> 1712 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1695 1713
1696 <trans-unit id="5131854469652959713" datatype="html"> 1714 <trans-unit id="5131854469652959713" datatype="html">
1697 <source>GLOBAL SEARCH</source> 1715 <source>GLOBAL SEARCH</source>
@@ -2434,8 +2452,8 @@ The link will expire within 1 hour.</source>
2434 <trans-unit id="6161604372916832458" datatype="html"> 2452 <trans-unit id="6161604372916832458" datatype="html">
2435 <source>Upload on hold</source> 2453 <source>Upload on hold</source>
2436 <target state="new">Upload on hold</target> 2454 <target state="new">Upload on hold</target>
2437 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2455
2438 </trans-unit> 2456 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2439 <trans-unit id="285180972645018518" datatype="html"> 2457 <trans-unit id="285180972645018518" datatype="html">
2440 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2458 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2441 <target state="translated">对不起,您的帐户的上传功能已被禁用。如果你想添加视频,管理员必须解锁您的权限。</target> 2459 <target state="translated">对不起,您的帐户的上传功能已被禁用。如果你想添加视频,管理员必须解锁您的权限。</target>
@@ -3108,11 +3126,7 @@ The link will expire within 1 hour.</source>
3108 <target>ID</target> 3126 <target>ID</target>
3109 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3127 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3110 </trans-unit> 3128 </trans-unit>
3111 <trans-unit id="2265605798180116441"> 3129
3112 <source>Follower handle</source>
3113 <target>管理关注</target>
3114 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3115 </trans-unit>
3116 <trans-unit id="5911214550882917183"> 3130 <trans-unit id="5911214550882917183">
3117 <source>State</source> 3131 <source>State</source>
3118 <target>状态</target> 3132 <target>状态</target>
@@ -3183,11 +3197,7 @@ The link will expire within 1 hour.</source>
3183 </target> 3197 </target>
3184 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3198 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3185 </trans-unit> 3199 </trans-unit>
3186 <trans-unit id="6641024648411549335"> 3200
3187 <source>Host</source>
3188 <target>主机名</target>
3189 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3190 </trans-unit>
3191 <trans-unit id="6571718060636962350" datatype="html"> 3201 <trans-unit id="6571718060636962350" datatype="html">
3192 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3202 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3193 <target state="translated">允许冗余 <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 3203 <target state="translated">允许冗余 <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -3196,9 +3206,9 @@ The link will expire within 1 hour.</source>
3196 <trans-unit id="9160510009013134726" datatype="html"> 3206 <trans-unit id="9160510009013134726" datatype="html">
3197 <source>Unfollow</source> 3207 <source>Unfollow</source>
3198 <target state="translated">取消关注</target> 3208 <target state="translated">取消关注</target>
3199 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3209
3200 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3210
3201 </trans-unit> 3211 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3202 <trans-unit id="8246779176913476983" datatype="html"> 3212 <trans-unit id="8246779176913476983" datatype="html">
3203 <source>Open instance in a new tab</source> 3213 <source>Open instance in a new tab</source>
3204 <target state="translated">在新选项卡中打开页面</target> 3214 <target state="translated">在新选项卡中打开页面</target>
@@ -3209,28 +3219,20 @@ The link will expire within 1 hour.</source>
3209 <trans-unit id="9132918641931433659" datatype="html"> 3219 <trans-unit id="9132918641931433659" datatype="html">
3210 <source>No host found matching current filters.</source> 3220 <source>No host found matching current filters.</source>
3211 <target state="translated">没有找到匹配当前筛选器的主机。</target> 3221 <target state="translated">没有找到匹配当前筛选器的主机。</target>
3212 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3222
3213 </trans-unit> 3223 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3214 <trans-unit id="7274241885665071790" datatype="html"> 3224 <trans-unit id="7274241885665071790" datatype="html">
3215 <source>Your instance is not following anyone.</source> 3225 <source>Your instance is not following anyone.</source>
3216 <target state="translated">您的站点没有跟踪任何人。</target> 3226 <target state="translated">您的站点没有跟踪任何人。</target>
3217 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3227
3218 </trans-unit> 3228 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3219 <trans-unit id="4774348799569692380" datatype="html"> 3229 <trans-unit id="4774348799569692380" datatype="html">
3220 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3230 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3221 <target state="translated">正在观看 <x id="INTERPOLATION"/> 到 <x id="INTERPOLATION_1"/> 总共有 <x id="INTERPOLATION_2"/> 个主机</target> 3231 <target state="translated">正在观看 <x id="INTERPOLATION"/> 到 <x id="INTERPOLATION_1"/> 总共有 <x id="INTERPOLATION_2"/> 个主机</target>
3222 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3232 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3223 </trans-unit> 3233 </trans-unit>
3224 <trans-unit id="6275803119759621687" datatype="html"> 3234
3225 <source>Follow domains</source> 3235
3226 <target state="translated">关注域名</target>
3227 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3228 </trans-unit>
3229 <trans-unit id="1268699198448750610" datatype="html">
3230 <source>Follow instances</source>
3231 <target state="translated">跟踪站点</target>
3232 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3233 </trans-unit>
3234 <trans-unit id="9216117865911519658" datatype="html"> 3236 <trans-unit id="9216117865911519658" datatype="html">
3235 <source>Action</source> 3237 <source>Action</source>
3236 <target state="new">Action</target> 3238 <target state="new">Action</target>
@@ -3280,11 +3282,11 @@ The link will expire within 1 hour.</source>
3280 <trans-unit id="5248717555542428023"> 3282 <trans-unit id="5248717555542428023">
3281 <source>Username</source> 3283 <source>Username</source>
3282 <target>用户名</target> 3284 <target>用户名</target>
3283 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3285
3284 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3286
3285 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3287
3286 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3288
3287 </trans-unit> 3289 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3288 <trans-unit id="5428411040014095392" datatype="html"> 3290 <trans-unit id="5428411040014095392" datatype="html">
3289 <source>e.g. jane_doe</source> 3291 <source>e.g. jane_doe</source>
3290 <target state="translated">例如: jane_doe</target> 3292 <target state="translated">例如: jane_doe</target>
@@ -3312,9 +3314,9 @@ The link will expire within 1 hour.</source>
3312 <trans-unit id="4145496584631696119"> 3314 <trans-unit id="4145496584631696119">
3313 <source>Role</source> 3315 <source>Role</source>
3314 <target>角色</target> 3316 <target>角色</target>
3315 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3317
3316 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3318
3317 </trans-unit> 3319 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3318 <trans-unit id="7046347992315328430" datatype="html"> 3320 <trans-unit id="7046347992315328430" datatype="html">
3319 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3321 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3320 <target state="translated">已启用转码。视频配额仅考虑在内 <x id="START_TAG_STRONG"/>原始<x id="CLOSE_TAG_STRONG"/> 视频大小。 <x id="LINE_BREAK"/> 这个用户最多可以上传 ~ <x id="INTERPOLATION"/>. </target> 3322 <target state="translated">已启用转码。视频配额仅考虑在内 <x id="START_TAG_STRONG"/>原始<x id="CLOSE_TAG_STRONG"/> 视频大小。 <x id="LINE_BREAK"/> 这个用户最多可以上传 ~ <x id="INTERPOLATION"/>. </target>
@@ -3331,15 +3333,9 @@ The link will expire within 1 hour.</source>
3331 <trans-unit id="2622255144026150901" datatype="html"> 3333 <trans-unit id="2622255144026150901" datatype="html">
3332 <source>Auth plugin</source> 3334 <source>Auth plugin</source>
3333 <target state="new">Auth plugin</target> 3335 <target state="new">Auth plugin</target>
3334 <context-group purpose="location"> 3336
3335 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3337
3336 <context context-type="linenumber">188</context> 3338 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3337 </context-group>
3338 <context-group purpose="location">
3339 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3340 <context context-type="linenumber">188</context>
3341 </context-group>
3342 </trans-unit>
3343 <trans-unit id="588099657508661970" datatype="html"> 3339 <trans-unit id="588099657508661970" datatype="html">
3344 <source>None (local authentication)</source> 3340 <source>None (local authentication)</source>
3345 <target state="translated">None (本地身份验证)</target> 3341 <target state="translated">None (本地身份验证)</target>
@@ -3593,6 +3589,12 @@ The link will expire within 1 hour.</source>
3593 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3589 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3594 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3590 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3595 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3591 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3592 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3593 <source>Follower</source><target state="new">Follower</target>
3594 <context-group purpose="location">
3595 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3596 <context context-type="linenumber">24</context>
3597 </context-group>
3596 </trans-unit> 3598 </trans-unit>
3597 <trans-unit id="4691552465058437520" datatype="html"> 3599 <trans-unit id="4691552465058437520" datatype="html">
3598 <source>Commented video</source> 3600 <source>Commented video</source>
@@ -3899,8 +3901,8 @@ The link will expire within 1 hour.</source>
3899 <trans-unit id="4917252294930256268" datatype="html"> 3901 <trans-unit id="4917252294930256268" datatype="html">
3900 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3902 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3901 <target state="translated">看起来你不在 HTTPS 服务器上。你的网络服务器需要有 TLS 激活,以便跟踪服务器。</target> 3903 <target state="translated">看起来你不在 HTTPS 服务器上。你的网络服务器需要有 TLS 激活,以便跟踪服务器。</target>
3902 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 3904
3903 </trans-unit> 3905 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3904 <trans-unit id="4058814854824495833" datatype="html"> 3906 <trans-unit id="4058814854824495833" datatype="html">
3905 <source>Mute domains</source> 3907 <source>Mute domains</source>
3906 <target state="translated">静音网络</target> 3908 <target state="translated">静音网络</target>
@@ -5902,11 +5904,8 @@ color: red;
5902 <trans-unit id="5512878593724620692" datatype="html"> 5904 <trans-unit id="5512878593724620692" datatype="html">
5903 <source>CHANNELS</source> 5905 <source>CHANNELS</source>
5904 <target state="new">CHANNELS</target> 5906 <target state="new">CHANNELS</target>
5905 <context-group purpose="location"> 5907
5906 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5908 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5907 <context context-type="linenumber">82</context>
5908 </context-group>
5909 </trans-unit>
5910 <trans-unit id="3666829335406793239"> 5909 <trans-unit id="3666829335406793239">
5911 <source>This account does not have channels.</source> 5910 <source>This account does not have channels.</source>
5912 <target>此帐户没有视频频道。</target> 5911 <target>此帐户没有视频频道。</target>
@@ -5951,6 +5950,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
5951It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another 5950It will delete <x id="PH_1"/> videos uploaded in this channel, and you will not be able to create another
5952channel with the same name (<x id="PH_2"/>)!</target> 5951channel with the same name (<x id="PH_2"/>)!</target>
5953 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 5952 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
5953 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
5954 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5955 <context-group purpose="location">
5956 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5957 <context context-type="linenumber">48</context>
5958 </context-group>
5954 </trans-unit> 5959 </trans-unit>
5955 <trans-unit id="5387007581996837469" datatype="html"> 5960 <trans-unit id="5387007581996837469" datatype="html">
5956 <source>My Channels</source> 5961 <source>My Channels</source>
@@ -6494,12 +6499,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6494 <source>Your message has been sent.</source> 6499 <source>Your message has been sent.</source>
6495 <target>您的信息已发送。</target> 6500 <target>您的信息已发送。</target>
6496 6501
6497 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group></trans-unit> 6502 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6498 <trans-unit id="2072135752262464360"> 6503 <trans-unit id="2072135752262464360">
6499 <source>You already sent this form recently</source> 6504 <source>You already sent this form recently</source>
6500 <target>您最近已发送了此表格</target> 6505 <target>您最近已发送了此表格</target>
6501 6506
6502 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group></trans-unit> 6507 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6503 <trans-unit id="819067926858619041" datatype="html"> 6508 <trans-unit id="819067926858619041" datatype="html">
6504 <source>Account videos</source> 6509 <source>Account videos</source>
6505 <target state="new">Account videos</target> 6510 <target state="new">Account videos</target>
@@ -6546,13 +6551,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
6546 <target state="new"> 6551 <target state="new">
6547 <x id="PH"/> direct account followers 6552 <x id="PH"/> direct account followers
6548 </target> 6553 </target>
6549 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6554
6550 </trans-unit> 6555 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6551 <trans-unit id="6250999352462648289" datatype="html"> 6556 <trans-unit id="6250999352462648289" datatype="html">
6552 <source>Report this account</source> 6557 <source>Report this account</source>
6553 <target state="new">Report this account</target> 6558 <target state="new">Report this account</target>
6554 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6559
6555 </trans-unit> 6560 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6556 <trans-unit id="1504521795586863905" datatype="html"> 6561 <trans-unit id="1504521795586863905" datatype="html">
6557 <source>VIDEOS</source> 6562 <source>VIDEOS</source>
6558 <target state="new">VIDEOS</target> 6563 <target state="new">VIDEOS</target>
@@ -6562,31 +6567,21 @@ channel with the same name (<x id="PH_2"/>)!</target>
6562 <trans-unit id="25349740244798533"> 6567 <trans-unit id="25349740244798533">
6563 <source>Username copied</source> 6568 <source>Username copied</source>
6564 <target>用户名已复制</target> 6569 <target>用户名已复制</target>
6565 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6570
6566 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6571
6567 </trans-unit> 6572 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6568 <trans-unit id="9221735175659318025" datatype="html"> 6573 <trans-unit id="9221735175659318025" datatype="html">
6569 <source>1 subscriber</source> 6574 <source>1 subscriber</source>
6570 <target state="new">1 subscriber</target> 6575 <target state="new">1 subscriber</target>
6571 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6576
6572 </trans-unit> 6577 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6573 <trans-unit id="4097331874769079975" datatype="html"> 6578 <trans-unit id="4097331874769079975" datatype="html">
6574 <source><x id="PH"/> subscribers</source> 6579 <source><x id="PH"/> subscribers</source>
6575 <target state="new"><x id="PH"/> subscribers</target> 6580 <target state="new"><x id="PH"/> subscribers</target>
6576 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6581
6577 </trans-unit> 6582 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6578 <trans-unit id="4682675125751819107" datatype="html"> 6583
6579 <source>Instances you follow</source> 6584
6580 <target state="new">Instances you follow</target>
6581 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6582 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6583 </trans-unit>
6584 <trans-unit id="8899833753704589712" datatype="html">
6585 <source>Instances following you</source>
6586 <target state="new">Instances following you</target>
6587 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6588 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6589 </trans-unit>
6590 <trans-unit id="1035838766454786107" datatype="html"> 6585 <trans-unit id="1035838766454786107" datatype="html">
6591 <source>Audio-only</source> 6586 <source>Audio-only</source>
6592 <target state="new">Audio-only</target> 6587 <target state="new">Audio-only</target>
@@ -6636,6 +6631,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
6636 <source>Auto (via ffmpeg)</source> 6631 <source>Auto (via ffmpeg)</source>
6637 <target>自动(由 ffmpeg 决定)</target> 6632 <target>自动(由 ffmpeg 决定)</target>
6638 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6633 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6634 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6635 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6636 <context-group purpose="location">
6637 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6638 <context context-type="linenumber">3</context>
6639 </context-group>
6639 </trans-unit> 6640 </trans-unit>
6640 <trans-unit id="931255636742351800" datatype="html"> 6641 <trans-unit id="931255636742351800" datatype="html">
6641 <source>No limit</source> 6642 <source>No limit</source>
@@ -6786,18 +6787,34 @@ channel with the same name (<x id="PH_2"/>)!</target>
6786 <trans-unit id="2127446333083057097" datatype="html"> 6787 <trans-unit id="2127446333083057097" datatype="html">
6787 <source>Domain is required.</source> 6788 <source>Domain is required.</source>
6788 <target state="new">Domain is required.</target> 6789 <target state="new">Domain is required.</target>
6789 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6790
6790 </trans-unit> 6791 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6791 <trans-unit id="6780793142903080663" datatype="html"> 6792 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6792 <source>Domains entered are invalid.</source> 6793 <context-group purpose="location">
6793 <target state="new">Domains entered are invalid.</target> 6794 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6794 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6795 <context context-type="linenumber">93</context>
6795 </trans-unit> 6796 </context-group>
6796 <trans-unit id="5886492514458202177" datatype="html"> 6797 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6797 <source>Domains entered contain duplicates.</source> 6798 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6798 <target state="new">Domains entered contain duplicates.</target> 6799 <context-group purpose="location">
6799 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6800 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6801 <context context-type="linenumber">94</context>
6802 </context-group>
6803 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6804 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6805 <context-group purpose="location">
6806 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6807 <context context-type="linenumber">102</context>
6808 </context-group>
6809 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6810 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6811 <context-group purpose="location">
6812 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6813 <context context-type="linenumber">103</context>
6814 </context-group>
6800 </trans-unit> 6815 </trans-unit>
6816
6817
6801 <trans-unit id="240806681889331244"> 6818 <trans-unit id="240806681889331244">
6802 <source>Unlimited</source> 6819 <source>Unlimited</source>
6803 <target>无限制 6820 <target>无限制
@@ -6958,26 +6975,52 @@ channel with the same name (<x id="PH_2"/>)!</target>
6958 <x id="PH"/> 已被移除出关注列表 6975 <x id="PH"/> 已被移除出关注列表
6959 </target> 6976 </target>
6960 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6977 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6978 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6979 <source>Follow</source><target state="new">Follow</target>
6980 <context-group purpose="location">
6981 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6982 <context context-type="linenumber">3</context>
6983 </context-group>
6984 <context-group purpose="location">
6985 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6986 <context context-type="linenumber">37</context>
6987 </context-group>
6988 <context-group purpose="location">
6989 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6990 <context context-type="linenumber">18</context>
6991 </context-group>
6992 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6993 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6994 <context-group purpose="location">
6995 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6996 <context context-type="linenumber">11</context>
6997 </context-group>
6961 </trans-unit> 6998 </trans-unit>
6962 <trans-unit id="2740793005745065895"> 6999 <trans-unit id="2740793005745065895">
6963 <source><x id="PH"/> is not valid </source> 7000 <source><x id="PH"/> is not valid </source>
6964 <target> 7001 <target>
6965 <x id="PH"/> 不合法 7002 <x id="PH"/> 不合法
6966 </target> 7003 </target>
6967 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 7004
6968 </trans-unit> 7005 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6969 <trans-unit id="2355066641781598196"> 7006 <trans-unit id="2355066641781598196">
6970 <source>Follow request(s) sent!</source> 7007 <source>Follow request(s) sent!</source>
6971 <target>关注请求已发送!</target> 7008 <target>关注请求已发送!</target>
6972 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 7009
7010 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
7011 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
7012 <context-group purpose="location">
7013 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
7014 <context context-type="linenumber">3</context>
7015 </context-group>
6973 </trans-unit> 7016 </trans-unit>
6974 <trans-unit id="4245720728052819482"> 7017 <trans-unit id="4245720728052819482">
6975 <source>Do you really want to unfollow <x id="PH"/>?</source> 7018 <source>Do you really want to unfollow <x id="PH"/>?</source>
6976 <target>您确定要取消关注 7019 <target>您确定要取消关注
6977 <x id="PH"/> 吗? 7020 <x id="PH"/> 吗?
6978 </target> 7021 </target>
6979 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 7022
6980 </trans-unit> 7023 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6981 <trans-unit id="9160510009013134726"> 7024 <trans-unit id="9160510009013134726">
6982 <source>Unfollow</source> 7025 <source>Unfollow</source>
6983 <target>取消关注</target> 7026 <target>取消关注</target>
@@ -6988,8 +7031,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
6988 <target>您已不再关注 7031 <target>您已不再关注
6989 <x id="PH"/>。 7032 <x id="PH"/>。
6990 </target> 7033 </target>
6991 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 7034
6992 </trans-unit> 7035 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6993 <trans-unit id="2593763089859685916"> 7036 <trans-unit id="2593763089859685916">
6994 <source>enabled</source> 7037 <source>enabled</source>
6995 <target>已启用</target> 7038 <target>已启用</target>
@@ -7480,9 +7523,9 @@ channel with the same name (<x id="PH_2"/>)!</target>
7480 <trans-unit id="1519954996184640001"> 7523 <trans-unit id="1519954996184640001">
7481 <source>Error</source> 7524 <source>Error</source>
7482 <target>错误</target> 7525 <target>错误</target>
7483 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7526
7484 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7527
7485 </trans-unit> 7528 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7486 <trans-unit id="5076187961693950167" datatype="html"> 7529 <trans-unit id="5076187961693950167" datatype="html">
7487 <source>Standard logs</source> 7530 <source>Standard logs</source>
7488 <target state="new">Standard logs</target> 7531 <target state="new">Standard logs</target>
@@ -7527,16 +7570,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7527 <target>更改用户密码</target> 7570 <target>更改用户密码</target>
7528 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7571 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7529 </trans-unit> 7572 </trans-unit>
7530 <trans-unit id="177544274549739411" datatype="html"> 7573
7531 <source>Following list</source> 7574
7532 <target state="new">Following list</target>
7533 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7534 </trans-unit>
7535 <trans-unit id="8092429110007204784" datatype="html">
7536 <source>Followers list</source>
7537 <target state="new">Followers list</target>
7538 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7539 </trans-unit>
7540 <trans-unit id="780323526182667308" datatype="html"> 7575 <trans-unit id="780323526182667308" datatype="html">
7541 <source>User <x id="PH"/> updated.</source> 7576 <source>User <x id="PH"/> updated.</source>
7542 <target state="new">User 7577 <target state="new">User
@@ -7576,16 +7611,8 @@ channel with the same name (<x id="PH_2"/>)!</target>
7576 <target state="new">Federation</target> 7611 <target state="new">Federation</target>
7577 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7612 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7578 </trans-unit> 7613 </trans-unit>
7579 <trans-unit id="4682675125751819107" datatype="html"> 7614
7580 <source>Instances you follow</source> 7615
7581 <target state="new">Instances you follow</target>
7582 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7583 </trans-unit>
7584 <trans-unit id="8899833753704589712" datatype="html">
7585 <source>Instances following you</source>
7586 <target state="new">Instances following you</target>
7587 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7588 </trans-unit>
7589 <trans-unit id="3767259920053407667" datatype="html"> 7616 <trans-unit id="3767259920053407667" datatype="html">
7590 <source>Videos will be deleted, comments will be tombstoned.</source> 7617 <source>Videos will be deleted, comments will be tombstoned.</source>
7591 <target state="new">Videos will be deleted, comments will be tombstoned.</target> 7618 <target state="new">Videos will be deleted, comments will be tombstoned.</target>
@@ -7616,6 +7643,24 @@ channel with the same name (<x id="PH_2"/>)!</target>
7616 <target>把电子邮件地址设为已验证</target> 7643 <target>把电子邮件地址设为已验证</target>
7617 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7644 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7618 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7645 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7646 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7647 <source>Created</source><target state="new">Created</target>
7648 <context-group purpose="location">
7649 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7650 <context context-type="linenumber">115</context>
7651 </context-group>
7652 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7653 <source>Daily quota</source><target state="new">Daily quota</target>
7654 <context-group purpose="location">
7655 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7656 <context context-type="linenumber">120</context>
7657 </context-group>
7658 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7659 <source>Last login</source><target state="new">Last login</target>
7660 <context-group purpose="location">
7661 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7662 <context context-type="linenumber">122</context>
7663 </context-group>
7619 </trans-unit> 7664 </trans-unit>
7620 <trans-unit id="3403978719736970622"> 7665 <trans-unit id="3403978719736970622">
7621 <source>You cannot ban root.</source> 7666 <source>You cannot ban root.</source>
@@ -7929,13 +7974,13 @@ channel with the same name (<x id="PH_2"/>)!</target>
7929 <target>视频频道 7974 <target>视频频道
7930 <x id="PH"/> 已创建。 7975 <x id="PH"/> 已创建。
7931 </target> 7976 </target>
7932 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 7977
7933 </trans-unit> 7978 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7934 <trans-unit id="8723777130353305761"> 7979 <trans-unit id="8723777130353305761">
7935 <source>This name already exists on this instance.</source> 7980 <source>This name already exists on this instance.</source>
7936 <target>此用户名在本实例上已经被使用过。</target> 7981 <target>此用户名在本实例上已经被使用过。</target>
7937 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 7982
7938 </trans-unit> 7983 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7939 <trans-unit id="7589345916094713536"> 7984 <trans-unit id="7589345916094713536">
7940 <source>Video channel <x id="PH"/> updated.</source> 7985 <source>Video channel <x id="PH"/> updated.</source>
7941 <target>视频频道 7986 <target>视频频道
@@ -7958,13 +8003,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
7958 <target state="new">Banner deleted.</target> 8003 <target state="new">Banner deleted.</target>
7959 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 8004 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
7960 </trans-unit> 8005 </trans-unit>
7961 <trans-unit id="2575302837003821736"> 8006
7962 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7963 <target>输入视频频道的显示名(
7964 <x id="PH"/>)以确认操作
7965 </target>
7966 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
7967 </trans-unit>
7968 <trans-unit id="624066830180032195"> 8007 <trans-unit id="624066830180032195">
7969 <source>Video channel <x id="PH"/> deleted.</source> 8008 <source>Video channel <x id="PH"/> deleted.</source>
7970 <target>视频频道 8009 <target>视频频道
@@ -8126,6 +8165,12 @@ channel with the same name (<x id="PH_2"/>)!</target>
8126 <source>Ownership change request sent.</source> 8165 <source>Ownership change request sent.</source>
8127 <target>视频转移请求已发送。</target> 8166 <target>视频转移请求已发送。</target>
8128 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8167 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8168 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8169 <source>Sort by</source><target state="new">Sort by</target>
8170 <context-group purpose="location">
8171 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8172 <context context-type="linenumber">26</context>
8173 </context-group>
8129 </trans-unit> 8174 </trans-unit>
8130 <trans-unit id="3245220240937722814"> 8175 <trans-unit id="3245220240937722814">
8131 <source>My channels</source> 8176 <source>My channels</source>
@@ -8228,7 +8273,7 @@ channel with the same name (<x id="PH_2"/>)!</target>
8228 <target>订阅此帐户</target> 8273 <target>订阅此帐户</target>
8229 8274
8230 8275
8231 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8276 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8232 <trans-unit id="3131904093925601441" datatype="html"> 8277 <trans-unit id="3131904093925601441" datatype="html">
8233 <source>PLAYLISTS</source> 8278 <source>PLAYLISTS</source>
8234 <target state="translated">播放列表</target> 8279 <target state="translated">播放列表</target>
@@ -8275,35 +8320,35 @@ channel with the same name (<x id="PH_2"/>)!</target>
8275 <trans-unit id="3779524668013120370"> 8320 <trans-unit id="3779524668013120370">
8276 <source>Go to my subscriptions</source> 8321 <source>Go to my subscriptions</source>
8277 <target>转到我的订阅</target> 8322 <target>转到我的订阅</target>
8278 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8323
8279 </trans-unit> 8324 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8280 <trans-unit id="1136469849928650779"> 8325 <trans-unit id="1136469849928650779">
8281 <source>Go to my videos</source> 8326 <source>Go to my videos</source>
8282 <target>转到我的视频</target> 8327 <target>转到我的视频</target>
8283 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8328
8284 </trans-unit> 8329 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8285 <trans-unit id="7836683738999600376"> 8330 <trans-unit id="7836683738999600376">
8286 <source>Go to my imports</source> 8331 <source>Go to my imports</source>
8287 <target>转到我的导入</target> 8332 <target>转到我的导入</target>
8288 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8333
8289 </trans-unit> 8334 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8290 <trans-unit id="7511292153332773503"> 8335 <trans-unit id="7511292153332773503">
8291 <source>Go to my channels</source> 8336 <source>Go to my channels</source>
8292 <target>转到我的频道</target> 8337 <target>转到我的频道</target>
8293 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8338
8294 </trans-unit> 8339 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8295 <trans-unit id="2013324644839511073" datatype="html"> 8340 <trans-unit id="2013324644839511073" datatype="html">
8296 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8341 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8297Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8342Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8298 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>. 8343 <target state="new">Cannot retrieve OAuth Client credentials: <x id="PH"/>.
8299Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target> 8344Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</target>
8300 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8345
8301 </trans-unit> 8346 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8302 <trans-unit id="375263728166936544"> 8347 <trans-unit id="375263728166936544">
8303 <source>You need to reconnect.</source> 8348 <source>You need to reconnect.</source>
8304 <target>请重新进行授权。</target> 8349 <target>请重新进行授权。</target>
8305 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8350
8306 </trans-unit> 8351 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8307 <trans-unit id="2206638022166154361"> 8352 <trans-unit id="2206638022166154361">
8308 <source>Keyboard Shortcuts:</source> 8353 <source>Keyboard Shortcuts:</source>
8309 <target>键盘快捷键:</target> 8354 <target>键盘快捷键:</target>
@@ -8316,6 +8361,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8316 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8361 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8317 <context context-type="linenumber">98</context> 8362 <context context-type="linenumber">98</context>
8318 </context-group> 8363 </context-group>
8364 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8365 <source>In my library</source><target state="new">In my library</target>
8366 <context-group purpose="location">
8367 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8368 <context context-type="linenumber">104</context>
8369 </context-group>
8319 </trans-unit> 8370 </trans-unit>
8320 <trans-unit id="232050922346936574" datatype="html"> 8371 <trans-unit id="232050922346936574" datatype="html">
8321 <source>Trending</source> 8372 <source>Trending</source>
@@ -8344,38 +8395,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8344 <trans-unit id="1266887509445371246"> 8395 <trans-unit id="1266887509445371246">
8345 <source>Incorrect username or password.</source> 8396 <source>Incorrect username or password.</source>
8346 <target>用户名或密码不正确。</target> 8397 <target>用户名或密码不正确。</target>
8347 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8398
8348 </trans-unit> 8399 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8349 <trans-unit id="6974874606619467663" datatype="html"> 8400 <trans-unit id="6974874606619467663" datatype="html">
8350 <source>Your account is blocked.</source> 8401 <source>Your account is blocked.</source>
8351 <target state="new">Your account is blocked.</target> 8402 <target state="new">Your account is blocked.</target>
8352 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8403
8353 </trans-unit> 8404 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8354 <trans-unit id="7939914198003891823" datatype="html"> 8405 <trans-unit id="7939914198003891823" datatype="html">
8355 <source>any language</source> 8406 <source>any language</source>
8356 <target state="new">any language</target> 8407 <target state="new">any language</target>
8357 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8408
8358 </trans-unit> 8409 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8359 <trans-unit id="5633144232269377096" datatype="html"> 8410 <trans-unit id="5633144232269377096" datatype="html">
8360 <source>hide</source> 8411 <source>hide</source>
8361 <target state="new">hide</target> 8412 <target state="new">hide</target>
8362 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8413
8363 </trans-unit> 8414 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8364 <trans-unit id="8603861867909474404" datatype="html"> 8415 <trans-unit id="8603861867909474404" datatype="html">
8365 <source>blur</source> 8416 <source>blur</source>
8366 <target state="new">blur</target> 8417 <target state="new">blur</target>
8367 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8418
8368 </trans-unit> 8419 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8369 <trans-unit id="4534458451100881847" datatype="html"> 8420 <trans-unit id="4534458451100881847" datatype="html">
8370 <source>display</source> 8421 <source>display</source>
8371 <target state="new">display</target> 8422 <target state="new">display</target>
8372 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8423
8373 </trans-unit> 8424 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8374 <trans-unit id="4467323362722952678" datatype="html"> 8425 <trans-unit id="4467323362722952678" datatype="html">
8375 <source>Unknown</source> 8426 <source>Unknown</source>
8376 <target state="new">Unknown</target> 8427 <target state="new">Unknown</target>
8377 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8428
8378 </trans-unit> 8429 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8379 <trans-unit id="8781423666414310853"> 8430 <trans-unit id="8781423666414310853">
8380 <source>Your password has been successfully reset!</source> 8431 <source>Your password has been successfully reset!</source>
8381 <target>密码重置成功!</target> 8432 <target>密码重置成功!</target>
@@ -9986,18 +10037,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9986 <target>尝试次数过多,请在 10037 <target>尝试次数过多,请在
9987 <x id="PH"/> 分钟后重试。 10038 <x id="PH"/> 分钟后重试。
9988 </target> 10039 </target>
9989 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 10040
9990 </trans-unit> 10041 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9991 <trans-unit id="4965472196059235310"> 10042 <trans-unit id="4965472196059235310">
9992 <source>Too many attempts, please try again later.</source> 10043 <source>Too many attempts, please try again later.</source>
9993 <target>尝试次数过多,请稍后重试。</target> 10044 <target>尝试次数过多,请稍后重试。</target>
9994 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 10045
9995 </trans-unit> 10046 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9996 <trans-unit id="1693549688987384699"> 10047 <trans-unit id="1693549688987384699">
9997 <source>Server error. Please retry later.</source> 10048 <source>Server error. Please retry later.</source>
9998 <target>服务器出现错误。请稍后重试。</target> 10049 <target>服务器出现错误。请稍后重试。</target>
9999 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 10050
10000 </trans-unit> 10051 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
10001 <trans-unit id="5927402622550505067" datatype="html"> 10052 <trans-unit id="5927402622550505067" datatype="html">
10002 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 10053 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
10003 <target state="new">Subscribed to all current channels of 10054 <target state="new">Subscribed to all current channels of
@@ -10593,35 +10644,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10593 <trans-unit id="3284171506518522275"> 10644 <trans-unit id="3284171506518522275">
10594 <source>Your video was uploaded to your account and is private.</source> 10645 <source>Your video was uploaded to your account and is private.</source>
10595 <target>您的视频已经以私有方式上传至您的帐户。</target> 10646 <target>您的视频已经以私有方式上传至您的帐户。</target>
10596 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10647
10597 </trans-unit> 10648 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10598 <trans-unit id="5699822024600815733"> 10649 <trans-unit id="5699822024600815733">
10599 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10650 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10600 <target>相关信息(如标签、说明)将会丢失,您确定要离开这个页面吗?</target> 10651 <target>相关信息(如标签、说明)将会丢失,您确定要离开这个页面吗?</target>
10601 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10652
10602 </trans-unit> 10653 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10603 <trans-unit id="1219739004043110649"> 10654 <trans-unit id="1219739004043110649">
10604 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10655 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10605 <target>您的视频尚未上传完毕,您确定要离开这个页面吗?</target> 10656 <target>您的视频尚未上传完毕,您确定要离开这个页面吗?</target>
10606 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10657
10607 </trans-unit> 10658 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10608 <trans-unit id="6932865105766151309" datatype="html"> 10659 <trans-unit id="6932865105766151309" datatype="html">
10609 <source>Upload</source> 10660 <source>Upload</source>
10610 <target state="translated">上传</target> 10661 <target state="translated">上传</target>
10611 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10662
10612 </trans-unit> 10663 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10613 <trans-unit id="8278735427925094503"> 10664 <trans-unit id="8278735427925094503">
10614 <source>Upload <x id="PH"/> </source> 10665 <source>Upload <x id="PH"/> </source>
10615 <target>上传 10666 <target>上传
10616 <x id="PH"/> 10667 <x id="PH"/>
10617 </target> 10668 </target>
10618 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10669
10619 </trans-unit> 10670 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10620 <trans-unit id="5981816353437801748"> 10671 <trans-unit id="5981816353437801748">
10621 <source>Video published.</source> 10672 <source>Video published.</source>
10622 <target>视频已发布。</target> 10673 <target>视频已发布。</target>
10623 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10674
10624 </trans-unit> 10675 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10625 <trans-unit id="764164089183618119"> 10676 <trans-unit id="764164089183618119">
10626 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10677 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10627 <target>您有未保存的修改!如果您离开本页面,您将会失去这些修改。</target> 10678 <target>您有未保存的修改!如果您离开本页面,您将会失去这些修改。</target>
@@ -10689,27 +10740,27 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10689 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10740 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10690 <target state="translated">此视频在此实例上不可用,是否要在原始实例上重定向: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target> 10741 <target state="translated">此视频在此实例上不可用,是否要在原始实例上重定向: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</target>
10691 10742
10692 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group></trans-unit> 10743 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10693 <trans-unit id="5761611056224181752" datatype="html"> 10744 <trans-unit id="5761611056224181752" datatype="html">
10694 <source>Redirection</source> 10745 <source>Redirection</source>
10695 <target state="translated">重新导向</target> 10746 <target state="translated">重新导向</target>
10696 10747
10697 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group></trans-unit> 10748 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10698 <trans-unit id="8858527736400081688"> 10749 <trans-unit id="8858527736400081688">
10699 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10750 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10700 <target>此视频包含成人或裸露内容。您确定要观看吗?</target> 10751 <target>此视频包含成人或裸露内容。您确定要观看吗?</target>
10701 10752
10702 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group></trans-unit> 10753 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10703 <trans-unit id="3937119019020041049"> 10754 <trans-unit id="3937119019020041049">
10704 <source>Mature or explicit content</source> 10755 <source>Mature or explicit content</source>
10705 <target>成人或裸露内容</target> 10756 <target>成人或裸露内容</target>
10706 10757
10707 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group></trans-unit> 10758 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10708 <trans-unit id="1755474755114288376" datatype="html"> 10759 <trans-unit id="1755474755114288376" datatype="html">
10709 <source>Up Next</source> 10760 <source>Up Next</source>
10710 <target state="translated">往下</target> 10761 <target state="translated">往下</target>
10711 10762
10712 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group></trans-unit> 10763 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10713 <trans-unit id="2159130950882492111" datatype="html"> 10764 <trans-unit id="2159130950882492111" datatype="html">
10714 <source>Cancel</source> 10765 <source>Cancel</source>
10715 <target state="translated">取消</target> 10766 <target state="translated">取消</target>
@@ -10719,62 +10770,62 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10719 <source>Autoplay is suspended</source> 10770 <source>Autoplay is suspended</source>
10720 <target state="translated">自动播放已经暂停</target> 10771 <target state="translated">自动播放已经暂停</target>
10721 10772
10722 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group></trans-unit> 10773 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10723 <trans-unit id="7895294730547405228" datatype="html"> 10774 <trans-unit id="7895294730547405228" datatype="html">
10724 <source>Enter/exit fullscreen (requires player focus)</source> 10775 <source>Enter/exit fullscreen (requires player focus)</source>
10725 <target state="translated">进入/离开全屏幕(需要播放器聚焦)</target> 10776 <target state="translated">进入/离开全屏幕(需要播放器聚焦)</target>
10726 10777
10727 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group></trans-unit> 10778 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10728 <trans-unit id="7618388257165864759" datatype="html"> 10779 <trans-unit id="7618388257165864759" datatype="html">
10729 <source>Play/Pause the video (requires player focus)</source> 10780 <source>Play/Pause the video (requires player focus)</source>
10730 <target state="translated">播放/暂停视频(需要播放器聚焦)</target> 10781 <target state="translated">播放/暂停视频(需要播放器聚焦)</target>
10731 10782
10732 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group></trans-unit> 10783 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10733 <trans-unit id="7761890399634216630" datatype="html"> 10784 <trans-unit id="7761890399634216630" datatype="html">
10734 <source>Mute/unmute the video (requires player focus)</source> 10785 <source>Mute/unmute the video (requires player focus)</source>
10735 <target state="translated">静音/解除视频静音(需要播放器焦点)</target> 10786 <target state="translated">静音/解除视频静音(需要播放器焦点)</target>
10736 10787
10737 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group></trans-unit> 10788 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10738 <trans-unit id="5996585232248234904" datatype="html"> 10789 <trans-unit id="5996585232248234904" datatype="html">
10739 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10790 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10740 <target state="translated">跳到视频的百分比:0 是 0%,9 是 90%(需要播放器焦点)</target> 10791 <target state="translated">跳到视频的百分比:0 是 0%,9 是 90%(需要播放器焦点)</target>
10741 10792
10742 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group></trans-unit> 10793 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10743 <trans-unit id="3748765405903319998" datatype="html"> 10794 <trans-unit id="3748765405903319998" datatype="html">
10744 <source>Increase the volume (requires player focus)</source> 10795 <source>Increase the volume (requires player focus)</source>
10745 <target state="translated">增加音量(需要播放器焦点)</target> 10796 <target state="translated">增加音量(需要播放器焦点)</target>
10746 10797
10747 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group></trans-unit> 10798 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10748 <trans-unit id="5810704036407159982" datatype="html"> 10799 <trans-unit id="5810704036407159982" datatype="html">
10749 <source>Decrease the volume (requires player focus)</source> 10800 <source>Decrease the volume (requires player focus)</source>
10750 <target state="translated">降低音量(需要播放器焦点)</target> 10801 <target state="translated">降低音量(需要播放器焦点)</target>
10751 10802
10752 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit> 10803 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10753 <trans-unit id="2622048822548065691" datatype="html"> 10804 <trans-unit id="2622048822548065691" datatype="html">
10754 <source>Seek the video forward (requires player focus)</source> 10805 <source>Seek the video forward (requires player focus)</source>
10755 <target state="translated">快进视频(需要播放器焦点)</target> 10806 <target state="translated">快进视频(需要播放器焦点)</target>
10756 10807
10757 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit> 10808 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10758 <trans-unit id="6540078205109221153" datatype="html"> 10809 <trans-unit id="6540078205109221153" datatype="html">
10759 <source>Seek the video backward (requires player focus)</source> 10810 <source>Seek the video backward (requires player focus)</source>
10760 <target state="translated">向后快退视频(需要播放器焦点點)</target> 10811 <target state="translated">向后快退视频(需要播放器焦点點)</target>
10761 10812
10762 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group></trans-unit> 10813 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10763 <trans-unit id="1956491957766210808" datatype="html"> 10814 <trans-unit id="1956491957766210808" datatype="html">
10764 <source>Increase playback rate (requires player focus)</source> 10815 <source>Increase playback rate (requires player focus)</source>
10765 <target state="translated">提高播放速度(需要播放器点击)</target> 10816 <target state="translated">提高播放速度(需要播放器点击)</target>
10766 10817
10767 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group></trans-unit> 10818 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10768 <trans-unit id="5495529997674803186" datatype="html"> 10819 <trans-unit id="5495529997674803186" datatype="html">
10769 <source>Decrease playback rate (requires player focus)</source> 10820 <source>Decrease playback rate (requires player focus)</source>
10770 <target state="translated">减慢播放速度(需要播放器点击)</target> 10821 <target state="translated">减慢播放速度(需要播放器点击)</target>
10771 10822
10772 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit> 10823 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10773 <trans-unit id="3178343147230721210" datatype="html"> 10824 <trans-unit id="3178343147230721210" datatype="html">
10774 <source>Navigate in the video frame by frame (requires player focus)</source> 10825 <source>Navigate in the video frame by frame (requires player focus)</source>
10775 <target state="translated">逐画格浏览视频(需要点击播放器)</target> 10826 <target state="translated">逐画格浏览视频(需要点击播放器)</target>
10776 10827
10777 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group></trans-unit> 10828 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10778 <trans-unit id="8025996572234182184"> 10829 <trans-unit id="8025996572234182184">
10779 <source>Like the video</source> 10830 <source>Like the video</source>
10780 <target state="translated">喜欢这视频</target> 10831 <target state="translated">喜欢这视频</target>
diff --git a/client/src/locale/angular.zh-Hant-TW.xlf b/client/src/locale/angular.zh-Hant-TW.xlf
index d73c743f4..1f5036e7d 100644
--- a/client/src/locale/angular.zh-Hant-TW.xlf
+++ b/client/src/locale/angular.zh-Hant-TW.xlf
@@ -162,19 +162,19 @@
162 <target state="translated"> 162 <target state="translated">
163 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/> 163 <x id="INTERPOLATION" equiv-text="{{ action.label }}"/>
164 </target> 164 </target>
165 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group> 165
166 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group> 166
167 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group> 167
168 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group> 168
169 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group> 169
170 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 170
171 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group> 171
172 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group> 172
173 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group> 173
174 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group> 174
175 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group> 175
176 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group> 176
177 </trans-unit> 177 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.html</context><context context-type="linenumber">77</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/buttons/action-dropdown.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">14</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-main/misc/top-menu-dropdown.component.html</context><context context-type="linenumber">24</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">52</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">78</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">101</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/videos-selection.component.html</context><context context-type="linenumber">1</context></context-group></trans-unit>
178 <trans-unit id="1486537403020619891" datatype="html"> 178 <trans-unit id="1486537403020619891" datatype="html">
179 <source>My watch history</source> 179 <source>My watch history</source>
180 <target state="translated">我的觀看紀錄</target> 180 <target state="translated">我的觀看紀錄</target>
@@ -243,22 +243,22 @@
243 <trans-unit id="5674286808255988565"> 243 <trans-unit id="5674286808255988565">
244 <source>Create</source> 244 <source>Create</source>
245 <target>建立</target> 245 <target>建立</target>
246 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 246
247 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group> 247
248 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">103</context></context-group> 248
249 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 249
250 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group> 250
251 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group> 251
252 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 252
253 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group> 253
254 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group> 254
255 </trans-unit> 255 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">102</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts</context><context context-type="linenumber">89</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-playlist/video-add-to-playlist.component.html</context><context context-type="linenumber">81</context></context-group></trans-unit>
256 <trans-unit id="1006562256968398209" datatype="html"> 256 <trans-unit id="1006562256968398209" datatype="html">
257 <source>video</source> 257 <source>video</source>
258 <target state="translated">影片</target> 258 <target state="translated">影片</target>
259 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">288</context></context-group> 259
260 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group> 260
261 </trans-unit> 261 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">287</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.ts</context><context context-type="linenumber">55</context></context-group></trans-unit>
262 <trans-unit id="6438815964972582865" datatype="html"> 262 <trans-unit id="6438815964972582865" datatype="html">
263 <source>The following link contains a private token and should not be shared with anyone.</source> 263 <source>The following link contains a private token and should not be shared with anyone.</source>
264 <target state="translated">以下連結包含了一個專用權杖,不應該與其他人分享。</target> 264 <target state="translated">以下連結包含了一個專用權杖,不應該與其他人分享。</target>
@@ -330,13 +330,13 @@
330 <trans-unit id="6995024616159044376" datatype="html"> 330 <trans-unit id="6995024616159044376" datatype="html">
331 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source> 331 <source>Your video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="videoQuotaUsedBytes"/>, quota: <x id="PH_2" equiv-text="videoQuotaBytes"/>)</source>
332 <target state="translated">此影片超過了您的影片配額(影片大小:<x id="PH" equiv-text="videoSizeBytes"/>,已使用:<x id="PH_1" equiv-text="videoQuotaUsedBytes"/>,配額:<x id="PH_2" equiv-text="videoQuotaBytes"/>)</target> 332 <target state="translated">此影片超過了您的影片配額(影片大小:<x id="PH" equiv-text="videoSizeBytes"/>,已使用:<x id="PH_1" equiv-text="videoQuotaUsedBytes"/>,配額:<x id="PH_2" equiv-text="videoQuotaBytes"/>)</target>
333 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">323</context></context-group> 333
334 </trans-unit> 334 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">322</context></context-group></trans-unit>
335 <trans-unit id="7873395933409147217" datatype="html"> 335 <trans-unit id="7873395933409147217" datatype="html">
336 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source> 336 <source>Your daily video quota is exceeded with this video (video size: <x id="PH" equiv-text="videoSizeBytes"/>, used: <x id="PH_1" equiv-text="quotaUsedDailyBytes"/>, quota: <x id="PH_2" equiv-text="quotaDailyBytes"/>)</source>
337 <target state="translated">此影片超過了您的每日影片配額(影片大小:<x id="PH" equiv-text="videoSizeBytes"/>,已使用:<x id="PH_1" equiv-text="quotaUsedDailyBytes"/>,配額:<x id="PH_2" equiv-text="quotaDailyBytes"/>)</target> 337 <target state="translated">此影片超過了您的每日影片配額(影片大小:<x id="PH" equiv-text="videoSizeBytes"/>,已使用:<x id="PH_1" equiv-text="quotaUsedDailyBytes"/>,配額:<x id="PH_2" equiv-text="quotaDailyBytes"/>)</target>
338 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">341</context></context-group> 338
339 </trans-unit> 339 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">340</context></context-group></trans-unit>
340 <trans-unit id="5235042777215655908" datatype="html"> 340 <trans-unit id="5235042777215655908" datatype="html">
341 <source>subtitles</source> 341 <source>subtitles</source>
342 <target state="translated">字幕</target> 342 <target state="translated">字幕</target>
@@ -774,10 +774,10 @@
774 <trans-unit id="2602586221576511475"> 774 <trans-unit id="2602586221576511475">
775 <source>Video quota</source> 775 <source>Video quota</source>
776 <target>影片配額</target> 776 <target>影片配額</target>
777 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group> 777
778 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 778
779 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group> 779
780 </trans-unit> 780 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">151</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-features-table.component.html</context><context context-type="linenumber">47</context></context-group></trans-unit>
781 <trans-unit id="1502595455339510144"> 781 <trans-unit id="1502595455339510144">
782 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source> 782 <source>Unlimited <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/> per day)<x id="CLOSE_TAG_NG_CONTAINER"/></source>
783 <target>無限 <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/>每日)<x id="CLOSE_TAG_NG_CONTAINER"/></target> 783 <target>無限 <x id="START_TAG_NG_CONTAINER"/>(<x id="INTERPOLATION"/>每日)<x id="CLOSE_TAG_NG_CONTAINER"/></target>
@@ -857,6 +857,30 @@
857 <target>聯盟</target> 857 <target>聯盟</target>
858 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 858 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
859 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group> 859 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-instance/instance-statistics.component.html</context><context context-type="linenumber">58</context></context-group>
860 </trans-unit><trans-unit id="8726138323871139597" datatype="html">
861 <source>Following</source><target state="new">Following</target>
862 <context-group purpose="location">
863 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
864 <context context-type="linenumber">29</context>
865 </context-group>
866 <context-group purpose="location">
867 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
868 <context context-type="linenumber">31</context>
869 </context-group>
870 <context-group purpose="location">
871 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
872 <context context-type="linenumber">28</context>
873 </context-group>
874 </trans-unit><trans-unit id="4914577418256256836" datatype="html">
875 <source>Followers</source><target state="new">Followers</target>
876 <context-group purpose="location">
877 <context context-type="sourcefile">src/app/+admin/admin.component.ts</context>
878 <context context-type="linenumber">34</context>
879 </context-group>
880 <context-group purpose="location">
881 <context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context>
882 <context context-type="linenumber">37</context>
883 </context-group>
860 </trans-unit> 884 </trans-unit>
861 <trans-unit id="3541687134897970106"> 885 <trans-unit id="3541687134897970106">
862 <source>followers</source> 886 <source>followers</source>
@@ -920,25 +944,25 @@
920 <trans-unit id="2159130950882492111"> 944 <trans-unit id="2159130950882492111">
921 <source>Cancel</source> 945 <source>Cancel</source>
922 <target>取消</target> 946 <target>取消</target>
923 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group> 947
924 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">117</context></context-group> 948
925 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group> 949
926 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group> 950
927 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group> 951
928 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group> 952
929 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group> 953
930 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group> 954
931 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">408</context></context-group> 955
932 <context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group> 956
933 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group> 957
934 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group> 958
935 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 959
936 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group> 960
937 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group> 961
938 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group> 962
939 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group> 963
940 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group> 964
941 </trans-unit> 965 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">33</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">121</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.html</context><context context-type="linenumber">22</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html</context><context context-type="linenumber">37</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">69</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.html</context><context context-type="linenumber">81</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/shared/comment/video-comment-add.component.html</context><context context-type="linenumber">73</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">415</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/modal/confirm.component.html</context><context context-type="linenumber">20</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/moderation-comment-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">31</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/report.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/report-modals/video-report.component.html</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-ban-modal.component.html</context><context context-type="linenumber">26</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/video-block.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-video-miniature/video-download.component.html</context><context context-type="linenumber">152</context></context-group></trans-unit>
942 <trans-unit id="3616223838716839702"> 966 <trans-unit id="3616223838716839702">
943 <source>Ban this user</source> 967 <source>Ban this user</source>
944 <target>阻擋此使用者</target> 968 <target>阻擋此使用者</target>
@@ -1004,19 +1028,13 @@
1004 <trans-unit id="7252854992688790751" datatype="html"> 1028 <trans-unit id="7252854992688790751" datatype="html">
1005 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1029 <source>This instance allows registration. However, be careful to check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> before creating an account. You may also search for another instance to match your exact needs at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1006 <target state="translated">此站臺允許註冊。然而,請留心查閱<x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>條款<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>條款<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> ,然後才建立帳號。您亦可搜尋另一個站臺以切合您的需要:<x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>。 </target> 1030 <target state="translated">此站臺允許註冊。然而,請留心查閱<x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;terms-anchor&quot; (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>條款<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;terms-link&quot; target=&quot;_blank&quot; routerLink=&quot;/about/instance&quot; fragment=&quot;terms&quot;>"/>條款<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> ,然後才建立帳號。您亦可搜尋另一個站臺以切合您的需要:<x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br />"/><x id="START_LINK_2" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>。 </target>
1007 <context-group purpose="location"> 1031
1008 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1032 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">64</context></context-group></trans-unit>
1009 <context context-type="linenumber">60,62</context>
1010 </context-group>
1011 </trans-unit>
1012 <trans-unit id="7215649348148521605" datatype="html"> 1033 <trans-unit id="7215649348148521605" datatype="html">
1013 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source> 1034 <source>Currently this instance doesn't allow for user registration, you may check the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>Terms<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there. Find yours among multiple instances at: <x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>. </source>
1014 <target state="translated">目前此站臺不允許使用者註冊,您可查閱<x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>條款<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> 以瞭解詳情,或尋找別的站臺,好讓您註冊帳號並上載您的影片。看看一眾站臺中有哪個合您心意:<x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>。 </target> 1035 <target state="translated">目前此站臺不允許使用者註冊,您可查閱<x id="START_LINK" ctype="x-a" equiv-text="&lt;a (click)=&quot;onTermsClick($event, instanceInformation)&quot; href='#'>"/>條款<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/> 以瞭解詳情,或尋找別的站臺,好讓您註冊帳號並上載您的影片。看看一眾站臺中有哪個合您心意:<x id="LINE_BREAK" ctype="lb" equiv-text="&lt;br /> "/><x id="START_LINK_1" equiv-text="&lt;a class=&quot;alert-link&quot; href=&quot;https://joinpeertube.org/instances&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;>"/>https://joinpeertube.org/instances<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a>"/>。 </target>
1015 <context-group purpose="location"> 1036
1016 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1037 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">69</context></context-group></trans-unit>
1017 <context context-type="linenumber">65,67</context>
1018 </context-group>
1019 </trans-unit>
1020 <trans-unit id="2392488717875840729"> 1038 <trans-unit id="2392488717875840729">
1021 <source>User</source> 1039 <source>User</source>
1022 <target>使用者</target> 1040 <target>使用者</target>
@@ -1027,67 +1045,67 @@
1027 <source>Username or email address</source> 1045 <source>Username or email address</source>
1028 <target>使用者名稱或電子信箱</target> 1046 <target>使用者名稱或電子信箱</target>
1029 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group> 1047 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">23</context></context-group>
1048 </trans-unit><trans-unit id="1758058452376026925" datatype="html">
1049 <source> ⚠️ Most email addresses do not include capital letters. </source><target state="new"> ⚠️ Most email addresses do not include capital letters. </target>
1050 <context-group purpose="location">
1051 <context context-type="sourcefile">src/app/+login/login.component.html</context>
1052 <context context-type="linenumber">33,34</context>
1053 </context-group>
1030 </trans-unit> 1054 </trans-unit>
1031 <trans-unit id="1431416938026210429"> 1055 <trans-unit id="1431416938026210429">
1032 <source>Password</source> 1056 <source>Password</source>
1033 <target>密碼</target> 1057 <target>密碼</target>
1034 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">34</context></context-group> 1058
1035 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">36</context></context-group> 1059
1036 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group> 1060
1037 <context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group> 1061
1038 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group> 1062
1039 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group> 1063
1040 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1064
1041 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group> 1065
1042 </trans-unit> 1066 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">117</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">38</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">40</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">8</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+reset-password/reset-password.component.html</context><context context-type="linenumber">10</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">56</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">58</context></context-group></trans-unit>
1043 <trans-unit id="8715156686857791956" datatype="html"> 1067 <trans-unit id="8715156686857791956" datatype="html">
1044 <source>Click here to reset your password</source> 1068 <source>Click here to reset your password</source>
1045 <target state="translated">點擊此處以重設您的密碼</target> 1069 <target state="translated">點擊此處以重設您的密碼</target>
1046 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">47</context></context-group> 1070
1047 </trans-unit> 1071 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1048 <trans-unit id="892063502898494584" datatype="html"> 1072 <trans-unit id="892063502898494584" datatype="html">
1049 <source>I forgot my password</source> 1073 <source>I forgot my password</source>
1050 <target state="translated">我忘了我的密碼</target> 1074 <target state="translated">我忘了我的密碼</target>
1051 <context-group purpose="location"> 1075
1052 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1076 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">51</context></context-group></trans-unit>
1053 <context context-type="linenumber">47</context>
1054 </context-group>
1055 </trans-unit>
1056 <trans-unit id="2101170466365500913" datatype="html"> 1077 <trans-unit id="2101170466365500913" datatype="html">
1057 <source>Logging into an account lets you publish content</source> 1078 <source>Logging into an account lets you publish content</source>
1058 <target state="translated">登入帳號就可讓您發佈內容</target> 1079 <target state="translated">登入帳號就可讓您發佈內容</target>
1059 <context-group purpose="location"> 1080
1060 <context context-type="sourcefile">src/app/+login/login.component.html</context> 1081 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">60</context></context-group></trans-unit>
1061 <context context-type="linenumber">56,57</context>
1062 </context-group>
1063 </trans-unit>
1064 <trans-unit id="2454050363478003966"> 1082 <trans-unit id="2454050363478003966">
1065 <source>Login</source> 1083 <source>Login</source>
1066 <target>登入</target> 1084 <target>登入</target>
1067 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group> 1085
1068 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">44</context></context-group> 1086
1069 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group> 1087
1070 </trans-unit> 1088 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login-routing.module.ts</context><context context-type="linenumber">12</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">48</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">99</context></context-group></trans-unit>
1071 <trans-unit id="3183213940445113677" datatype="html"> 1089 <trans-unit id="3183213940445113677" datatype="html">
1072 <source>Or sign in with</source> 1090 <source>Or sign in with</source>
1073 <target state="translated">或使用其他帳戶登入</target> 1091 <target state="translated">或使用其他帳戶登入</target>
1074 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">72</context></context-group> 1092
1075 </trans-unit> 1093 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">76</context></context-group></trans-unit>
1076 <trans-unit id="3238209155172574367"> 1094 <trans-unit id="3238209155172574367">
1077 <source>Forgot your password</source> 1095 <source>Forgot your password</source>
1078 <target>忘記您的密碼</target> 1096 <target>忘記您的密碼</target>
1079 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">91</context></context-group> 1097
1080 </trans-unit> 1098 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">95</context></context-group></trans-unit>
1081 <trans-unit id="87327320394367488"> 1099 <trans-unit id="87327320394367488">
1082 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source> 1100 <source>We are sorry, you cannot recover your password because your instance administrator did not configure the PeerTube email system.</source>
1083 <target>我們很抱歉,您無法復原您的密碼,因為您的站臺管理員並未設定 PeerTube 電子郵件系統。</target> 1101 <target>我們很抱歉,您無法復原您的密碼,因為您的站臺管理員並未設定 PeerTube 電子郵件系統。</target>
1084 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">99</context></context-group> 1102
1085 </trans-unit> 1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group></trans-unit>
1086 <trans-unit id="3188014010833256853" datatype="html"> 1104 <trans-unit id="3188014010833256853" datatype="html">
1087 <source>Enter your email address and we will send you a link to reset your password.</source> 1105 <source>Enter your email address and we will send you a link to reset your password.</source>
1088 <target state="translated">輸入您的電子郵件地址,然後我們將會寄送連結給您重設您的密碼。</target> 1106 <target state="translated">輸入您的電子郵件地址,然後我們將會寄送連結給您重設您的密碼。</target>
1089 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">103</context></context-group> 1107
1090 </trans-unit> 1108 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group></trans-unit>
1091 <trans-unit id="1190256911880544559" datatype="html"> 1109 <trans-unit id="1190256911880544559" datatype="html">
1092 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>. 1110 <source>An email with the reset password instructions will be sent to <x id="PH" equiv-text="this.forgotPasswordEmail"/>.
1093The link will expire within 1 hour.</source> 1111The link will expire within 1 hour.</source>
@@ -1097,26 +1115,26 @@ The link will expire within 1 hour.</source>
1097 <trans-unit id="4768749765465246664"> 1115 <trans-unit id="4768749765465246664">
1098 <source>Email</source> 1116 <source>Email</source>
1099 <target>電子郵件</target> 1117 <target>電子郵件</target>
1100 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">107</context></context-group> 1118
1101 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group> 1119
1102 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group> 1120
1103 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group> 1121
1104 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group> 1122
1105 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1123
1106 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group> 1124
1107 </trans-unit> 1125 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">105</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">112</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.html</context><context context-type="linenumber">4</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">45</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">47</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">8</context></context-group></trans-unit>
1108 <trans-unit id="3967269098753656610"> 1126 <trans-unit id="3967269098753656610">
1109 <source>Email address</source> 1127 <source>Email address</source>
1110 <target>電子信箱</target> 1128 <target>電子信箱</target>
1111 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">109</context></context-group> 1129
1112 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group> 1130
1113 </trans-unit> 1131 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">113</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html</context><context context-type="linenumber">10</context></context-group></trans-unit>
1114 <trans-unit id="7808756054397155068" datatype="html"> 1132 <trans-unit id="7808756054397155068" datatype="html">
1115 <source>Reset</source> 1133 <source>Reset</source>
1116 <target state="translated">重設</target> 1134 <target state="translated">重設</target>
1117 <note priority="1" from="description">Password reset button</note> 1135 <note priority="1" from="description">Password reset button</note>
1118 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">122</context></context-group> 1136
1119 </trans-unit> 1137 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">126</context></context-group></trans-unit>
1120 <trans-unit id="4319634264526091601" datatype="html"> 1138 <trans-unit id="4319634264526091601" datatype="html">
1121 <source>on this instance</source> 1139 <source>on this instance</source>
1122 <target state="translated">在此站臺</target> 1140 <target state="translated">在此站臺</target>
@@ -1442,9 +1460,9 @@ The link will expire within 1 hour.</source>
1442 <trans-unit id="2308975396733519902"> 1460 <trans-unit id="2308975396733519902">
1443 <source>Create an account</source> 1461 <source>Create an account</source>
1444 <target>建立帳號</target> 1462 <target>建立帳號</target>
1445 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">50</context></context-group> 1463
1446 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group> 1464
1447 </trans-unit> 1465 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.html</context><context context-type="linenumber">54</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.html</context><context context-type="linenumber">100</context></context-group></trans-unit>
1448 <trans-unit id="3058024914967508975" datatype="html"> 1466 <trans-unit id="3058024914967508975" datatype="html">
1449 <source>My videos</source> 1467 <source>My videos</source>
1450 <target state="translated">我的影片</target> 1468 <target state="translated">我的影片</target>
@@ -1511,10 +1529,10 @@ The link will expire within 1 hour.</source>
1511 <trans-unit id="1504521795586863905" datatype="html"> 1529 <trans-unit id="1504521795586863905" datatype="html">
1512 <source>VIDEOS</source> 1530 <source>VIDEOS</source>
1513 <target state="translated">影片</target> 1531 <target state="translated">影片</target>
1514 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">83</context></context-group> 1532
1515 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group> 1533
1516 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group> 1534
1517 </trans-unit> 1535 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">82</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html</context><context context-type="linenumber">215</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit>
1518 <trans-unit id="667372110624203230" datatype="html"> 1536 <trans-unit id="667372110624203230" datatype="html">
1519 <source>Import jobs concurrency</source> 1537 <source>Import jobs concurrency</source>
1520 <target state="translated">匯入工作並行</target> 1538 <target state="translated">匯入工作並行</target>
@@ -1593,8 +1611,8 @@ The link will expire within 1 hour.</source>
1593 <trans-unit id="4424964105331349857" datatype="html"> 1611 <trans-unit id="4424964105331349857" datatype="html">
1594 <source>I'm a teapot</source> 1612 <source>I'm a teapot</source>
1595 <target state="translated">我是茶壺</target> 1613 <target state="translated">我是茶壺</target>
1596 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">26</context></context-group> 1614
1597 </trans-unit> 1615 <context-group purpose="location"><context context-type="sourcefile">src/app/+page-not-found/page-not-found.component.ts</context><context context-type="linenumber">27</context></context-group></trans-unit>
1598 <trans-unit id="1597262876035959248" datatype="html"> 1616 <trans-unit id="1597262876035959248" datatype="html">
1599 <source>That's an error.</source> 1617 <source>That's an error.</source>
1600 <target state="translated">發生錯誤。</target> 1618 <target state="translated">發生錯誤。</target>
@@ -1687,8 +1705,8 @@ The link will expire within 1 hour.</source>
1687 <trans-unit id="2971365540217107489" datatype="html"> 1705 <trans-unit id="2971365540217107489" datatype="html">
1688 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source> 1706 <source>Media is too large for the server. Please contact you administrator if you want to increase the limit size.</source>
1689 <target state="translated">媒體對此伺服器來說太大。如果您想要增加限制大小的話,請聯絡您的管理員。</target> 1707 <target state="translated">媒體對此伺服器來說太大。如果您想要增加限制大小的話,請聯絡您的管理員。</target>
1690 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">62</context></context-group> 1708
1691 </trans-unit> 1709 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">61</context></context-group></trans-unit>
1692 <trans-unit id="5131854469652959713" datatype="html"> 1710 <trans-unit id="5131854469652959713" datatype="html">
1693 <source>GLOBAL SEARCH</source> 1711 <source>GLOBAL SEARCH</source>
1694 <target state="translated">全域搜尋</target> 1712 <target state="translated">全域搜尋</target>
@@ -2370,13 +2388,13 @@ The link will expire within 1 hour.</source>
2370 <trans-unit id="9172233176401579786"> 2388 <trans-unit id="9172233176401579786">
2371 <source>Scheduled</source> 2389 <source>Scheduled</source>
2372 <target>排定</target> 2390 <target>排定</target>
2373 2391 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-edit.component.ts</context><context context-type="linenumber">192</context></context-group>
2374 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-edit.component.ts</context><context context-type="linenumber">192</context></context-group></trans-unit> 2392 </trans-unit>
2375 <trans-unit id="1435317307066082710" datatype="html"> 2393 <trans-unit id="1435317307066082710" datatype="html">
2376 <source>Hide the video until a specific date</source> 2394 <source>Hide the video until a specific date</source>
2377 <target state="translated">在特定日期前隱藏影片</target> 2395 <target state="translated">在特定日期前隱藏影片</target>
2378 2396 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-edit.component.ts</context><context context-type="linenumber">193</context></context-group>
2379 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/shared/video-edit.component.ts</context><context context-type="linenumber">193</context></context-group></trans-unit> 2397 </trans-unit>
2380 <trans-unit id="6148369758871787018"> 2398 <trans-unit id="6148369758871787018">
2381 <source>Video background image</source> 2399 <source>Video background image</source>
2382 <target>影片背景圖片</target> 2400 <target>影片背景圖片</target>
@@ -2428,8 +2446,8 @@ The link will expire within 1 hour.</source>
2428 <trans-unit id="6161604372916832458" datatype="html"> 2446 <trans-unit id="6161604372916832458" datatype="html">
2429 <source>Upload on hold</source> 2447 <source>Upload on hold</source>
2430 <target state="translated">暫緩上傳</target> 2448 <target state="translated">暫緩上傳</target>
2431 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">124</context></context-group> 2449
2432 </trans-unit> 2450 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">123</context></context-group></trans-unit>
2433 <trans-unit id="285180972645018518" datatype="html"> 2451 <trans-unit id="285180972645018518" datatype="html">
2434 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source> 2452 <source>Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.</source>
2435 <target state="translated">抱歉,您的帳號已停用上傳功能。如果您想要新增影片,管理員必須解鎖您的配額。</target> 2453 <target state="translated">抱歉,您的帳號已停用上傳功能。如果您想要新增影片,管理員必須解鎖您的配額。</target>
@@ -3114,11 +3132,7 @@ The link will expire within 1 hour.</source>
3114 <target>ID</target> 3132 <target>ID</target>
3115 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group> 3133 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/system/jobs/jobs.component.html</context><context context-type="linenumber">45</context></context-group>
3116 </trans-unit> 3134 </trans-unit>
3117 <trans-unit id="2265605798180116441"> 3135
3118 <source>Follower handle</source>
3119 <target>追蹤者處理</target>
3120 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">24</context></context-group>
3121 </trans-unit>
3122 <trans-unit id="5911214550882917183"> 3136 <trans-unit id="5911214550882917183">
3123 <source>State</source> 3137 <source>State</source>
3124 <target>狀態</target> 3138 <target>狀態</target>
@@ -3186,11 +3200,7 @@ The link will expire within 1 hour.</source>
3186 </target> 3200 </target>
3187 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group> 3201 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/batch-domains-modal.component.html</context><context context-type="linenumber">3</context></context-group>
3188 </trans-unit> 3202 </trans-unit>
3189 <trans-unit id="6641024648411549335"> 3203
3190 <source>Host</source>
3191 <target>主機</target>
3192 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">31</context></context-group>
3193 </trans-unit>
3194 <trans-unit id="6571718060636962350" datatype="html"> 3204 <trans-unit id="6571718060636962350" datatype="html">
3195 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source> 3205 <source>Redundancy allowed <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></source>
3196 <target state="translated">允許冗餘 <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target> 3206 <target state="translated">允許冗餘 <x id="START_TAG_P_SORTICON"/><x id="CLOSE_TAG_P_SORTICON"/></target>
@@ -3199,9 +3209,9 @@ The link will expire within 1 hour.</source>
3199 <trans-unit id="9160510009013134726" datatype="html"> 3209 <trans-unit id="9160510009013134726" datatype="html">
3200 <source>Unfollow</source> 3210 <source>Unfollow</source>
3201 <target state="translated">取消追蹤</target> 3211 <target state="translated">取消追蹤</target>
3202 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group> 3212
3203 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">58</context></context-group> 3213
3204 </trans-unit> 3214 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">41</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">48</context></context-group></trans-unit>
3205 <trans-unit id="8246779176913476983" datatype="html"> 3215 <trans-unit id="8246779176913476983" datatype="html">
3206 <source>Open instance in a new tab</source> 3216 <source>Open instance in a new tab</source>
3207 <target state="translated">在新分頁中開啟站臺</target> 3217 <target state="translated">在新分頁中開啟站臺</target>
@@ -3212,28 +3222,20 @@ The link will expire within 1 hour.</source>
3212 <trans-unit id="9132918641931433659" datatype="html"> 3222 <trans-unit id="9132918641931433659" datatype="html">
3213 <source>No host found matching current filters.</source> 3223 <source>No host found matching current filters.</source>
3214 <target state="translated">沒有主機符合目前的過濾器。</target> 3224 <target state="translated">沒有主機符合目前的過濾器。</target>
3215 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">70</context></context-group> 3225
3216 </trans-unit> 3226 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group></trans-unit>
3217 <trans-unit id="7274241885665071790" datatype="html"> 3227 <trans-unit id="7274241885665071790" datatype="html">
3218 <source>Your instance is not following anyone.</source> 3228 <source>Your instance is not following anyone.</source>
3219 <target state="translated">您的站臺並未追蹤任何人。</target> 3229 <target state="translated">您的站臺並未追蹤任何人。</target>
3220 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">71</context></context-group> 3230
3221 </trans-unit> 3231 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">72</context></context-group></trans-unit>
3222 <trans-unit id="4774348799569692380" datatype="html"> 3232 <trans-unit id="4774348799569692380" datatype="html">
3223 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source> 3233 <source>Showing <x id="INTERPOLATION"/> to <x id="INTERPOLATION_1"/> of <x id="INTERPOLATION_2"/> hosts</source>
3224 <target state="translated">正在顯示 <x id="INTERPOLATION"/> 到 <x id="INTERPOLATION_1"/>,總共有 <x id="INTERPOLATION_2"/> 個主機</target> 3234 <target state="translated">正在顯示 <x id="INTERPOLATION"/> 到 <x id="INTERPOLATION_1"/>,總共有 <x id="INTERPOLATION_2"/> 個主機</target>
3225 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group> 3235 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">11</context></context-group>
3226 </trans-unit> 3236 </trans-unit>
3227 <trans-unit id="6275803119759621687" datatype="html"> 3237
3228 <source>Follow domains</source> 3238
3229 <target state="translated">追蹤網域</target>
3230 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">78</context></context-group>
3231 </trans-unit>
3232 <trans-unit id="1268699198448750610" datatype="html">
3233 <source>Follow instances</source>
3234 <target state="translated">追蹤站台</target>
3235 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">18</context></context-group>
3236 </trans-unit>
3237 <trans-unit id="9216117865911519658" datatype="html"> 3239 <trans-unit id="9216117865911519658" datatype="html">
3238 <source>Action</source> 3240 <source>Action</source>
3239 <target state="translated">動作</target> 3241 <target state="translated">動作</target>
@@ -3262,9 +3264,9 @@ The link will expire within 1 hour.</source>
3262 <trans-unit id="8286337167859377104"> 3264 <trans-unit id="8286337167859377104">
3263 <source>Create user</source> 3265 <source>Create user</source>
3264 <target>建立使用者</target> 3266 <target>建立使用者</target>
3265 3267 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-create.component.ts</context><context context-type="linenumber">93</context></context-group>
3266 3268 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.html</context><context context-type="linenumber">20</context></context-group>
3267 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-create.component.ts</context><context context-type="linenumber">93</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.html</context><context context-type="linenumber">20</context></context-group></trans-unit> 3269 </trans-unit>
3268 <trans-unit id="8363291180171434623" datatype="html"> 3270 <trans-unit id="8363291180171434623" datatype="html">
3269 <source>Table parameters</source> 3271 <source>Table parameters</source>
3270 <target state="translated">參數表</target> 3272 <target state="translated">參數表</target>
@@ -3283,11 +3285,11 @@ The link will expire within 1 hour.</source>
3283 <trans-unit id="5248717555542428023"> 3285 <trans-unit id="5248717555542428023">
3284 <source>Username</source> 3286 <source>Username</source>
3285 <target>使用者名稱</target> 3287 <target>使用者名稱</target>
3286 <context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group> 3288
3287 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group> 3289
3288 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3290
3289 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group> 3291
3290 </trans-unit> 3292 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">83</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">111</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html</context><context context-type="linenumber">6</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+signup/+register/register-step-user.component.html</context><context context-type="linenumber">23</context></context-group></trans-unit>
3291 <trans-unit id="5428411040014095392" datatype="html"> 3293 <trans-unit id="5428411040014095392" datatype="html">
3292 <source>e.g. jane_doe</source> 3294 <source>e.g. jane_doe</source>
3293 <target state="translated">例如:jane_doe</target> 3295 <target state="translated">例如:jane_doe</target>
@@ -3315,9 +3317,9 @@ The link will expire within 1 hour.</source>
3315 <trans-unit id="4145496584631696119"> 3317 <trans-unit id="4145496584631696119">
3316 <source>Role</source> 3318 <source>Role</source>
3317 <target>角色</target> 3319 <target>角色</target>
3318 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3320
3319 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group> 3321
3320 </trans-unit> 3322 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">136</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">114</context></context-group></trans-unit>
3321 <trans-unit id="7046347992315328430" datatype="html"> 3323 <trans-unit id="7046347992315328430" datatype="html">
3322 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source> 3324 <source>Transcoding is enabled. The video quota only takes into account <x id="START_TAG_STRONG"/>original<x id="CLOSE_TAG_STRONG"/> video size. <x id="LINE_BREAK"/> At most, this user could upload ~ <x id="INTERPOLATION"/>. </source>
3323 <target state="translated">轉換編碼已啟用。影片配額僅考慮<x id="START_TAG_STRONG"/>原始<x id="CLOSE_TAG_STRONG"/>影片大小。<x id="LINE_BREAK"/>此使用者最多只能上傳 ~ <x id="INTERPOLATION"/>。 </target> 3325 <target state="translated">轉換編碼已啟用。影片配額僅考慮<x id="START_TAG_STRONG"/>原始<x id="CLOSE_TAG_STRONG"/>影片大小。<x id="LINE_BREAK"/>此使用者最多只能上傳 ~ <x id="INTERPOLATION"/>。 </target>
@@ -3334,15 +3336,9 @@ The link will expire within 1 hour.</source>
3334 <trans-unit id="2622255144026150901" datatype="html"> 3336 <trans-unit id="2622255144026150901" datatype="html">
3335 <source>Auth plugin</source> 3337 <source>Auth plugin</source>
3336 <target state="translated">驗證外掛程式</target> 3338 <target state="translated">驗證外掛程式</target>
3337 <context-group purpose="location"> 3339
3338 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context> 3340
3339 <context context-type="linenumber">188</context> 3341 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context><context context-type="linenumber">188</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">121</context></context-group></trans-unit>
3340 </context-group>
3341 <context-group purpose="location">
3342 <context context-type="sourcefile">src/app/+admin/users/user-edit/user-edit.component.html</context>
3343 <context context-type="linenumber">188</context>
3344 </context-group>
3345 </trans-unit>
3346 <trans-unit id="588099657508661970" datatype="html"> 3342 <trans-unit id="588099657508661970" datatype="html">
3347 <source>None (local authentication)</source> 3343 <source>None (local authentication)</source>
3348 <target state="translated">無(本機驗證)</target> 3344 <target state="translated">無(本機驗證)</target>
@@ -3593,6 +3589,12 @@ The link will expire within 1 hour.</source>
3593 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group> 3589 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/moderation/video-comment-list/video-comment-list.component.html</context><context context-type="linenumber">65</context></context-group>
3594 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group> 3590 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-ownership/my-ownership.component.html</context><context context-type="linenumber">18</context></context-group>
3595 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group> 3591 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-abuse-list/abuse-list-table.component.html</context><context context-type="linenumber">41</context></context-group>
3592 </trans-unit><trans-unit id="8390803680962035202" datatype="html">
3593 <source>Follower</source><target state="new">Follower</target>
3594 <context-group purpose="location">
3595 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
3596 <context context-type="linenumber">24</context>
3597 </context-group>
3596 </trans-unit> 3598 </trans-unit>
3597 <trans-unit id="4691552465058437520" datatype="html"> 3599 <trans-unit id="4691552465058437520" datatype="html">
3598 <source>Commented video</source> 3600 <source>Commented video</source>
@@ -3892,8 +3894,8 @@ The link will expire within 1 hour.</source>
3892 <trans-unit id="4917252294930256268" datatype="html"> 3894 <trans-unit id="4917252294930256268" datatype="html">
3893 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source> 3895 <source>It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.</source>
3894 <target state="translated">看起來您似乎不在 HTTPS 伺服器上。您的網路伺服器必須啟用 TLS 才能追蹤伺服器。</target> 3896 <target state="translated">看起來您似乎不在 HTTPS 伺服器上。您的網路伺服器必須啟用 TLS 才能追蹤伺服器。</target>
3895 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">81</context></context-group> 3897
3896 </trans-unit> 3898 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context><context context-type="linenumber">28</context></context-group></trans-unit>
3897 <trans-unit id="4058814854824495833" datatype="html"> 3899 <trans-unit id="4058814854824495833" datatype="html">
3898 <source>Mute domains</source> 3900 <source>Mute domains</source>
3899 <target state="translated">靜音網域</target> 3901 <target state="translated">靜音網域</target>
@@ -5847,11 +5849,8 @@ color: red;
5847 <trans-unit id="5512878593724620692" datatype="html"> 5849 <trans-unit id="5512878593724620692" datatype="html">
5848 <source>CHANNELS</source> 5850 <source>CHANNELS</source>
5849 <target state="translated">頻道</target> 5851 <target state="translated">頻道</target>
5850 <context-group purpose="location"> 5852
5851 <context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context> 5853 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">81</context></context-group></trans-unit>
5852 <context context-type="linenumber">82</context>
5853 </context-group>
5854 </trans-unit>
5855 <trans-unit id="3666829335406793239"> 5854 <trans-unit id="3666829335406793239">
5856 <source>This account does not have channels.</source> 5855 <source>This account does not have channels.</source>
5857 <target>此帳號沒有頻道。</target> 5856 <target>此帳號沒有頻道。</target>
@@ -5890,6 +5889,12 @@ It will delete <x id="PH_1" equiv-text="videoChannel.videosCount"/> videos uploa
5890channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source> 5889channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</source>
5891 <target state="translated">您真的想要刪除 <x id="PH" equiv-text="videoChannel.displayName"/> 嗎?其將會刪除 <x id="PH_1" equiv-text="videoChannel.videosCount"/> 部上傳至此頻道的影片,且您將無法建立其他同名的頻道 (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target> 5890 <target state="translated">您真的想要刪除 <x id="PH" equiv-text="videoChannel.displayName"/> 嗎?其將會刪除 <x id="PH_1" equiv-text="videoChannel.videosCount"/> 部上傳至此頻道的影片,且您將無法建立其他同名的頻道 (<x id="PH_2" equiv-text="videoChannel.name"/>)!</target>
5892 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group> 5891 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">44</context></context-group>
5892 </trans-unit><trans-unit id="4433306639366959484" datatype="html">
5893 <source>Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</source><target state="new">Please type the name of the video channel (<x id="PH" equiv-text="videoChannel.name"/>) to confirm</target>
5894 <context-group purpose="location">
5895 <context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context>
5896 <context context-type="linenumber">48</context>
5897 </context-group>
5893 </trans-unit> 5898 </trans-unit>
5894 <trans-unit id="5387007581996837469" datatype="html"> 5899 <trans-unit id="5387007581996837469" datatype="html">
5895 <source>My Channels</source> 5900 <source>My Channels</source>
@@ -6405,13 +6410,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6405 <trans-unit id="6979021199788941693"> 6410 <trans-unit id="6979021199788941693">
6406 <source>Your message has been sent.</source> 6411 <source>Your message has been sent.</source>
6407 <target>您的訊息已被傳送。</target> 6412 <target>您的訊息已被傳送。</target>
6408 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">89</context></context-group> 6413
6409 </trans-unit> 6414 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">88</context></context-group></trans-unit>
6410 <trans-unit id="2072135752262464360"> 6415 <trans-unit id="2072135752262464360">
6411 <source>You already sent this form recently</source> 6416 <source>You already sent this form recently</source>
6412 <target>您最近已發送此表單</target> 6417 <target>您最近已發送此表單</target>
6413 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">95</context></context-group> 6418
6414 </trans-unit> 6419 <context-group purpose="location"><context context-type="sourcefile">src/app/+about/about-instance/contact-admin-modal.component.ts</context><context context-type="linenumber">94</context></context-group></trans-unit>
6415 <trans-unit id="819067926858619041" datatype="html"> 6420 <trans-unit id="819067926858619041" datatype="html">
6416 <source>Account videos</source> 6421 <source>Account videos</source>
6417 <target state="translated">帳號影片</target> 6422 <target state="translated">帳號影片</target>
@@ -6456,13 +6461,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6456 <target state="translated"> 6461 <target state="translated">
6457 <x id="PH"/> 直接帳號追蹤者 6462 <x id="PH"/> 直接帳號追蹤者
6458 </target> 6463 </target>
6459 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">155</context></context-group> 6464
6460 </trans-unit> 6465 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">154</context></context-group></trans-unit>
6461 <trans-unit id="6250999352462648289" datatype="html"> 6466 <trans-unit id="6250999352462648289" datatype="html">
6462 <source>Report this account</source> 6467 <source>Report this account</source>
6463 <target state="translated">回報此帳號</target> 6468 <target state="translated">回報此帳號</target>
6464 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">196</context></context-group> 6469
6465 </trans-unit> 6470 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">195</context></context-group></trans-unit>
6466 <trans-unit id="1504521795586863905" datatype="html"> 6471 <trans-unit id="1504521795586863905" datatype="html">
6467 <source>VIDEOS</source> 6472 <source>VIDEOS</source>
6468 <target state="translated">影片</target> 6473 <target state="translated">影片</target>
@@ -6472,31 +6477,21 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6472 <trans-unit id="25349740244798533"> 6477 <trans-unit id="25349740244798533">
6473 <source>Username copied</source> 6478 <source>Username copied</source>
6474 <target>使用者名稱已複製</target> 6479 <target>使用者名稱已複製</target>
6475 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">121</context></context-group> 6480
6476 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group> 6481
6477 </trans-unit> 6482 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">120</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">103</context></context-group></trans-unit>
6478 <trans-unit id="9221735175659318025" datatype="html"> 6483 <trans-unit id="9221735175659318025" datatype="html">
6479 <source>1 subscriber</source> 6484 <source>1 subscriber</source>
6480 <target state="translated">1 個訂閱者</target> 6485 <target state="translated">1 個訂閱者</target>
6481 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">125</context></context-group> 6486
6482 </trans-unit> 6487 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">124</context></context-group></trans-unit>
6483 <trans-unit id="4097331874769079975" datatype="html"> 6488 <trans-unit id="4097331874769079975" datatype="html">
6484 <source><x id="PH"/> subscribers</source> 6489 <source><x id="PH"/> subscribers</source>
6485 <target state="translated"><x id="PH"/> 個訂閱者</target> 6490 <target state="translated"><x id="PH"/> 個訂閱者</target>
6486 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">127</context></context-group> 6491
6487 </trans-unit> 6492 <context-group purpose="location"><context context-type="sourcefile">src/app/+accounts/accounts.component.ts</context><context context-type="linenumber">126</context></context-group></trans-unit>
6488 <trans-unit id="4682675125751819107" datatype="html"> 6493
6489 <source>Instances you follow</source> 6494
6490 <target state="translated">您追蹤的站臺</target>
6491 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
6492 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context><context context-type="linenumber">3</context></context-group>
6493 </trans-unit>
6494 <trans-unit id="8899833753704589712" datatype="html">
6495 <source>Instances following you</source>
6496 <target state="translated">追蹤您的站臺</target>
6497 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
6498 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context><context context-type="linenumber">3</context></context-group>
6499 </trans-unit>
6500 <trans-unit id="1035838766454786107" datatype="html"> 6495 <trans-unit id="1035838766454786107" datatype="html">
6501 <source>Audio-only</source> 6496 <source>Audio-only</source>
6502 <target state="translated">僅音訊</target> 6497 <target state="translated">僅音訊</target>
@@ -6546,6 +6541,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6546 <source>Auto (via ffmpeg)</source> 6541 <source>Auto (via ffmpeg)</source>
6547 <target>自動(透過 ffmpeg)</target> 6542 <target>自動(透過 ffmpeg)</target>
6548 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group> 6543 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/config/shared/config.service.ts</context><context context-type="linenumber">50</context></context-group>
6544 </trans-unit><trans-unit id="3642770981085338761" datatype="html">
6545 <source>Followers of your instance</source><target state="new">Followers of your instance</target>
6546 <context-group purpose="location">
6547 <context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.html</context>
6548 <context context-type="linenumber">3</context>
6549 </context-group>
6549 </trans-unit> 6550 </trans-unit>
6550 <trans-unit id="931255636742351800" datatype="html"> 6551 <trans-unit id="931255636742351800" datatype="html">
6551 <source>No limit</source> 6552 <source>No limit</source>
@@ -6694,18 +6695,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6694 <trans-unit id="2127446333083057097" datatype="html"> 6695 <trans-unit id="2127446333083057097" datatype="html">
6695 <source>Domain is required.</source> 6696 <source>Domain is required.</source>
6696 <target state="translated">網域必填。</target> 6697 <target state="translated">網域必填。</target>
6697 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">56</context></context-group> 6698
6698 </trans-unit> 6699 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">92</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">101</context></context-group></trans-unit><trans-unit id="7951488350851416577" datatype="html">
6699 <trans-unit id="6780793142903080663" datatype="html"> 6700 <source>Hosts entered are invalid.</source><target state="new">Hosts entered are invalid.</target>
6700 <source>Domains entered are invalid.</source> 6701 <context-group purpose="location">
6701 <target state="translated">輸入的域名無效。</target> 6702 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6702 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">57</context></context-group> 6703 <context context-type="linenumber">93</context>
6703 </trans-unit> 6704 </context-group>
6704 <trans-unit id="5886492514458202177" datatype="html"> 6705 </trans-unit><trans-unit id="1469559036084108672" datatype="html">
6705 <source>Domains entered contain duplicates.</source> 6706 <source>Hosts entered contain duplicates.</source><target state="new">Hosts entered contain duplicates.</target>
6706 <target state="translated">輸入的域名包含重覆的項目。</target> 6707 <context-group purpose="location">
6707 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">58</context></context-group> 6708 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6709 <context context-type="linenumber">94</context>
6710 </context-group>
6711 </trans-unit><trans-unit id="5991533283446904296" datatype="html">
6712 <source>Hosts or handles are invalid.</source><target state="new">Hosts or handles are invalid.</target>
6713 <context-group purpose="location">
6714 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6715 <context context-type="linenumber">102</context>
6716 </context-group>
6717 </trans-unit><trans-unit id="6759198394434886237" datatype="html">
6718 <source>Hosts or handles contain duplicates.</source><target state="new">Hosts or handles contain duplicates.</target>
6719 <context-group purpose="location">
6720 <context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context>
6721 <context context-type="linenumber">103</context>
6722 </context-group>
6708 </trans-unit> 6723 </trans-unit>
6724
6725
6709 <trans-unit id="240806681889331244"> 6726 <trans-unit id="240806681889331244">
6710 <source>Unlimited</source> 6727 <source>Unlimited</source>
6711 <target>無限制</target> 6728 <target>無限制</target>
@@ -6869,24 +6886,50 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6869 <source><x id="PH"/> removed from instance followers </source> 6886 <source><x id="PH"/> removed from instance followers </source>
6870 <target><x id="PH"/> 已從站臺追蹤者中移除 </target> 6887 <target><x id="PH"/> 已從站臺追蹤者中移除 </target>
6871 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group> 6888 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/followers-list/followers-list.component.ts</context><context context-type="linenumber">81</context></context-group>
6889 </trans-unit><trans-unit id="6018246591673612412" datatype="html">
6890 <source>Follow</source><target state="new">Follow</target>
6891 <context-group purpose="location">
6892 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6893 <context context-type="linenumber">3</context>
6894 </context-group>
6895 <context-group purpose="location">
6896 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6897 <context context-type="linenumber">37</context>
6898 </context-group>
6899 <context-group purpose="location">
6900 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6901 <context context-type="linenumber">18</context>
6902 </context-group>
6903 </trans-unit><trans-unit id="3596798855644241001" datatype="html">
6904 <source>1 host (without "http://"), account handle or channel handle per line</source><target state="new">1 host (without "http://"), account handle or channel handle per line</target>
6905 <context-group purpose="location">
6906 <context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.html</context>
6907 <context context-type="linenumber">11</context>
6908 </context-group>
6872 </trans-unit> 6909 </trans-unit>
6873 <trans-unit id="2740793005745065895"> 6910 <trans-unit id="2740793005745065895">
6874 <source><x id="PH"/> is not valid </source> 6911 <source><x id="PH"/> is not valid </source>
6875 <target> 6912 <target>
6876 <x id="PH"/> 無效 6913 <x id="PH"/> 無效
6877 </target> 6914 </target>
6878 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/batch-domains-validators.ts</context><context context-type="linenumber">19</context></context-group> 6915
6879 </trans-unit> 6916 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">27</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/shared/form-validators/host-validators.ts</context><context context-type="linenumber">50</context></context-group></trans-unit>
6880 <trans-unit id="2355066641781598196"> 6917 <trans-unit id="2355066641781598196">
6881 <source>Follow request(s) sent!</source> 6918 <source>Follow request(s) sent!</source>
6882 <target>追蹤請求已傳送!</target> 6919 <target>追蹤請求已傳送!</target>
6883 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group> 6920
6921 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/follow-modal.component.ts</context><context context-type="linenumber">62</context></context-group></trans-unit><trans-unit id="3459358413436264734" datatype="html">
6922 <source>Your instance subscriptions</source><target state="new">Your instance subscriptions</target>
6923 <context-group purpose="location">
6924 <context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.html</context>
6925 <context context-type="linenumber">3</context>
6926 </context-group>
6884 </trans-unit> 6927 </trans-unit>
6885 <trans-unit id="4245720728052819482"> 6928 <trans-unit id="4245720728052819482">
6886 <source>Do you really want to unfollow <x id="PH"/>?</source> 6929 <source>Do you really want to unfollow <x id="PH"/>?</source>
6887 <target>您想要取消追蹤 <x id="PH"/> 嗎?</target> 6930 <target>您想要取消追蹤 <x id="PH"/> 嗎?</target>
6888 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">57</context></context-group> 6931
6889 </trans-unit> 6932 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">47</context></context-group></trans-unit>
6890 <trans-unit id="9160510009013134726"> 6933 <trans-unit id="9160510009013134726">
6891 <source>Unfollow</source> 6934 <source>Unfollow</source>
6892 <target>取消追蹤</target> 6935 <target>取消追蹤</target>
@@ -6895,8 +6938,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
6895 <trans-unit id="3935234189109112926"> 6938 <trans-unit id="3935234189109112926">
6896 <source>You are not following <x id="PH"/> anymore.</source> 6939 <source>You are not following <x id="PH"/> anymore.</source>
6897 <target>您無法再追蹤 <x id="PH"/>。</target> 6940 <target>您無法再追蹤 <x id="PH"/>。</target>
6898 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">64</context></context-group> 6941
6899 </trans-unit> 6942 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/following-list/following-list.component.ts</context><context context-type="linenumber">54</context></context-group></trans-unit>
6900 <trans-unit id="2593763089859685916"> 6943 <trans-unit id="2593763089859685916">
6901 <source>enabled</source> 6944 <source>enabled</source>
6902 <target>已啟用</target> 6945 <target>已啟用</target>
@@ -7368,9 +7411,9 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7368 <trans-unit id="1519954996184640001"> 7411 <trans-unit id="1519954996184640001">
7369 <source>Error</source> 7412 <source>Error</source>
7370 <target>錯誤</target> 7413 <target>錯誤</target>
7371 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">104</context></context-group> 7414
7372 <context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group> 7415
7373 </trans-unit> 7416 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">103</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/core/notification/notifier.service.ts</context><context context-type="linenumber">18</context></context-group></trans-unit>
7374 <trans-unit id="5076187961693950167" datatype="html"> 7417 <trans-unit id="5076187961693950167" datatype="html">
7375 <source>Standard logs</source> 7418 <source>Standard logs</source>
7376 <target state="translated">標準日誌</target> 7419 <target state="translated">標準日誌</target>
@@ -7384,8 +7427,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7384 <trans-unit id="1886888801485703107"> 7427 <trans-unit id="1886888801485703107">
7385 <source>User <x id="PH"/> created.</source> 7428 <source>User <x id="PH"/> created.</source>
7386 <target>使用者 <x id="PH"/> 已建立。</target> 7429 <target>使用者 <x id="PH"/> 已建立。</target>
7387 7430 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-create.component.ts</context><context context-type="linenumber">76</context></context-group>
7388 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-create.component.ts</context><context context-type="linenumber">76</context></context-group></trans-unit> 7431 </trans-unit>
7389 <trans-unit id="8286337167859377104" datatype="html"> 7432 <trans-unit id="8286337167859377104" datatype="html">
7390 <source>Create user</source> 7433 <source>Create user</source>
7391 <target state="translated">建立使用者</target> 7434 <target state="translated">建立使用者</target>
@@ -7411,16 +7454,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7411 <target>更新使用者密碼</target> 7454 <target>更新使用者密碼</target>
7412 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group> 7455 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-edit/user-password.component.ts</context><context context-type="linenumber">52</context></context-group>
7413 </trans-unit> 7456 </trans-unit>
7414 <trans-unit id="177544274549739411" datatype="html"> 7457
7415 <source>Following list</source> 7458
7416 <target state="translated">追蹤清單</target>
7417 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">28</context></context-group>
7418 </trans-unit>
7419 <trans-unit id="8092429110007204784" datatype="html">
7420 <source>Followers list</source>
7421 <target state="translated">追蹤者清單</target>
7422 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/follows/follows.routes.ts</context><context context-type="linenumber">37</context></context-group>
7423 </trans-unit>
7424 <trans-unit id="780323526182667308" datatype="html"> 7459 <trans-unit id="780323526182667308" datatype="html">
7425 <source>User <x id="PH"/> updated.</source> 7460 <source>User <x id="PH"/> updated.</source>
7426 <target state="translated">使用者 <x id="PH"/> 已更新。</target> 7461 <target state="translated">使用者 <x id="PH"/> 已更新。</target>
@@ -7456,16 +7491,8 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7456 <target state="translated">聯盟</target> 7491 <target state="translated">聯盟</target>
7457 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group> 7492 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">26</context></context-group>
7458 </trans-unit> 7493 </trans-unit>
7459 <trans-unit id="4682675125751819107" datatype="html"> 7494
7460 <source>Instances you follow</source> 7495
7461 <target state="translated">您追蹤的站臺</target>
7462 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">29</context></context-group>
7463 </trans-unit>
7464 <trans-unit id="8899833753704589712" datatype="html">
7465 <source>Instances following you</source>
7466 <target state="translated">追蹤您的站臺</target>
7467 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/admin.component.ts</context><context context-type="linenumber">34</context></context-group>
7468 </trans-unit>
7469 <trans-unit id="3767259920053407667" datatype="html"> 7496 <trans-unit id="3767259920053407667" datatype="html">
7470 <source>Videos will be deleted, comments will be tombstoned.</source> 7497 <source>Videos will be deleted, comments will be tombstoned.</source>
7471 <target state="translated">影片與留言都將會被刪除。</target> 7498 <target state="translated">影片與留言都將會被刪除。</target>
@@ -7496,6 +7523,24 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7496 <target>設定電子郵件為已驗證</target> 7523 <target>設定電子郵件為已驗證</target>
7497 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group> 7524 <context-group purpose="location"><context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context><context context-type="linenumber">100</context></context-group>
7498 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group> 7525 <context-group purpose="location"><context context-type="sourcefile">src/app/shared/shared-moderation/user-moderation-dropdown.component.ts</context><context context-type="linenumber">281</context></context-group>
7526 </trans-unit><trans-unit id="4207916966377787111" datatype="html">
7527 <source>Created</source><target state="new">Created</target>
7528 <context-group purpose="location">
7529 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7530 <context context-type="linenumber">115</context>
7531 </context-group>
7532 </trans-unit><trans-unit id="8140268298586972139" datatype="html">
7533 <source>Daily quota</source><target state="new">Daily quota</target>
7534 <context-group purpose="location">
7535 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7536 <context context-type="linenumber">120</context>
7537 </context-group>
7538 </trans-unit><trans-unit id="7910076708497708162" datatype="html">
7539 <source>Last login</source><target state="new">Last login</target>
7540 <context-group purpose="location">
7541 <context context-type="sourcefile">src/app/+admin/users/user-list/user-list.component.ts</context>
7542 <context context-type="linenumber">122</context>
7543 </context-group>
7499 </trans-unit> 7544 </trans-unit>
7500 <trans-unit id="3403978719736970622"> 7545 <trans-unit id="3403978719736970622">
7501 <source>You cannot ban root.</source> 7546 <source>You cannot ban root.</source>
@@ -7800,13 +7845,13 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7800 <trans-unit id="1137937154872046253"> 7845 <trans-unit id="1137937154872046253">
7801 <source>Video channel <x id="PH"/> created.</source> 7846 <source>Video channel <x id="PH"/> created.</source>
7802 <target>影片頻道 <x id="PH"/> 已更新。</target> 7847 <target>影片頻道 <x id="PH"/> 已更新。</target>
7803 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">67</context></context-group> 7848
7804 </trans-unit> 7849 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
7805 <trans-unit id="8723777130353305761"> 7850 <trans-unit id="8723777130353305761">
7806 <source>This name already exists on this instance.</source> 7851 <source>This name already exists on this instance.</source>
7807 <target>此名稱已存在於此站臺上。</target> 7852 <target>此名稱已存在於此站臺上。</target>
7808 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">73</context></context-group> 7853
7809 </trans-unit> 7854 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts</context><context context-type="linenumber">72</context></context-group></trans-unit>
7810 <trans-unit id="7589345916094713536"> 7855 <trans-unit id="7589345916094713536">
7811 <source>Video channel <x id="PH"/> updated.</source> 7856 <source>Video channel <x id="PH"/> updated.</source>
7812 <target>影片頻道 <x id="PH"/> 已更新。</target> 7857 <target>影片頻道 <x id="PH"/> 已更新。</target>
@@ -7827,11 +7872,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7827 <target state="translated">橫幅已刪除。</target> 7872 <target state="translated">橫幅已刪除。</target>
7828 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group> 7873 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts</context><context context-type="linenumber">152</context></context-group>
7829 </trans-unit> 7874 </trans-unit>
7830 <trans-unit id="2575302837003821736"> 7875
7831 <source>Please type the display name of the video channel (<x id="PH"/>) to confirm</source>
7832 <target>請輸入影片頻道的顯示名稱 ( <x id="PH"/>) 以確認</target>
7833 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/+my-video-channels/my-video-channels.component.ts</context><context context-type="linenumber">48</context></context-group>
7834 </trans-unit>
7835 <trans-unit id="624066830180032195"> 7876 <trans-unit id="624066830180032195">
7836 <source>Video channel <x id="PH"/> deleted.</source> 7877 <source>Video channel <x id="PH"/> deleted.</source>
7837 <target>影片頻道 <x id="PH"/> 已刪除。</target> 7878 <target>影片頻道 <x id="PH"/> 已刪除。</target>
@@ -7981,6 +8022,12 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
7981 <source>Ownership change request sent.</source> 8022 <source>Ownership change request sent.</source>
7982 <target>所有權變更請求已發送。</target> 8023 <target>所有權變更請求已發送。</target>
7983 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group> 8024 <context-group purpose="location"><context context-type="sourcefile">src/app/+my-library/my-videos/modals/video-change-ownership.component.ts</context><context context-type="linenumber">64</context></context-group>
8025 </trans-unit><trans-unit id="7699622144571229146" datatype="html">
8026 <source>Sort by</source><target state="new">Sort by</target>
8027 <context-group purpose="location">
8028 <context context-type="sourcefile">src/app/+my-library/my-videos/my-videos.component.html</context>
8029 <context context-type="linenumber">26</context>
8030 </context-group>
7984 </trans-unit> 8031 </trans-unit>
7985 <trans-unit id="3245220240937722814"> 8032 <trans-unit id="3245220240937722814">
7986 <source>My channels</source> 8033 <source>My channels</source>
@@ -8079,7 +8126,7 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8079 <target>訂閱帳號</target> 8126 <target>訂閱帳號</target>
8080 8127
8081 8128
8082 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">704</context></context-group></trans-unit> 8129 <context-group purpose="location"><context context-type="sourcefile">src/app/+video-channels/video-channels.component.ts</context><context context-type="linenumber">71</context></context-group><context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">711</context></context-group></trans-unit>
8083 <trans-unit id="3131904093925601441" datatype="html"> 8130 <trans-unit id="3131904093925601441" datatype="html">
8084 <source>PLAYLISTS</source> 8131 <source>PLAYLISTS</source>
8085 <target state="translated">播放清單</target> 8132 <target state="translated">播放清單</target>
@@ -8126,34 +8173,34 @@ channel with the same name (<x id="PH_2" equiv-text="videoChannel.name"/>)!</sou
8126 <trans-unit id="3779524668013120370"> 8173 <trans-unit id="3779524668013120370">
8127 <source>Go to my subscriptions</source> 8174 <source>Go to my subscriptions</source>
8128 <target>前往我的訂閱</target> 8175 <target>前往我的訂閱</target>
8129 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">64</context></context-group> 8176
8130 </trans-unit> 8177 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">63</context></context-group></trans-unit>
8131 <trans-unit id="1136469849928650779"> 8178 <trans-unit id="1136469849928650779">
8132 <source>Go to my videos</source> 8179 <source>Go to my videos</source>
8133 <target>前往我的影片</target> 8180 <target>前往我的影片</target>
8134 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">68</context></context-group> 8181
8135 </trans-unit> 8182 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">67</context></context-group></trans-unit>
8136 <trans-unit id="7836683738999600376"> 8183 <trans-unit id="7836683738999600376">
8137 <source>Go to my imports</source> 8184 <source>Go to my imports</source>
8138 <target>前往我的匯入</target> 8185 <target>前往我的匯入</target>
8139 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">72</context></context-group> 8186
8140 </trans-unit> 8187 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
8141 <trans-unit id="7511292153332773503"> 8188 <trans-unit id="7511292153332773503">
8142 <source>Go to my channels</source> 8189 <source>Go to my channels</source>
8143 <target>前往我的頻道</target> 8190 <target>前往我的頻道</target>
8144 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">76</context></context-group> 8191
8145 </trans-unit> 8192 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">75</context></context-group></trans-unit>
8146 <trans-unit id="2013324644839511073" datatype="html"> 8193 <trans-unit id="2013324644839511073" datatype="html">
8147 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>. 8194 <source>Cannot retrieve OAuth Client credentials: <x id="PH" equiv-text="error.text"/>.
8148Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source> 8195Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.</source>
8149 <target state="translated">無法擷取 OAuth 客戶端憑證:<x id="PH" equiv-text="error.text"/>。請確保您已正確設定 PeerTube(config/ 目錄),特別是 "webserver" 部份。</target> 8196 <target state="translated">無法擷取 OAuth 客戶端憑證:<x id="PH" equiv-text="error.text"/>。請確保您已正確設定 PeerTube(config/ 目錄),特別是 "webserver" 部份。</target>
8150 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">99</context></context-group> 8197
8151 </trans-unit> 8198 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">98</context></context-group></trans-unit>
8152 <trans-unit id="375263728166936544"> 8199 <trans-unit id="375263728166936544">
8153 <source>You need to reconnect.</source> 8200 <source>You need to reconnect.</source>
8154 <target>您需要重新連線。</target> 8201 <target>您需要重新連線。</target>
8155 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">220</context></context-group> 8202
8156 </trans-unit> 8203 <context-group purpose="location"><context context-type="sourcefile">src/app/core/auth/auth.service.ts</context><context context-type="linenumber">219</context></context-group></trans-unit>
8157 <trans-unit id="2206638022166154361"> 8204 <trans-unit id="2206638022166154361">
8158 <source>Keyboard Shortcuts:</source> 8205 <source>Keyboard Shortcuts:</source>
8159 <target>鍵盤快捷鍵:</target> 8206 <target>鍵盤快捷鍵:</target>
@@ -8166,6 +8213,12 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8166 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context> 8213 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8167 <context context-type="linenumber">98</context> 8214 <context context-type="linenumber">98</context>
8168 </context-group> 8215 </context-group>
8216 </trans-unit><trans-unit id="4024404994702813072" datatype="html">
8217 <source>In my library</source><target state="new">In my library</target>
8218 <context-group purpose="location">
8219 <context context-type="sourcefile">src/app/core/menu/menu.service.ts</context>
8220 <context context-type="linenumber">104</context>
8221 </context-group>
8169 </trans-unit> 8222 </trans-unit>
8170 <trans-unit id="232050922346936574" datatype="html"> 8223 <trans-unit id="232050922346936574" datatype="html">
8171 <source>Trending</source> 8224 <source>Trending</source>
@@ -8194,38 +8247,38 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
8194 <trans-unit id="1266887509445371246"> 8247 <trans-unit id="1266887509445371246">
8195 <source>Incorrect username or password.</source> 8248 <source>Incorrect username or password.</source>
8196 <target>不正確的使用者名稱或密碼。</target> 8249 <target>不正確的使用者名稱或密碼。</target>
8197 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">159</context></context-group> 8250
8198 </trans-unit> 8251 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">163</context></context-group></trans-unit>
8199 <trans-unit id="6974874606619467663" datatype="html"> 8252 <trans-unit id="6974874606619467663" datatype="html">
8200 <source>Your account is blocked.</source> 8253 <source>Your account is blocked.</source>
8201 <target state="translated">您的帳號已被封鎖。</target> 8254 <target state="translated">您的帳號已被封鎖。</target>
8202 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">160</context></context-group> 8255
8203 </trans-unit> 8256 <context-group purpose="location"><context context-type="sourcefile">src/app/+login/login.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
8204 <trans-unit id="7939914198003891823" datatype="html"> 8257 <trans-unit id="7939914198003891823" datatype="html">
8205 <source>any language</source> 8258 <source>any language</source>
8206 <target state="translated">任何語言</target> 8259 <target state="translated">任何語言</target>
8207 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">263</context></context-group> 8260
8208 </trans-unit> 8261 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">266</context></context-group></trans-unit>
8209 <trans-unit id="5633144232269377096" datatype="html"> 8262 <trans-unit id="5633144232269377096" datatype="html">
8210 <source>hide</source> 8263 <source>hide</source>
8211 <target state="translated">隱藏</target> 8264 <target state="translated">隱藏</target>
8212 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">298</context></context-group> 8265
8213 </trans-unit> 8266 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">301</context></context-group></trans-unit>
8214 <trans-unit id="8603861867909474404" datatype="html"> 8267 <trans-unit id="8603861867909474404" datatype="html">
8215 <source>blur</source> 8268 <source>blur</source>
8216 <target state="translated">模糊</target> 8269 <target state="translated">模糊</target>
8217 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">302</context></context-group> 8270
8218 </trans-unit> 8271 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">305</context></context-group></trans-unit>
8219 <trans-unit id="4534458451100881847" datatype="html"> 8272 <trans-unit id="4534458451100881847" datatype="html">
8220 <source>display</source> 8273 <source>display</source>
8221 <target state="translated">顯示</target> 8274 <target state="translated">顯示</target>
8222 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">306</context></context-group> 8275
8223 </trans-unit> 8276 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">309</context></context-group></trans-unit>
8224 <trans-unit id="4467323362722952678" datatype="html"> 8277 <trans-unit id="4467323362722952678" datatype="html">
8225 <source>Unknown</source> 8278 <source>Unknown</source>
8226 <target state="translated">未知</target> 8279 <target state="translated">未知</target>
8227 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">193</context></context-group> 8280
8228 </trans-unit> 8281 <context-group purpose="location"><context context-type="sourcefile">src/app/menu/menu.component.ts</context><context context-type="linenumber">196</context></context-group></trans-unit>
8229 <trans-unit id="8781423666414310853"> 8282 <trans-unit id="8781423666414310853">
8230 <source>Your password has been successfully reset!</source> 8283 <source>Your password has been successfully reset!</source>
8231 <target>您的密碼已成功重設!</target> 8284 <target>您的密碼已成功重設!</target>
@@ -9803,18 +9856,18 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
9803 <trans-unit id="968295009933361070"> 9856 <trans-unit id="968295009933361070">
9804 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source> 9857 <source>Too many attempts, please try again after <x id="PH"/> minutes.</source>
9805 <target>太多次嘗試,請在 <x id="PH"/> 分鐘後再試。</target> 9858 <target>太多次嘗試,請在 <x id="PH"/> 分鐘後再試。</target>
9806 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">67</context></context-group> 9859
9807 </trans-unit> 9860 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">66</context></context-group></trans-unit>
9808 <trans-unit id="4965472196059235310"> 9861 <trans-unit id="4965472196059235310">
9809 <source>Too many attempts, please try again later.</source> 9862 <source>Too many attempts, please try again later.</source>
9810 <target>太多次嘗試,請稍後再試。</target> 9863 <target>太多次嘗試,請稍後再試。</target>
9811 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">69</context></context-group> 9864
9812 </trans-unit> 9865 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">68</context></context-group></trans-unit>
9813 <trans-unit id="1693549688987384699"> 9866 <trans-unit id="1693549688987384699">
9814 <source>Server error. Please retry later.</source> 9867 <source>Server error. Please retry later.</source>
9815 <target>伺服器錯誤。請稍後重試。</target> 9868 <target>伺服器錯誤。請稍後重試。</target>
9816 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">72</context></context-group> 9869
9817 </trans-unit> 9870 <context-group purpose="location"><context context-type="sourcefile">src/app/core/rest/rest-extractor.service.ts</context><context context-type="linenumber">71</context></context-group></trans-unit>
9818 <trans-unit id="5927402622550505067" datatype="html"> 9871 <trans-unit id="5927402622550505067" datatype="html">
9819 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source> 9872 <source>Subscribed to all current channels of <x id="PH"/>. You will be notified of all their new videos.</source>
9820 <target state="translated">訂閱 <x id="PH"/> 目前的所有頻道。您將會收到它們所有的新影片。</target> 9873 <target state="translated">訂閱 <x id="PH"/> 目前的所有頻道。您將會收到它們所有的新影片。</target>
@@ -10393,35 +10446,35 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10393 <trans-unit id="3284171506518522275"> 10446 <trans-unit id="3284171506518522275">
10394 <source>Your video was uploaded to your account and is private.</source> 10447 <source>Your video was uploaded to your account and is private.</source>
10395 <target>您的影片已上傳到您的帳號並為私人影片。</target> 10448 <target>您的影片已上傳到您的帳號並為私人影片。</target>
10396 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group> 10449
10397 </trans-unit> 10450 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">161</context></context-group></trans-unit>
10398 <trans-unit id="5699822024600815733"> 10451 <trans-unit id="5699822024600815733">
10399 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source> 10452 <source>But associated data (tags, description...) will be lost, are you sure you want to leave this page?</source>
10400 <target>但相關資料(標籤、描述等)將會遺失,您確定您想要離開此頁面嗎?</target> 10453 <target>但相關資料(標籤、描述等)將會遺失,您確定您想要離開此頁面嗎?</target>
10401 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">163</context></context-group> 10454
10402 </trans-unit> 10455 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">162</context></context-group></trans-unit>
10403 <trans-unit id="1219739004043110649"> 10456 <trans-unit id="1219739004043110649">
10404 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source> 10457 <source>Your video is not uploaded yet, are you sure you want to leave this page?</source>
10405 <target>您的影片尚未上傳,您確定您想要離開此頁面嗎?</target> 10458 <target>您的影片尚未上傳,您確定您想要離開此頁面嗎?</target>
10406 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">165</context></context-group> 10459
10407 </trans-unit> 10460 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">164</context></context-group></trans-unit>
10408 <trans-unit id="6932865105766151309" datatype="html"> 10461 <trans-unit id="6932865105766151309" datatype="html">
10409 <source>Upload</source> 10462 <source>Upload</source>
10410 <target state="translated">上傳</target> 10463 <target state="translated">上傳</target>
10411 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">222</context></context-group> 10464
10412 </trans-unit> 10465 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">221</context></context-group></trans-unit>
10413 <trans-unit id="8278735427925094503"> 10466 <trans-unit id="8278735427925094503">
10414 <source>Upload <x id="PH"/> </source> 10467 <source>Upload <x id="PH"/> </source>
10415 <target>上傳 10468 <target>上傳
10416 <x id="PH"/> 10469 <x id="PH"/>
10417 </target> 10470 </target>
10418 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">224</context></context-group> 10471
10419 </trans-unit> 10472 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">223</context></context-group></trans-unit>
10420 <trans-unit id="5981816353437801748"> 10473 <trans-unit id="5981816353437801748">
10421 <source>Video published.</source> 10474 <source>Video published.</source>
10422 <target>影片已發佈。</target> 10475 <target>影片已發佈。</target>
10423 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">245</context></context-group> 10476
10424 </trans-unit> 10477 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-edit/video-add-components/video-upload.component.ts</context><context context-type="linenumber">244</context></context-group></trans-unit>
10425 <trans-unit id="764164089183618119"> 10478 <trans-unit id="764164089183618119">
10426 <source>You have unsaved changes! If you leave, your changes will be lost.</source> 10479 <source>You have unsaved changes! If you leave, your changes will be lost.</source>
10427 <target>您有未儲存的變更!如果您離開,您的變更將會遺失。</target> 10480 <target>您有未儲存的變更!如果您離開,您的變更將會遺失。</target>
@@ -10468,28 +10521,28 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10468 <trans-unit id="961774488937452220" datatype="html"> 10521 <trans-unit id="961774488937452220" datatype="html">
10469 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source> 10522 <source>This video is not available on this instance. Do you want to be redirected on the origin instance: &lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a>?</source>
10470 <target state="translated">此影片在此站臺上不可用。您想要重新導向至原始站臺:&lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a> 嗎?</target> 10523 <target state="translated">此影片在此站臺上不可用。您想要重新導向至原始站臺:&lt;a href="<x id="PH"/>"><x id="PH_1"/>&lt;/a> 嗎?</target>
10471 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">288</context></context-group> 10524
10472 </trans-unit> 10525 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">295</context></context-group></trans-unit>
10473 <trans-unit id="5761611056224181752" datatype="html"> 10526 <trans-unit id="5761611056224181752" datatype="html">
10474 <source>Redirection</source> 10527 <source>Redirection</source>
10475 <target state="translated">重新導向</target> 10528 <target state="translated">重新導向</target>
10476 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">289</context></context-group> 10529
10477 </trans-unit> 10530 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">296</context></context-group></trans-unit>
10478 <trans-unit id="8858527736400081688"> 10531 <trans-unit id="8858527736400081688">
10479 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source> 10532 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
10480 <target>這部影片包含成人或裸露內容。您確定您想要觀看嗎?</target> 10533 <target>這部影片包含成人或裸露內容。您確定您想要觀看嗎?</target>
10481 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">335</context></context-group> 10534
10482 </trans-unit> 10535 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">342</context></context-group></trans-unit>
10483 <trans-unit id="3937119019020041049"> 10536 <trans-unit id="3937119019020041049">
10484 <source>Mature or explicit content</source> 10537 <source>Mature or explicit content</source>
10485 <target>成人或裸露內容</target> 10538 <target>成人或裸露內容</target>
10486 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">336</context></context-group> 10539
10487 </trans-unit> 10540 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">343</context></context-group></trans-unit>
10488 <trans-unit id="1755474755114288376" datatype="html"> 10541 <trans-unit id="1755474755114288376" datatype="html">
10489 <source>Up Next</source> 10542 <source>Up Next</source>
10490 <target state="translated">往下</target> 10543 <target state="translated">往下</target>
10491 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">407</context></context-group> 10544
10492 </trans-unit> 10545 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">414</context></context-group></trans-unit>
10493 <trans-unit id="2159130950882492111" datatype="html"> 10546 <trans-unit id="2159130950882492111" datatype="html">
10494 <source>Cancel</source> 10547 <source>Cancel</source>
10495 <target state="translated">取消</target> 10548 <target state="translated">取消</target>
@@ -10498,63 +10551,63 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
10498 <trans-unit id="3354816756665089864" datatype="html"> 10551 <trans-unit id="3354816756665089864" datatype="html">
10499 <source>Autoplay is suspended</source> 10552 <source>Autoplay is suspended</source>
10500 <target state="translated">自動播放已暫停</target> 10553 <target state="translated">自動播放已暫停</target>
10501 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">409</context></context-group> 10554
10502 </trans-unit> 10555 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">416</context></context-group></trans-unit>
10503 <trans-unit id="7895294730547405228" datatype="html"> 10556 <trans-unit id="7895294730547405228" datatype="html">
10504 <source>Enter/exit fullscreen (requires player focus)</source> 10557 <source>Enter/exit fullscreen (requires player focus)</source>
10505 <target state="translated">進入/離開全螢幕(需要播放器焦點)</target> 10558 <target state="translated">進入/離開全螢幕(需要播放器焦點)</target>
10506 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">678</context></context-group> 10559
10507 </trans-unit> 10560 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group></trans-unit>
10508 <trans-unit id="7618388257165864759" datatype="html"> 10561 <trans-unit id="7618388257165864759" datatype="html">
10509 <source>Play/Pause the video (requires player focus)</source> 10562 <source>Play/Pause the video (requires player focus)</source>
10510 <target state="translated">播放/暫停影片(需要播放器焦點)</target> 10563 <target state="translated">播放/暫停影片(需要播放器焦點)</target>
10511 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">679</context></context-group> 10564
10512 </trans-unit> 10565 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">686</context></context-group></trans-unit>
10513 <trans-unit id="7761890399634216630" datatype="html"> 10566 <trans-unit id="7761890399634216630" datatype="html">
10514 <source>Mute/unmute the video (requires player focus)</source> 10567 <source>Mute/unmute the video (requires player focus)</source>
10515 <target state="translated">靜音/解除靜音影片(需要播放器焦點)</target> 10568 <target state="translated">靜音/解除靜音影片(需要播放器焦點)</target>
10516 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">680</context></context-group> 10569
10517 </trans-unit> 10570 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group></trans-unit>
10518 <trans-unit id="5996585232248234904" datatype="html"> 10571 <trans-unit id="5996585232248234904" datatype="html">
10519 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source> 10572 <source>Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)</source>
10520 <target state="translated">跳到影片的百分比:0 是 0%,9 是 90%(需要播放器焦點)</target> 10573 <target state="translated">跳到影片的百分比:0 是 0%,9 是 90%(需要播放器焦點)</target>
10521 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">682</context></context-group> 10574
10522 </trans-unit> 10575 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">689</context></context-group></trans-unit>
10523 <trans-unit id="3748765405903319998" datatype="html"> 10576 <trans-unit id="3748765405903319998" datatype="html">
10524 <source>Increase the volume (requires player focus)</source> 10577 <source>Increase the volume (requires player focus)</source>
10525 <target state="translated">增加音量(需要播放器焦點)</target> 10578 <target state="translated">增加音量(需要播放器焦點)</target>
10526 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">684</context></context-group> 10579
10527 </trans-unit> 10580 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group></trans-unit>
10528 <trans-unit id="5810704036407159982" datatype="html"> 10581 <trans-unit id="5810704036407159982" datatype="html">
10529 <source>Decrease the volume (requires player focus)</source> 10582 <source>Decrease the volume (requires player focus)</source>
10530 <target state="translated">降低音量(需要播放器焦點)</target> 10583 <target state="translated">降低音量(需要播放器焦點)</target>
10531 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">685</context></context-group> 10584
10532 </trans-unit> 10585 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">692</context></context-group></trans-unit>
10533 <trans-unit id="2622048822548065691" datatype="html"> 10586 <trans-unit id="2622048822548065691" datatype="html">
10534 <source>Seek the video forward (requires player focus)</source> 10587 <source>Seek the video forward (requires player focus)</source>
10535 <target state="translated">快轉影片(需要播放器焦點)</target> 10588 <target state="translated">快轉影片(需要播放器焦點)</target>
10536 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">687</context></context-group> 10589
10537 </trans-unit> 10590 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">694</context></context-group></trans-unit>
10538 <trans-unit id="6540078205109221153" datatype="html"> 10591 <trans-unit id="6540078205109221153" datatype="html">
10539 <source>Seek the video backward (requires player focus)</source> 10592 <source>Seek the video backward (requires player focus)</source>
10540 <target state="translated">向後快轉影片(需要播放器焦點)</target> 10593 <target state="translated">向後快轉影片(需要播放器焦點)</target>
10541 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">688</context></context-group> 10594
10542 </trans-unit> 10595 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">695</context></context-group></trans-unit>
10543 <trans-unit id="1956491957766210808" datatype="html"> 10596 <trans-unit id="1956491957766210808" datatype="html">
10544 <source>Increase playback rate (requires player focus)</source> 10597 <source>Increase playback rate (requires player focus)</source>
10545 <target state="translated">提高播放速度(需要播放器焦點)</target> 10598 <target state="translated">提高播放速度(需要播放器焦點)</target>
10546 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">690</context></context-group> 10599
10547 </trans-unit> 10600 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">697</context></context-group></trans-unit>
10548 <trans-unit id="5495529997674803186" datatype="html"> 10601 <trans-unit id="5495529997674803186" datatype="html">
10549 <source>Decrease playback rate (requires player focus)</source> 10602 <source>Decrease playback rate (requires player focus)</source>
10550 <target state="translated">減慢播放速度(需要播放器焦點)</target> 10603 <target state="translated">減慢播放速度(需要播放器焦點)</target>
10551 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">691</context></context-group> 10604
10552 </trans-unit> 10605 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">698</context></context-group></trans-unit>
10553 <trans-unit id="3178343147230721210" datatype="html"> 10606 <trans-unit id="3178343147230721210" datatype="html">
10554 <source>Navigate in the video frame by frame (requires player focus)</source> 10607 <source>Navigate in the video frame by frame (requires player focus)</source>
10555 <target state="translated">逐畫格瀏覽影片(需要播放器焦點)</target> 10608 <target state="translated">逐畫格瀏覽影片(需要播放器焦點)</target>
10556 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">693</context></context-group> 10609
10557 </trans-unit> 10610 <context-group purpose="location"><context context-type="sourcefile">src/app/+videos/+video-watch/video-watch.component.ts</context><context context-type="linenumber">700</context></context-group></trans-unit>
10558 <trans-unit id="8025996572234182184"> 10611 <trans-unit id="8025996572234182184">
10559 <source>Like the video</source> 10612 <source>Like the video</source>
10560 <target>喜歡此影片</target> 10613 <target>喜歡此影片</target>
diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss
index 30d487b11..bd834db70 100644
--- a/client/src/sass/application.scss
+++ b/client/src/sass/application.scss
@@ -123,12 +123,16 @@ code {
123 vertical-align: middle; 123 vertical-align: middle;
124} 124}
125 125
126.form-error { 126.form-error,
127.form-warning {
127 display: block; 128 display: block;
128 color: $red;
129 margin-top: 5px; 129 margin-top: 5px;
130} 130}
131 131
132.form-error {
133 color: $red;
134}
135
132.input-error, 136.input-error,
133my-input-toggle-hidden ::ng-deep input { 137my-input-toggle-hidden ::ng-deep input {
134 border-color: $red !important; 138 border-color: $red !important;
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts
index e59d8b940..334f386b6 100644
--- a/client/src/standalone/videos/embed.ts
+++ b/client/src/standalone/videos/embed.ts
@@ -1,9 +1,9 @@
1import './embed.scss' 1import './embed.scss'
2import videojs from 'video.js' 2import videojs from 'video.js'
3import { peertubeTranslate } from '../../../../shared/core-utils/i18n' 3import { peertubeTranslate } from '../../../../shared/core-utils/i18n'
4import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
5import { 4import {
6 HTMLServerConfig, 5 HTMLServerConfig,
6 HttpStatusCode,
7 OAuth2ErrorCode, 7 OAuth2ErrorCode,
8 ResultList, 8 ResultList,
9 UserRefreshToken, 9 UserRefreshToken,
@@ -536,6 +536,7 @@ export class PeerTubeEmbed {
536 videoCaptions, 536 videoCaptions,
537 inactivityTimeout: 2500, 537 inactivityTimeout: 2500,
538 videoViewUrl: this.getVideoUrl(videoInfo.uuid) + '/views', 538 videoViewUrl: this.getVideoUrl(videoInfo.uuid) + '/views',
539 videoShortUUID: videoInfo.shortUUID,
539 videoUUID: videoInfo.uuid, 540 videoUUID: videoInfo.uuid,
540 541
541 isLive: videoInfo.isLive, 542 isLive: videoInfo.isLive,
diff --git a/package.json b/package.json
index 7611ac9ab..9ce1e1b0e 100644
--- a/package.json
+++ b/package.json
@@ -74,7 +74,6 @@
74 }, 74 },
75 "dependencies": { 75 "dependencies": {
76 "@uploadx/core": "^4.4.0", 76 "@uploadx/core": "^4.4.0",
77 "apicache": "1.6.2",
78 "async": "^3.0.1", 77 "async": "^3.0.1",
79 "async-lru": "^1.1.1", 78 "async-lru": "^1.1.1",
80 "bcrypt": "5.0.1", 79 "bcrypt": "5.0.1",
@@ -148,7 +147,6 @@
148 }, 147 },
149 "devDependencies": { 148 "devDependencies": {
150 "@openapitools/openapi-generator-cli": "^2.1.4", 149 "@openapitools/openapi-generator-cli": "^2.1.4",
151 "@types/apicache": "^1.2.0",
152 "@types/async": "^3.0.0", 150 "@types/async": "^3.0.0",
153 "@types/async-lock": "^1.1.0", 151 "@types/async-lock": "^1.1.0",
154 "@types/bcrypt": "^5.0.0", 152 "@types/bcrypt": "^5.0.0",
@@ -159,7 +157,7 @@
159 "@types/chai": "^4.0.4", 157 "@types/chai": "^4.0.4",
160 "@types/chai-json-schema": "^1.4.3", 158 "@types/chai-json-schema": "^1.4.3",
161 "@types/chai-xml": "^0.3.1", 159 "@types/chai-xml": "^0.3.1",
162 "@types/config": "^0.0.38", 160 "@types/config": "^0.0.39",
163 "@types/express": "4.17.9", 161 "@types/express": "4.17.9",
164 "@types/express-rate-limit": "^5.0.0", 162 "@types/express-rate-limit": "^5.0.0",
165 "@types/fluent-ffmpeg": "^2.1.16", 163 "@types/fluent-ffmpeg": "^2.1.16",
@@ -167,7 +165,7 @@
167 "@types/lodash": "^4.14.64", 165 "@types/lodash": "^4.14.64",
168 "@types/lru-cache": "^5.1.0", 166 "@types/lru-cache": "^5.1.0",
169 "@types/magnet-uri": "^5.1.1", 167 "@types/magnet-uri": "^5.1.1",
170 "@types/maildev": "^0.0.2", 168 "@types/maildev": "^0.0.3",
171 "@types/memoizee": "^0.4.2", 169 "@types/memoizee": "^0.4.2",
172 "@types/mkdirp": "^1.0.0", 170 "@types/mkdirp": "^1.0.0",
173 "@types/mocha": "^8.0.3", 171 "@types/mocha": "^8.0.3",
@@ -205,7 +203,7 @@
205 "source-map-support": "^0.5.0", 203 "source-map-support": "^0.5.0",
206 "supertest": "^6.0.1", 204 "supertest": "^6.0.1",
207 "swagger-cli": "^4.0.2", 205 "swagger-cli": "^4.0.2",
208 "ts-node": "10.0.0", 206 "ts-node": "10.1.0",
209 "typescript": "^4.0.5" 207 "typescript": "^4.0.5"
210 }, 208 },
211 "bundlewatch": { 209 "bundlewatch": {
diff --git a/scripts/benchmark.ts b/scripts/benchmark.ts
index 0cadb36d9..5dcf9b01b 100644
--- a/scripts/benchmark.ts
+++ b/scripts/benchmark.ts
@@ -2,21 +2,11 @@ import { registerTSPaths } from '../server/helpers/register-ts-paths'
2registerTSPaths() 2registerTSPaths()
3 3
4import * as autocannon from 'autocannon' 4import * as autocannon from 'autocannon'
5import {
6 addVideoCommentReply,
7 addVideoCommentThread,
8 createVideoCaption,
9 flushAndRunServer,
10 getVideosList,
11 killallServers,
12 ServerInfo,
13 setAccessTokensToServers,
14 uploadVideo
15} from '@shared/extra-utils'
16import { Video, VideoPrivacy } from '@shared/models'
17import { writeJson } from 'fs-extra' 5import { writeJson } from 'fs-extra'
6import { createSingleServer, killallServers, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
7import { Video, VideoPrivacy } from '@shared/models'
18 8
19let server: ServerInfo 9let server: PeerTubeServer
20let video: Video 10let video: Video
21let threadId: number 11let threadId: number
22 12
@@ -25,7 +15,7 @@ const outfile = process.argv[2]
25run() 15run()
26 .catch(err => console.error(err)) 16 .catch(err => console.error(err))
27 .finally(() => { 17 .finally(() => {
28 if (server) killallServers([ server ]) 18 if (server) return killallServers([ server ])
29 }) 19 })
30 20
31function buildAuthorizationHeader () { 21function buildAuthorizationHeader () {
@@ -198,7 +188,7 @@ function runBenchmark (options: {
198} 188}
199 189
200async function prepare () { 190async function prepare () {
201 server = await flushAndRunServer(1, { 191 server = await createSingleServer(1, {
202 rates_limit: { 192 rates_limit: {
203 api: { 193 api: {
204 max: 5_000_000 194 max: 5_000_000
@@ -207,7 +197,7 @@ async function prepare () {
207 }) 197 })
208 await setAccessTokensToServers([ server ]) 198 await setAccessTokensToServers([ server ])
209 199
210 const videoAttributes = { 200 const attributes = {
211 name: 'my super video', 201 name: 'my super video',
212 category: 2, 202 category: 2,
213 nsfw: true, 203 nsfw: true,
@@ -220,33 +210,29 @@ async function prepare () {
220 } 210 }
221 211
222 for (let i = 0; i < 10; i++) { 212 for (let i = 0; i < 10; i++) {
223 Object.assign(videoAttributes, { name: 'my super video ' + i }) 213 await server.videos.upload({ attributes: { ...attributes, name: 'my super video ' + i } })
224 await uploadVideo(server.url, server.accessToken, videoAttributes)
225 } 214 }
226 215
227 const resVideos = await getVideosList(server.url) 216 const { data } = await server.videos.list()
228 video = resVideos.body.data.find(v => v.name === 'my super video 1') 217 video = data.find(v => v.name === 'my super video 1')
229 218
230 for (let i = 0; i < 10; i++) { 219 for (let i = 0; i < 10; i++) {
231 const text = 'my super first comment' 220 const text = 'my super first comment'
232 const res = await addVideoCommentThread(server.url, server.accessToken, video.id, text) 221 const created = await server.comments.createThread({ videoId: video.id, text })
233 threadId = res.body.comment.id 222 threadId = created.id
234 223
235 const text1 = 'my super answer to thread 1' 224 const text1 = 'my super answer to thread 1'
236 const childCommentRes = await addVideoCommentReply(server.url, server.accessToken, video.id, threadId, text1) 225 const child = await server.comments.addReply({ videoId: video.id, toCommentId: threadId, text: text1 })
237 const childCommentId = childCommentRes.body.comment.id
238 226
239 const text2 = 'my super answer to answer of thread 1' 227 const text2 = 'my super answer to answer of thread 1'
240 await addVideoCommentReply(server.url, server.accessToken, video.id, childCommentId, text2) 228 await server.comments.addReply({ videoId: video.id, toCommentId: child.id, text: text2 })
241 229
242 const text3 = 'my second answer to thread 1' 230 const text3 = 'my second answer to thread 1'
243 await addVideoCommentReply(server.url, server.accessToken, video.id, threadId, text3) 231 await server.comments.addReply({ videoId: video.id, toCommentId: threadId, text: text3 })
244 } 232 }
245 233
246 for (const caption of [ 'ar', 'fr', 'en', 'zh' ]) { 234 for (const caption of [ 'ar', 'fr', 'en', 'zh' ]) {
247 await createVideoCaption({ 235 await server.captions.add({
248 url: server.url,
249 accessToken: server.accessToken,
250 language: caption, 236 language: caption,
251 videoId: video.id, 237 videoId: video.id,
252 fixture: 'subtitle-good2.vtt' 238 fixture: 'subtitle-good2.vtt'
diff --git a/scripts/ci.sh b/scripts/ci.sh
index 07e37e0ee..71b1be53b 100755
--- a/scripts/ci.sh
+++ b/scripts/ci.sh
@@ -47,11 +47,12 @@ if [ "$1" = "client" ]; then
47 47
48 feedsFiles=$(findTestFiles ./dist/server/tests/feeds) 48 feedsFiles=$(findTestFiles ./dist/server/tests/feeds)
49 helperFiles=$(findTestFiles ./dist/server/tests/helpers) 49 helperFiles=$(findTestFiles ./dist/server/tests/helpers)
50 libFiles=$(findTestFiles ./dist/server/tests/lib)
50 miscFiles="./dist/server/tests/client.js ./dist/server/tests/misc-endpoints.js" 51 miscFiles="./dist/server/tests/client.js ./dist/server/tests/misc-endpoints.js"
51 # Not in plugin task, it needs an index.html 52 # Not in plugin task, it needs an index.html
52 pluginFiles="./dist/server/tests/plugins/html-injection.js" 53 pluginFiles="./dist/server/tests/plugins/html-injection.js"
53 54
54 MOCHA_PARALLEL=true runTest "$1" 2 $feedsFiles $helperFiles $miscFiles $pluginFiles 55 MOCHA_PARALLEL=true runTest "$1" 2 $feedsFiles $helperFiles $miscFiles $pluginFiles $libFiles
55elif [ "$1" = "cli-plugin" ]; then 56elif [ "$1" = "cli-plugin" ]; then
56 npm run build:server 57 npm run build:server
57 npm run setup:cli 58 npm run setup:cli
@@ -76,7 +77,7 @@ elif [ "$1" = "api-2" ]; then
76 serverFiles=$(findTestFiles ./dist/server/tests/api/server) 77 serverFiles=$(findTestFiles ./dist/server/tests/api/server)
77 usersFiles=$(findTestFiles ./dist/server/tests/api/users) 78 usersFiles=$(findTestFiles ./dist/server/tests/api/users)
78 79
79 MOCHA_PARALLEL=true runTest "$1" 3 $serverFiles $usersFiles $liveFiles 80 MOCHA_PARALLEL=true runTest "$1" 3 $liveFiles $serverFiles $usersFiles
80elif [ "$1" = "api-3" ]; then 81elif [ "$1" = "api-3" ]; then
81 npm run build:server 82 npm run build:server
82 83
diff --git a/scripts/generate-code-contributors.ts b/scripts/generate-code-contributors.ts
index db5af3f91..935ed3c5a 100755
--- a/scripts/generate-code-contributors.ts
+++ b/scripts/generate-code-contributors.ts
@@ -1,7 +1,7 @@
1import { registerTSPaths } from '../server/helpers/register-ts-paths' 1import { registerTSPaths } from '../server/helpers/register-ts-paths'
2registerTSPaths() 2registerTSPaths()
3 3
4import { execCLI } from '@shared/extra-utils' 4import { CLICommand } from '@shared/extra-utils'
5 5
6run() 6run()
7 .then(() => process.exit(0)) 7 .then(() => process.exit(0))
@@ -59,7 +59,7 @@ async function run () {
59} 59}
60 60
61async function getGitContributors () { 61async function getGitContributors () {
62 const output = await execCLI(`git --no-pager shortlog -sn < /dev/tty | sed 's/^\\s\\+[0-9]\\+\\s\\+//g'`) 62 const output = await CLICommand.exec(`git --no-pager shortlog -sn < /dev/tty | sed 's/^\\s\\+[0-9]\\+\\s\\+//g'`)
63 63
64 return output.split('\n') 64 return output.split('\n')
65 .filter(l => !!l) 65 .filter(l => !!l)
diff --git a/scripts/optimize-old-videos.ts b/scripts/optimize-old-videos.ts
index 9692d76ba..bde9d1e01 100644
--- a/scripts/optimize-old-videos.ts
+++ b/scripts/optimize-old-videos.ts
@@ -19,13 +19,13 @@ run()
19 process.exit(-1) 19 process.exit(-1)
20 }) 20 })
21 21
22let currentVideoId = null 22let currentVideoId: string
23let currentFile = null 23let currentFilePath: string
24 24
25process.on('SIGINT', async function () { 25process.on('SIGINT', async function () {
26 console.log('Cleaning up temp files') 26 console.log('Cleaning up temp files')
27 await remove(`${currentFile}_backup`) 27 await remove(`${currentFilePath}_backup`)
28 await remove(`${dirname(currentFile)}/${currentVideoId}-transcoded.mp4`) 28 await remove(`${dirname(currentFilePath)}/${currentVideoId}-transcoded.mp4`)
29 process.exit(0) 29 process.exit(0)
30}) 30})
31 31
@@ -40,12 +40,12 @@ async function run () {
40 currentVideoId = video.id 40 currentVideoId = video.id
41 41
42 for (const file of video.VideoFiles) { 42 for (const file of video.VideoFiles) {
43 currentFile = getVideoFilePath(video, file) 43 currentFilePath = getVideoFilePath(video, file)
44 44
45 const [ videoBitrate, fps, resolution ] = await Promise.all([ 45 const [ videoBitrate, fps, resolution ] = await Promise.all([
46 getVideoFileBitrate(currentFile), 46 getVideoFileBitrate(currentFilePath),
47 getVideoFileFPS(currentFile), 47 getVideoFileFPS(currentFilePath),
48 getVideoFileResolution(currentFile) 48 getVideoFileResolution(currentFilePath)
49 ]) 49 ])
50 50
51 const maxBitrate = getMaxBitrate(resolution.videoFileResolution, fps, VIDEO_TRANSCODING_FPS) 51 const maxBitrate = getMaxBitrate(resolution.videoFileResolution, fps, VIDEO_TRANSCODING_FPS)
@@ -53,25 +53,27 @@ async function run () {
53 if (isMaxBitrateExceeded) { 53 if (isMaxBitrateExceeded) {
54 console.log( 54 console.log(
55 'Optimizing video file %s with bitrate %s kbps (max: %s kbps)', 55 'Optimizing video file %s with bitrate %s kbps (max: %s kbps)',
56 basename(currentFile), videoBitrate / 1000, maxBitrate / 1000 56 basename(currentFilePath), videoBitrate / 1000, maxBitrate / 1000
57 ) 57 )
58 58
59 const backupFile = `${currentFile}_backup` 59 const backupFile = `${currentFilePath}_backup`
60 await copy(currentFile, backupFile) 60 await copy(currentFilePath, backupFile)
61 61
62 await optimizeOriginalVideofile(video, file) 62 await optimizeOriginalVideofile(video, file)
63 // Update file path, the video filename changed
64 currentFilePath = getVideoFilePath(video, file)
63 65
64 const originalDuration = await getDurationFromVideoFile(backupFile) 66 const originalDuration = await getDurationFromVideoFile(backupFile)
65 const newDuration = await getDurationFromVideoFile(currentFile) 67 const newDuration = await getDurationFromVideoFile(currentFilePath)
66 68
67 if (originalDuration === newDuration) { 69 if (originalDuration === newDuration) {
68 console.log('Finished optimizing %s', basename(currentFile)) 70 console.log('Finished optimizing %s', basename(currentFilePath))
69 await remove(backupFile) 71 await remove(backupFile)
70 continue 72 continue
71 } 73 }
72 74
73 console.log('Failed to optimize %s, restoring original', basename(currentFile)) 75 console.log('Failed to optimize %s, restoring original', basename(currentFilePath))
74 await move(backupFile, currentFile, { overwrite: true }) 76 await move(backupFile, currentFilePath, { overwrite: true })
75 await createTorrentAndSetInfoHash(video, file) 77 await createTorrentAndSetInfoHash(video, file)
76 await file.save() 78 await file.save()
77 } 79 }
diff --git a/scripts/parse-log.ts b/scripts/parse-log.ts
index c16503589..6cd3a1860 100755
--- a/scripts/parse-log.ts
+++ b/scripts/parse-log.ts
@@ -6,9 +6,8 @@ import { createReadStream, readdir } from 'fs-extra'
6import { join } from 'path' 6import { join } from 'path'
7import { createInterface } from 'readline' 7import { createInterface } from 'readline'
8import * as winston from 'winston' 8import * as winston from 'winston'
9import { labelFormatter } from '../server/helpers/logger' 9import { labelFormatter, mtimeSortFilesDesc } from '../server/helpers/logger'
10import { CONFIG } from '../server/initializers/config' 10import { CONFIG } from '../server/initializers/config'
11import { mtimeSortFilesDesc } from '../shared/core-utils/logs/logs'
12import { inspect } from 'util' 11import { inspect } from 'util'
13import { format as sqlFormat } from 'sql-formatter' 12import { format as sqlFormat } from 'sql-formatter'
14 13
diff --git a/scripts/prune-storage.ts b/scripts/prune-storage.ts
index 58d24816e..5b029d215 100755
--- a/scripts/prune-storage.ts
+++ b/scripts/prune-storage.ts
@@ -2,11 +2,11 @@ import { registerTSPaths } from '../server/helpers/register-ts-paths'
2registerTSPaths() 2registerTSPaths()
3 3
4import * as prompt from 'prompt' 4import * as prompt from 'prompt'
5import { join } from 'path' 5import { join, basename } from 'path'
6import { CONFIG } from '../server/initializers/config' 6import { CONFIG } from '../server/initializers/config'
7import { VideoModel } from '../server/models/video/video' 7import { VideoModel } from '../server/models/video/video'
8import { initDatabaseModels } from '../server/initializers/database' 8import { initDatabaseModels } from '../server/initializers/database'
9import { readdir, remove } from 'fs-extra' 9import { readdir, remove, stat } from 'fs-extra'
10import { VideoRedundancyModel } from '../server/models/redundancy/video-redundancy' 10import { VideoRedundancyModel } from '../server/models/redundancy/video-redundancy'
11import * as Bluebird from 'bluebird' 11import * as Bluebird from 'bluebird'
12import { getUUIDFromFilename } from '../server/helpers/utils' 12import { getUUIDFromFilename } from '../server/helpers/utils'
@@ -14,6 +14,7 @@ import { ThumbnailModel } from '../server/models/video/thumbnail'
14import { ActorImageModel } from '../server/models/actor/actor-image' 14import { ActorImageModel } from '../server/models/actor/actor-image'
15import { uniq, values } from 'lodash' 15import { uniq, values } from 'lodash'
16import { ThumbnailType } from '@shared/models' 16import { ThumbnailType } from '@shared/models'
17import { VideoFileModel } from '@server/models/video/video-file'
17 18
18run() 19run()
19 .then(() => process.exit(0)) 20 .then(() => process.exit(0))
@@ -37,8 +38,8 @@ async function run () {
37 console.log('Detecting files to remove, it could take a while...') 38 console.log('Detecting files to remove, it could take a while...')
38 39
39 toDelete = toDelete.concat( 40 toDelete = toDelete.concat(
40 await pruneDirectory(CONFIG.STORAGE.VIDEOS_DIR, doesVideoExist(true)), 41 await pruneDirectory(CONFIG.STORAGE.VIDEOS_DIR, doesWebTorrentFileExist()),
41 await pruneDirectory(CONFIG.STORAGE.TORRENTS_DIR, doesVideoExist(true)), 42 await pruneDirectory(CONFIG.STORAGE.TORRENTS_DIR, doesTorrentFileExist()),
42 43
43 await pruneDirectory(CONFIG.STORAGE.REDUNDANCY_DIR, doesRedundancyExist), 44 await pruneDirectory(CONFIG.STORAGE.REDUNDANCY_DIR, doesRedundancyExist),
44 45
@@ -78,26 +79,27 @@ async function pruneDirectory (directory: string, existFun: ExistFun) {
78 79
79 const toDelete: string[] = [] 80 const toDelete: string[] = []
80 await Bluebird.map(files, async file => { 81 await Bluebird.map(files, async file => {
81 if (await existFun(file) !== true) { 82 const filePath = join(directory, file)
82 toDelete.push(join(directory, file)) 83
84 if (await existFun(filePath) !== true) {
85 toDelete.push(filePath)
83 } 86 }
84 }, { concurrency: 20 }) 87 }, { concurrency: 20 })
85 88
86 return toDelete 89 return toDelete
87} 90}
88 91
89function doesVideoExist (keepOnlyOwned: boolean) { 92function doesWebTorrentFileExist () {
90 return async (file: string) => { 93 return (filePath: string) => VideoFileModel.doesOwnedWebTorrentVideoFileExist(basename(filePath))
91 const uuid = getUUIDFromFilename(file) 94}
92 const video = await VideoModel.load(uuid)
93 95
94 return video && (keepOnlyOwned === false || video.isOwned()) 96function doesTorrentFileExist () {
95 } 97 return (filePath: string) => VideoFileModel.doesOwnedTorrentFileExist(basename(filePath))
96} 98}
97 99
98function doesThumbnailExist (keepOnlyOwned: boolean, type: ThumbnailType) { 100function doesThumbnailExist (keepOnlyOwned: boolean, type: ThumbnailType) {
99 return async (file: string) => { 101 return async (filePath: string) => {
100 const thumbnail = await ThumbnailModel.loadByFilename(file, type) 102 const thumbnail = await ThumbnailModel.loadByFilename(basename(filePath), type)
101 if (!thumbnail) return false 103 if (!thumbnail) return false
102 104
103 if (keepOnlyOwned) { 105 if (keepOnlyOwned) {
@@ -109,21 +111,20 @@ function doesThumbnailExist (keepOnlyOwned: boolean, type: ThumbnailType) {
109 } 111 }
110} 112}
111 113
112async function doesActorImageExist (file: string) { 114async function doesActorImageExist (filePath: string) {
113 const image = await ActorImageModel.loadByName(file) 115 const image = await ActorImageModel.loadByName(basename(filePath))
114 116
115 return !!image 117 return !!image
116} 118}
117 119
118async function doesRedundancyExist (file: string) { 120async function doesRedundancyExist (filePath: string) {
119 const uuid = getUUIDFromFilename(file) 121 const isPlaylist = (await stat(filePath)).isDirectory()
120 const video = await VideoModel.loadWithFiles(uuid)
121
122 if (!video) return false
123
124 const isPlaylist = file.includes('.') === false
125 122
126 if (isPlaylist) { 123 if (isPlaylist) {
124 const uuid = getUUIDFromFilename(filePath)
125 const video = await VideoModel.loadWithFiles(uuid)
126 if (!video) return false
127
127 const p = video.getHLSPlaylist() 128 const p = video.getHLSPlaylist()
128 if (!p) return false 129 if (!p) return false
129 130
@@ -131,19 +132,10 @@ async function doesRedundancyExist (file: string) {
131 return !!redundancy 132 return !!redundancy
132 } 133 }
133 134
134 const resolution = parseInt(file.split('-')[5], 10) 135 const file = await VideoFileModel.loadByFilename(basename(filePath))
135 if (isNaN(resolution)) { 136 if (!file) return false
136 console.error('Cannot prune %s because we cannot guess guess the resolution.', file)
137 return true
138 }
139
140 const videoFile = video.getWebTorrentFile(resolution)
141 if (!videoFile) {
142 console.error('Cannot find webtorrent file of video %s - %d', video.url, resolution)
143 return true
144 }
145 137
146 const redundancy = await VideoRedundancyModel.loadLocalByFileId(videoFile.id) 138 const redundancy = await VideoRedundancyModel.loadLocalByFileId(file.id)
147 return !!redundancy 139 return !!redundancy
148} 140}
149 141
diff --git a/scripts/update-host.ts b/scripts/update-host.ts
index 592684225..9e8dd41ca 100755
--- a/scripts/update-host.ts
+++ b/scripts/update-host.ts
@@ -16,7 +16,6 @@ import { VideoShareModel } from '../server/models/video/video-share'
16import { VideoCommentModel } from '../server/models/video/video-comment' 16import { VideoCommentModel } from '../server/models/video/video-comment'
17import { AccountModel } from '../server/models/account/account' 17import { AccountModel } from '../server/models/account/account'
18import { VideoChannelModel } from '../server/models/video/video-channel' 18import { VideoChannelModel } from '../server/models/video/video-channel'
19import { VideoStreamingPlaylistModel } from '../server/models/video/video-streaming-playlist'
20import { initDatabaseModels } from '../server/initializers/database' 19import { initDatabaseModels } from '../server/initializers/database'
21import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' 20import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
22import { getServerActor } from '@server/models/application/application' 21import { getServerActor } from '@server/models/application/application'
@@ -128,13 +127,17 @@ async function run () {
128 for (const file of video.VideoFiles) { 127 for (const file of video.VideoFiles) {
129 console.log('Updating torrent file %s of video %s.', file.resolution, video.uuid) 128 console.log('Updating torrent file %s of video %s.', file.resolution, video.uuid)
130 await createTorrentAndSetInfoHash(video, file) 129 await createTorrentAndSetInfoHash(video, file)
130
131 await file.save()
131 } 132 }
132 133
133 for (const playlist of video.VideoStreamingPlaylists) { 134 const playlist = video.getHLSPlaylist()
134 playlist.playlistUrl = WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsMasterPlaylistStaticPath(video.uuid) 135 for (const file of (playlist?.VideoFiles || [])) {
135 playlist.segmentsSha256Url = WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsSha256SegmentsStaticPath(video.uuid, video.isLive) 136 console.log('Updating fragmented torrent file %s of video %s.', file.resolution, video.uuid)
137
138 await createTorrentAndSetInfoHash(video, file)
136 139
137 await playlist.save() 140 await file.save()
138 } 141 }
139 } 142 }
140} 143}
diff --git a/server.ts b/server.ts
index e46300dce..bfc7ee145 100644
--- a/server.ts
+++ b/server.ts
@@ -125,7 +125,7 @@ import { PeerTubeVersionCheckScheduler } from './server/lib/schedulers/peertube-
125import { Hooks } from './server/lib/plugins/hooks' 125import { Hooks } from './server/lib/plugins/hooks'
126import { PluginManager } from './server/lib/plugins/plugin-manager' 126import { PluginManager } from './server/lib/plugins/plugin-manager'
127import { LiveManager } from './server/lib/live' 127import { LiveManager } from './server/lib/live'
128import { HttpStatusCode } from './shared/core-utils/miscs/http-error-codes' 128import { HttpStatusCode } from './shared/models/http/http-error-codes'
129import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache' 129import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache'
130import { ServerConfigManager } from '@server/lib/server-config-manager' 130import { ServerConfigManager } from '@server/lib/server-config-manager'
131 131
@@ -305,13 +305,19 @@ async function startApplication () {
305 updateStreamingPlaylistsInfohashesIfNeeded() 305 updateStreamingPlaylistsInfohashesIfNeeded()
306 .catch(err => logger.error('Cannot update streaming playlist infohashes.', { err })) 306 .catch(err => logger.error('Cannot update streaming playlist infohashes.', { err }))
307 307
308 if (cliOptions.plugins) await PluginManager.Instance.registerPluginsAndThemes()
309
310 LiveManager.Instance.init() 308 LiveManager.Instance.init()
311 if (CONFIG.LIVE.ENABLED) LiveManager.Instance.run() 309 if (CONFIG.LIVE.ENABLED) LiveManager.Instance.run()
312 310
313 // Make server listening 311 // Make server listening
314 server.listen(port, hostname, () => { 312 server.listen(port, hostname, async () => {
313 if (cliOptions.plugins) {
314 try {
315 await PluginManager.Instance.registerPluginsAndThemes()
316 } catch (err) {
317 logger.error('Cannot register plugins and themes.', { err })
318 }
319 }
320
315 logger.info('HTTP server listening on %s:%d', hostname, port) 321 logger.info('HTTP server listening on %s:%d', hostname, port)
316 logger.info('Web server: %s', WEBSERVER.URL) 322 logger.info('Web server: %s', WEBSERVER.URL)
317 323
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts
index d7de1b9bd..bef4bc068 100644
--- a/server/controllers/activitypub/client.ts
+++ b/server/controllers/activitypub/client.ts
@@ -24,7 +24,7 @@ import {
24 videosCustomGetValidator, 24 videosCustomGetValidator,
25 videosShareValidator 25 videosShareValidator
26} from '../../middlewares' 26} from '../../middlewares'
27import { cacheRoute } from '../../middlewares/cache' 27import { cacheRoute } from '../../middlewares/cache/cache'
28import { getAccountVideoRateValidatorFactory, videoCommentGetValidator } from '../../middlewares/validators' 28import { getAccountVideoRateValidatorFactory, videoCommentGetValidator } from '../../middlewares/validators'
29import { videoFileRedundancyGetValidator, videoPlaylistRedundancyGetValidator } from '../../middlewares/validators/redundancy' 29import { videoFileRedundancyGetValidator, videoPlaylistRedundancyGetValidator } from '../../middlewares/validators/redundancy'
30import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists' 30import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists'
@@ -77,7 +77,7 @@ activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId',
77activityPubClientRouter.get( 77activityPubClientRouter.get(
78 [ '/videos/watch/:id', '/w/:id' ], 78 [ '/videos/watch/:id', '/w/:id' ],
79 executeIfActivityPub, 79 executeIfActivityPub,
80 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS)), 80 cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS),
81 asyncMiddleware(videosCustomGetValidator('all')), 81 asyncMiddleware(videosCustomGetValidator('all')),
82 asyncMiddleware(videoController) 82 asyncMiddleware(videoController)
83) 83)
diff --git a/server/controllers/activitypub/inbox.ts b/server/controllers/activitypub/inbox.ts
index 14f301ab7..30662990a 100644
--- a/server/controllers/activitypub/inbox.ts
+++ b/server/controllers/activitypub/inbox.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { InboxManager } from '@server/lib/activitypub/inbox-manager' 2import { InboxManager } from '@server/lib/activitypub/inbox-manager'
3import { Activity, ActivityPubCollection, ActivityPubOrderedCollection, RootActivity } from '../../../shared' 3import { Activity, ActivityPubCollection, ActivityPubOrderedCollection, RootActivity } from '../../../shared'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
5import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity' 5import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
7import { asyncMiddleware, checkSignature, localAccountValidator, localVideoChannelValidator, signatureValidator } from '../../middlewares' 7import { asyncMiddleware, checkSignature, localAccountValidator, localVideoChannelValidator, signatureValidator } from '../../middlewares'
diff --git a/server/controllers/api/abuse.ts b/server/controllers/api/abuse.ts
index ba5b94840..e851365e9 100644
--- a/server/controllers/api/abuse.ts
+++ b/server/controllers/api/abuse.ts
@@ -6,7 +6,7 @@ import { AbuseModel } from '@server/models/abuse/abuse'
6import { AbuseMessageModel } from '@server/models/abuse/abuse-message' 6import { AbuseMessageModel } from '@server/models/abuse/abuse-message'
7import { getServerActor } from '@server/models/application/application' 7import { getServerActor } from '@server/models/application/application'
8import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' 8import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
9import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 9import { HttpStatusCode } from '@shared/models'
10import { AbuseCreate, AbuseState, UserRight } from '../../../shared' 10import { AbuseCreate, AbuseState, UserRight } from '../../../shared'
11import { getFormattedObjects } from '../../helpers/utils' 11import { getFormattedObjects } from '../../helpers/utils'
12import { sequelizeTypescript } from '../../initializers/database' 12import { sequelizeTypescript } from '../../initializers/database'
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts
index 49a8e3195..55e2aaf62 100644
--- a/server/controllers/api/accounts.ts
+++ b/server/controllers/api/accounts.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { pickCommonVideoQuery } from '@server/helpers/query'
2import { getServerActor } from '@server/models/application/application' 3import { getServerActor } from '@server/models/application/application'
3import { VideosWithSearchCommonQuery } from '@shared/models'
4import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' 4import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
5import { getFormattedObjects } from '../../helpers/utils' 5import { getFormattedObjects } from '../../helpers/utils'
6import { JobQueue } from '../../lib/job-queue' 6import { JobQueue } from '../../lib/job-queue'
@@ -159,27 +159,19 @@ async function listAccountVideos (req: express.Request, res: express.Response) {
159 const account = res.locals.account 159 const account = res.locals.account
160 const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined 160 const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined
161 const countVideos = getCountVideos(req) 161 const countVideos = getCountVideos(req)
162 const query = req.query as VideosWithSearchCommonQuery 162 const query = pickCommonVideoQuery(req.query)
163 163
164 const apiOptions = await Hooks.wrapObject({ 164 const apiOptions = await Hooks.wrapObject({
165 ...query,
166
165 followerActorId, 167 followerActorId,
166 start: query.start, 168 search: req.query.search,
167 count: query.count,
168 sort: query.sort,
169 includeLocalVideos: true, 169 includeLocalVideos: true,
170 categoryOneOf: query.categoryOneOf,
171 licenceOneOf: query.licenceOneOf,
172 languageOneOf: query.languageOneOf,
173 tagsOneOf: query.tagsOneOf,
174 tagsAllOf: query.tagsAllOf,
175 filter: query.filter,
176 isLive: query.isLive,
177 nsfw: buildNSFWFilter(res, query.nsfw), 170 nsfw: buildNSFWFilter(res, query.nsfw),
178 withFiles: false, 171 withFiles: false,
179 accountId: account.id, 172 accountId: account.id,
180 user: res.locals.oauth ? res.locals.oauth.token.User : undefined, 173 user: res.locals.oauth ? res.locals.oauth.token.User : undefined,
181 countVideos, 174 countVideos
182 search: query.search
183 }, 'filter:api.accounts.videos.list.params') 175 }, 'filter:api.accounts.videos.list.params')
184 176
185 const resultList = await Hooks.wrapPromiseFun( 177 const resultList = await Hooks.wrapPromiseFun(
diff --git a/server/controllers/api/bulk.ts b/server/controllers/api/bulk.ts
index 192daccde..62121ece5 100644
--- a/server/controllers/api/bulk.ts
+++ b/server/controllers/api/bulk.ts
@@ -1,10 +1,10 @@
1import * as express from 'express' 1import * as express from 'express'
2import { asyncMiddleware, authenticate } from '../../middlewares' 2import { removeComment } from '@server/lib/video-comment'
3import { bulkRemoveCommentsOfValidator } from '@server/middlewares/validators/bulk' 3import { bulkRemoveCommentsOfValidator } from '@server/middlewares/validators/bulk'
4import { VideoCommentModel } from '@server/models/video/video-comment' 4import { VideoCommentModel } from '@server/models/video/video-comment'
5import { removeComment } from '@server/lib/video-comment' 5import { HttpStatusCode } from '@shared/models'
6import { BulkRemoveCommentsOfBody } from '@shared/models/bulk/bulk-remove-comments-of-body.model' 6import { BulkRemoveCommentsOfBody } from '@shared/models/bulk/bulk-remove-comments-of-body.model'
7import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 7import { asyncMiddleware, authenticate } from '../../middlewares'
8 8
9const bulkRouter = express.Router() 9const bulkRouter = express.Router()
10 10
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts
index 9bd8c21c5..ee733a38c 100644
--- a/server/controllers/api/config.ts
+++ b/server/controllers/api/config.ts
@@ -1,8 +1,8 @@
1import { ServerConfigManager } from '@server/lib/server-config-manager'
2import * as express from 'express' 1import * as express from 'express'
3import { remove, writeJSON } from 'fs-extra' 2import { remove, writeJSON } from 'fs-extra'
4import { snakeCase } from 'lodash' 3import { snakeCase } from 'lodash'
5import validator from 'validator' 4import validator from 'validator'
5import { ServerConfigManager } from '@server/lib/server-config-manager'
6import { UserRight } from '../../../shared' 6import { UserRight } from '../../../shared'
7import { About } from '../../../shared/models/server/about.model' 7import { About } from '../../../shared/models/server/about.model'
8import { CustomConfig } from '../../../shared/models/server/custom-config.model' 8import { CustomConfig } from '../../../shared/models/server/custom-config.model'
diff --git a/server/controllers/api/custom-page.ts b/server/controllers/api/custom-page.ts
index c19f03c56..68d8c2ea4 100644
--- a/server/controllers/api/custom-page.ts
+++ b/server/controllers/api/custom-page.ts
@@ -1,8 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { ServerConfigManager } from '@server/lib/server-config-manager' 2import { ServerConfigManager } from '@server/lib/server-config-manager'
3import { ActorCustomPageModel } from '@server/models/account/actor-custom-page' 3import { ActorCustomPageModel } from '@server/models/account/actor-custom-page'
4import { HttpStatusCode } from '@shared/core-utils' 4import { HttpStatusCode, UserRight } from '@shared/models'
5import { UserRight } from '@shared/models'
6import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares' 5import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares'
7 6
8const customPageRouter = express.Router() 7const customPageRouter = express.Router()
diff --git a/server/controllers/api/index.ts b/server/controllers/api/index.ts
index 28378654a..93b14dadb 100644
--- a/server/controllers/api/index.ts
+++ b/server/controllers/api/index.ts
@@ -1,7 +1,7 @@
1import * as cors from 'cors' 1import * as cors from 'cors'
2import * as express from 'express' 2import * as express from 'express'
3import * as RateLimit from 'express-rate-limit' 3import * as RateLimit from 'express-rate-limit'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../../shared/models'
5import { badRequest } from '../../helpers/express-utils' 5import { badRequest } from '../../helpers/express-utils'
6import { CONFIG } from '../../initializers/config' 6import { CONFIG } from '../../initializers/config'
7import { abuseRouter } from './abuse' 7import { abuseRouter } from './abuse'
diff --git a/server/controllers/api/oauth-clients.ts b/server/controllers/api/oauth-clients.ts
index 15bbf5c4d..f95f06864 100644
--- a/server/controllers/api/oauth-clients.ts
+++ b/server/controllers/api/oauth-clients.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { OAuthClientLocal } from '../../../shared' 2import { OAuthClientLocal } from '../../../shared'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
4import { logger } from '../../helpers/logger' 4import { logger } from '../../helpers/logger'
5import { CONFIG } from '../../initializers/config' 5import { CONFIG } from '../../initializers/config'
6import { asyncMiddleware, openapiOperationDoc } from '../../middlewares' 6import { asyncMiddleware, openapiOperationDoc } from '../../middlewares'
diff --git a/server/controllers/api/overviews.ts b/server/controllers/api/overviews.ts
index ad879aad6..2dfac15ef 100644
--- a/server/controllers/api/overviews.ts
+++ b/server/controllers/api/overviews.ts
@@ -1,12 +1,13 @@
1import * as express from 'express' 1import * as express from 'express'
2import * as memoizee from 'memoizee'
3import { logger } from '@server/helpers/logger'
4import { Hooks } from '@server/lib/plugins/hooks'
5import { VideoModel } from '@server/models/video/video'
6import { CategoryOverview, ChannelOverview, TagOverview, VideosOverview } from '../../../shared/models/overviews'
2import { buildNSFWFilter } from '../../helpers/express-utils' 7import { buildNSFWFilter } from '../../helpers/express-utils'
3import { VideoModel } from '../../models/video/video' 8import { MEMOIZE_TTL, OVERVIEWS } from '../../initializers/constants'
4import { asyncMiddleware, optionalAuthenticate, videosOverviewValidator } from '../../middlewares' 9import { asyncMiddleware, optionalAuthenticate, videosOverviewValidator } from '../../middlewares'
5import { TagModel } from '../../models/video/tag' 10import { TagModel } from '../../models/video/tag'
6import { CategoryOverview, ChannelOverview, TagOverview, VideosOverview } from '../../../shared/models/overviews'
7import { MEMOIZE_TTL, OVERVIEWS } from '../../initializers/constants'
8import * as memoizee from 'memoizee'
9import { logger } from '@server/helpers/logger'
10 11
11const overviewsRouter = express.Router() 12const overviewsRouter = express.Router()
12 13
@@ -108,7 +109,7 @@ async function getVideos (
108 res: express.Response, 109 res: express.Response,
109 where: { videoChannelId?: number, tagsOneOf?: string[], categoryOneOf?: number[] } 110 where: { videoChannelId?: number, tagsOneOf?: string[], categoryOneOf?: number[] }
110) { 111) {
111 const query = Object.assign({ 112 const query = await Hooks.wrapObject({
112 start: 0, 113 start: 0,
113 count: 12, 114 count: 12,
114 sort: '-createdAt', 115 sort: '-createdAt',
@@ -116,10 +117,16 @@ async function getVideos (
116 nsfw: buildNSFWFilter(res), 117 nsfw: buildNSFWFilter(res),
117 user: res.locals.oauth ? res.locals.oauth.token.User : undefined, 118 user: res.locals.oauth ? res.locals.oauth.token.User : undefined,
118 withFiles: false, 119 withFiles: false,
119 countVideos: false 120 countVideos: false,
120 }, where) 121
122 ...where
123 }, 'filter:api.overviews.videos.list.params')
121 124
122 const { data } = await VideoModel.listForApi(query) 125 const { data } = await Hooks.wrapPromiseFun(
126 VideoModel.listForApi,
127 query,
128 'filter:api.overviews.videos.list.result'
129 )
123 130
124 return data.map(d => d.toFormattedJSON()) 131 return data.map(d => d.toFormattedJSON())
125} 132}
diff --git a/server/controllers/api/plugins.ts b/server/controllers/api/plugins.ts
index 1e6a02c49..3a9ef34e8 100644
--- a/server/controllers/api/plugins.ts
+++ b/server/controllers/api/plugins.ts
@@ -23,8 +23,8 @@ import {
23 updatePluginSettingsValidator 23 updatePluginSettingsValidator
24} from '@server/middlewares/validators/plugins' 24} from '@server/middlewares/validators/plugins'
25import { PluginModel } from '@server/models/server/plugin' 25import { PluginModel } from '@server/models/server/plugin'
26import { HttpStatusCode } from '@shared/core-utils'
27import { 26import {
27 HttpStatusCode,
28 InstallOrUpdatePlugin, 28 InstallOrUpdatePlugin,
29 ManagePlugin, 29 ManagePlugin,
30 PeertubePluginIndexList, 30 PeertubePluginIndexList,
diff --git a/server/controllers/api/search/search-video-channels.ts b/server/controllers/api/search/search-video-channels.ts
index 16beeed60..eef222506 100644
--- a/server/controllers/api/search/search-video-channels.ts
+++ b/server/controllers/api/search/search-video-channels.ts
@@ -1,14 +1,14 @@
1import * as express from 'express' 1import * as express from 'express'
2import { sanitizeUrl } from '@server/helpers/core-utils' 2import { sanitizeUrl } from '@server/helpers/core-utils'
3import { pickSearchChannelQuery } from '@server/helpers/query'
3import { doJSONRequest } from '@server/helpers/requests' 4import { doJSONRequest } from '@server/helpers/requests'
4import { CONFIG } from '@server/initializers/config' 5import { CONFIG } from '@server/initializers/config'
5import { WEBSERVER } from '@server/initializers/constants' 6import { WEBSERVER } from '@server/initializers/constants'
6import { Hooks } from '@server/lib/plugins/hooks' 7import { Hooks } from '@server/lib/plugins/hooks'
7import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search' 8import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
8import { getServerActor } from '@server/models/application/application' 9import { getServerActor } from '@server/models/application/application'
9import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 10import { HttpStatusCode, ResultList, VideoChannel } from '@shared/models'
10import { ResultList, VideoChannel } from '@shared/models' 11import { VideoChannelsSearchQueryAfterSanitize } from '../../../../shared/models/search'
11import { VideoChannelsSearchQuery } from '../../../../shared/models/search'
12import { isUserAbleToSearchRemoteURI } from '../../../helpers/express-utils' 12import { isUserAbleToSearchRemoteURI } from '../../../helpers/express-utils'
13import { logger } from '../../../helpers/logger' 13import { logger } from '../../../helpers/logger'
14import { getFormattedObjects } from '../../../helpers/utils' 14import { getFormattedObjects } from '../../../helpers/utils'
@@ -46,8 +46,8 @@ export { searchChannelsRouter }
46// --------------------------------------------------------------------------- 46// ---------------------------------------------------------------------------
47 47
48function searchVideoChannels (req: express.Request, res: express.Response) { 48function searchVideoChannels (req: express.Request, res: express.Response) {
49 const query: VideoChannelsSearchQuery = req.query 49 const query = pickSearchChannelQuery(req.query)
50 const search = query.search 50 let search = query.search || ''
51 51
52 const parts = search.split('@') 52 const parts = search.split('@')
53 53
@@ -58,7 +58,7 @@ function searchVideoChannels (req: express.Request, res: express.Response) {
58 if (isURISearch(search) || isWebfingerSearch) return searchVideoChannelURI(search, isWebfingerSearch, res) 58 if (isURISearch(search) || isWebfingerSearch) return searchVideoChannelURI(search, isWebfingerSearch, res)
59 59
60 // @username -> username to search in DB 60 // @username -> username to search in DB
61 if (query.search.startsWith('@')) query.search = query.search.replace(/^@/, '') 61 if (search.startsWith('@')) search = search.replace(/^@/, '')
62 62
63 if (isSearchIndexSearch(query)) { 63 if (isSearchIndexSearch(query)) {
64 return searchVideoChannelsIndex(query, res) 64 return searchVideoChannelsIndex(query, res)
@@ -67,7 +67,7 @@ function searchVideoChannels (req: express.Request, res: express.Response) {
67 return searchVideoChannelsDB(query, res) 67 return searchVideoChannelsDB(query, res)
68} 68}
69 69
70async function searchVideoChannelsIndex (query: VideoChannelsSearchQuery, res: express.Response) { 70async function searchVideoChannelsIndex (query: VideoChannelsSearchQueryAfterSanitize, res: express.Response) {
71 const result = await buildMutedForSearchIndex(res) 71 const result = await buildMutedForSearchIndex(res)
72 72
73 const body = await Hooks.wrapObject(Object.assign(query, result), 'filter:api.search.video-channels.index.list.params') 73 const body = await Hooks.wrapObject(Object.assign(query, result), 'filter:api.search.video-channels.index.list.params')
@@ -91,15 +91,13 @@ async function searchVideoChannelsIndex (query: VideoChannelsSearchQuery, res: e
91 } 91 }
92} 92}
93 93
94async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: express.Response) { 94async function searchVideoChannelsDB (query: VideoChannelsSearchQueryAfterSanitize, res: express.Response) {
95 const serverActor = await getServerActor() 95 const serverActor = await getServerActor()
96 96
97 const apiOptions = await Hooks.wrapObject({ 97 const apiOptions = await Hooks.wrapObject({
98 actorId: serverActor.id, 98 ...query,
99 search: query.search, 99
100 start: query.start, 100 actorId: serverActor.id
101 count: query.count,
102 sort: query.sort
103 }, 'filter:api.search.video-channels.local.list.params') 101 }, 'filter:api.search.video-channels.local.list.params')
104 102
105 const resultList = await Hooks.wrapPromiseFun( 103 const resultList = await Hooks.wrapPromiseFun(
diff --git a/server/controllers/api/search/search-video-playlists.ts b/server/controllers/api/search/search-video-playlists.ts
index b231ff1e2..0a56f19b7 100644
--- a/server/controllers/api/search/search-video-playlists.ts
+++ b/server/controllers/api/search/search-video-playlists.ts
@@ -2,17 +2,18 @@ import * as express from 'express'
2import { sanitizeUrl } from '@server/helpers/core-utils' 2import { sanitizeUrl } from '@server/helpers/core-utils'
3import { isUserAbleToSearchRemoteURI } from '@server/helpers/express-utils' 3import { isUserAbleToSearchRemoteURI } from '@server/helpers/express-utils'
4import { logger } from '@server/helpers/logger' 4import { logger } from '@server/helpers/logger'
5import { pickSearchPlaylistQuery } from '@server/helpers/query'
5import { doJSONRequest } from '@server/helpers/requests' 6import { doJSONRequest } from '@server/helpers/requests'
6import { getFormattedObjects } from '@server/helpers/utils' 7import { getFormattedObjects } from '@server/helpers/utils'
7import { CONFIG } from '@server/initializers/config' 8import { CONFIG } from '@server/initializers/config'
9import { WEBSERVER } from '@server/initializers/constants'
8import { getOrCreateAPVideoPlaylist } from '@server/lib/activitypub/playlists/get' 10import { getOrCreateAPVideoPlaylist } from '@server/lib/activitypub/playlists/get'
9import { Hooks } from '@server/lib/plugins/hooks' 11import { Hooks } from '@server/lib/plugins/hooks'
10import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search' 12import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
11import { getServerActor } from '@server/models/application/application' 13import { getServerActor } from '@server/models/application/application'
12import { VideoPlaylistModel } from '@server/models/video/video-playlist' 14import { VideoPlaylistModel } from '@server/models/video/video-playlist'
13import { MVideoPlaylistFullSummary } from '@server/types/models' 15import { MVideoPlaylistFullSummary } from '@server/types/models'
14import { HttpStatusCode } from '@shared/core-utils' 16import { HttpStatusCode, ResultList, VideoPlaylist, VideoPlaylistsSearchQueryAfterSanitize } from '@shared/models'
15import { ResultList, VideoPlaylist, VideoPlaylistsSearchQuery } from '@shared/models'
16import { 17import {
17 asyncMiddleware, 18 asyncMiddleware,
18 openapiOperationDoc, 19 openapiOperationDoc,
@@ -23,7 +24,6 @@ import {
23 videoPlaylistsListSearchValidator, 24 videoPlaylistsListSearchValidator,
24 videoPlaylistsSearchSortValidator 25 videoPlaylistsSearchSortValidator
25} from '../../../middlewares' 26} from '../../../middlewares'
26import { WEBSERVER } from '@server/initializers/constants'
27 27
28const searchPlaylistsRouter = express.Router() 28const searchPlaylistsRouter = express.Router()
29 29
@@ -45,7 +45,7 @@ export { searchPlaylistsRouter }
45// --------------------------------------------------------------------------- 45// ---------------------------------------------------------------------------
46 46
47function searchVideoPlaylists (req: express.Request, res: express.Response) { 47function searchVideoPlaylists (req: express.Request, res: express.Response) {
48 const query: VideoPlaylistsSearchQuery = req.query 48 const query = pickSearchPlaylistQuery(req.query)
49 const search = query.search 49 const search = query.search
50 50
51 if (isURISearch(search)) return searchVideoPlaylistsURI(search, res) 51 if (isURISearch(search)) return searchVideoPlaylistsURI(search, res)
@@ -57,7 +57,7 @@ function searchVideoPlaylists (req: express.Request, res: express.Response) {
57 return searchVideoPlaylistsDB(query, res) 57 return searchVideoPlaylistsDB(query, res)
58} 58}
59 59
60async function searchVideoPlaylistsIndex (query: VideoPlaylistsSearchQuery, res: express.Response) { 60async function searchVideoPlaylistsIndex (query: VideoPlaylistsSearchQueryAfterSanitize, res: express.Response) {
61 const result = await buildMutedForSearchIndex(res) 61 const result = await buildMutedForSearchIndex(res)
62 62
63 const body = await Hooks.wrapObject(Object.assign(query, result), 'filter:api.search.video-playlists.index.list.params') 63 const body = await Hooks.wrapObject(Object.assign(query, result), 'filter:api.search.video-playlists.index.list.params')
@@ -81,15 +81,13 @@ async function searchVideoPlaylistsIndex (query: VideoPlaylistsSearchQuery, res:
81 } 81 }
82} 82}
83 83
84async function searchVideoPlaylistsDB (query: VideoPlaylistsSearchQuery, res: express.Response) { 84async function searchVideoPlaylistsDB (query: VideoPlaylistsSearchQueryAfterSanitize, res: express.Response) {
85 const serverActor = await getServerActor() 85 const serverActor = await getServerActor()
86 86
87 const apiOptions = await Hooks.wrapObject({ 87 const apiOptions = await Hooks.wrapObject({
88 followerActorId: serverActor.id, 88 ...query,
89 search: query.search, 89
90 start: query.start, 90 followerActorId: serverActor.id
91 count: query.count,
92 sort: query.sort
93 }, 'filter:api.search.video-playlists.local.list.params') 91 }, 'filter:api.search.video-playlists.local.list.params')
94 92
95 const resultList = await Hooks.wrapPromiseFun( 93 const resultList = await Hooks.wrapPromiseFun(
diff --git a/server/controllers/api/search/search-videos.ts b/server/controllers/api/search/search-videos.ts
index b626baa28..4a6ce0de4 100644
--- a/server/controllers/api/search/search-videos.ts
+++ b/server/controllers/api/search/search-videos.ts
@@ -1,14 +1,14 @@
1import * as express from 'express' 1import * as express from 'express'
2import { sanitizeUrl } from '@server/helpers/core-utils' 2import { sanitizeUrl } from '@server/helpers/core-utils'
3import { pickSearchVideoQuery } from '@server/helpers/query'
3import { doJSONRequest } from '@server/helpers/requests' 4import { doJSONRequest } from '@server/helpers/requests'
4import { CONFIG } from '@server/initializers/config' 5import { CONFIG } from '@server/initializers/config'
5import { WEBSERVER } from '@server/initializers/constants' 6import { WEBSERVER } from '@server/initializers/constants'
6import { getOrCreateAPVideo } from '@server/lib/activitypub/videos' 7import { getOrCreateAPVideo } from '@server/lib/activitypub/videos'
7import { Hooks } from '@server/lib/plugins/hooks' 8import { Hooks } from '@server/lib/plugins/hooks'
8import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search' 9import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
9import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 10import { HttpStatusCode, ResultList, Video } from '@shared/models'
10import { ResultList, Video } from '@shared/models' 11import { VideosSearchQueryAfterSanitize } from '../../../../shared/models/search'
11import { VideosSearchQuery } from '../../../../shared/models/search'
12import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../../helpers/express-utils' 12import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../../helpers/express-utils'
13import { logger } from '../../../helpers/logger' 13import { logger } from '../../../helpers/logger'
14import { getFormattedObjects } from '../../../helpers/utils' 14import { getFormattedObjects } from '../../../helpers/utils'
@@ -47,7 +47,7 @@ export { searchVideosRouter }
47// --------------------------------------------------------------------------- 47// ---------------------------------------------------------------------------
48 48
49function searchVideos (req: express.Request, res: express.Response) { 49function searchVideos (req: express.Request, res: express.Response) {
50 const query: VideosSearchQuery = req.query 50 const query = pickSearchVideoQuery(req.query)
51 const search = query.search 51 const search = query.search
52 52
53 if (isURISearch(search)) { 53 if (isURISearch(search)) {
@@ -61,10 +61,10 @@ function searchVideos (req: express.Request, res: express.Response) {
61 return searchVideosDB(query, res) 61 return searchVideosDB(query, res)
62} 62}
63 63
64async function searchVideosIndex (query: VideosSearchQuery, res: express.Response) { 64async function searchVideosIndex (query: VideosSearchQueryAfterSanitize, res: express.Response) {
65 const result = await buildMutedForSearchIndex(res) 65 const result = await buildMutedForSearchIndex(res)
66 66
67 let body: VideosSearchQuery = Object.assign(query, result) 67 let body = { ...query, ...result }
68 68
69 // Use the default instance NSFW policy if not specified 69 // Use the default instance NSFW policy if not specified
70 if (!body.nsfw) { 70 if (!body.nsfw) {
@@ -98,13 +98,18 @@ async function searchVideosIndex (query: VideosSearchQuery, res: express.Respons
98 } 98 }
99} 99}
100 100
101async function searchVideosDB (query: VideosSearchQuery, res: express.Response) { 101async function searchVideosDB (query: VideosSearchQueryAfterSanitize, res: express.Response) {
102 const apiOptions = await Hooks.wrapObject(Object.assign(query, { 102 const apiOptions = await Hooks.wrapObject({
103 ...query,
104
103 includeLocalVideos: true, 105 includeLocalVideos: true,
104 nsfw: buildNSFWFilter(res, query.nsfw),
105 filter: query.filter, 106 filter: query.filter,
106 user: res.locals.oauth ? res.locals.oauth.token.User : undefined 107
107 }), 'filter:api.search.videos.local.list.params') 108 nsfw: buildNSFWFilter(res, query.nsfw),
109 user: res.locals.oauth
110 ? res.locals.oauth.token.User
111 : undefined
112 }, 'filter:api.search.videos.local.list.params')
108 113
109 const resultList = await Hooks.wrapPromiseFun( 114 const resultList = await Hooks.wrapPromiseFun(
110 VideoModel.searchAndPopulateAccountAndServer, 115 VideoModel.searchAndPopulateAccountAndServer,
diff --git a/server/controllers/api/server/contact.ts b/server/controllers/api/server/contact.ts
index caddc0909..b315e99cf 100644
--- a/server/controllers/api/server/contact.ts
+++ b/server/controllers/api/server/contact.ts
@@ -1,9 +1,9 @@
1import * as express from 'express' 1import * as express from 'express'
2import { asyncMiddleware, contactAdministratorValidator } from '../../../middlewares' 2import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
3import { Redis } from '../../../lib/redis'
4import { Emailer } from '../../../lib/emailer'
5import { ContactForm } from '../../../../shared/models/server' 3import { ContactForm } from '../../../../shared/models/server'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 4import { Emailer } from '../../../lib/emailer'
5import { Redis } from '../../../lib/redis'
6import { asyncMiddleware, contactAdministratorValidator } from '../../../middlewares'
7 7
8const contactRouter = express.Router() 8const contactRouter = express.Router()
9 9
@@ -15,7 +15,7 @@ contactRouter.post('/contact',
15async function contactAdministrator (req: express.Request, res: express.Response) { 15async function contactAdministrator (req: express.Request, res: express.Response) {
16 const data = req.body as ContactForm 16 const data = req.body as ContactForm
17 17
18 await Emailer.Instance.addContactFormJob(data.fromEmail, data.fromName, data.subject, data.body) 18 Emailer.Instance.addContactFormJob(data.fromEmail, data.fromName, data.subject, data.body)
19 19
20 await Redis.Instance.setContactFormIp(req.ip) 20 await Redis.Instance.setContactFormIp(req.ip)
21 21
diff --git a/server/controllers/api/server/debug.ts b/server/controllers/api/server/debug.ts
index a6e9147f3..0601b89ce 100644
--- a/server/controllers/api/server/debug.ts
+++ b/server/controllers/api/server/debug.ts
@@ -1,8 +1,8 @@
1import * as express from 'express'
1import { InboxManager } from '@server/lib/activitypub/inbox-manager' 2import { InboxManager } from '@server/lib/activitypub/inbox-manager'
2import { RemoveDanglingResumableUploadsScheduler } from '@server/lib/schedulers/remove-dangling-resumable-uploads-scheduler' 3import { RemoveDanglingResumableUploadsScheduler } from '@server/lib/schedulers/remove-dangling-resumable-uploads-scheduler'
3import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 4import { Debug, SendDebugCommand } from '@shared/models'
4import { SendDebugCommand } from '@shared/models' 5import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
5import * as express from 'express'
6import { UserRight } from '../../../../shared/models/users' 6import { UserRight } from '../../../../shared/models/users'
7import { authenticate, ensureUserHasRight } from '../../../middlewares' 7import { authenticate, ensureUserHasRight } from '../../../middlewares'
8 8
@@ -32,7 +32,7 @@ function getDebug (req: express.Request, res: express.Response) {
32 return res.json({ 32 return res.json({
33 ip: req.ip, 33 ip: req.ip,
34 activityPubMessagesWaiting: InboxManager.Instance.getActivityPubMessagesWaiting() 34 activityPubMessagesWaiting: InboxManager.Instance.getActivityPubMessagesWaiting()
35 }) 35 } as Debug)
36} 36}
37 37
38async function runCommand (req: express.Request, res: express.Response) { 38async function runCommand (req: express.Request, res: express.Response) {
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts
index 12357a2ca..cbe6b7e4f 100644
--- a/server/controllers/api/server/follows.ts
+++ b/server/controllers/api/server/follows.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { getServerActor } from '@server/models/application/application' 2import { getServerActor } from '@server/models/application/application'
3import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { UserRight } from '../../../../shared/models/users' 4import { UserRight } from '../../../../shared/models/users'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { getFormattedObjects } from '../../../helpers/utils' 6import { getFormattedObjects } from '../../../helpers/utils'
@@ -29,6 +29,7 @@ import {
29 removeFollowingValidator 29 removeFollowingValidator
30} from '../../../middlewares/validators' 30} from '../../../middlewares/validators'
31import { ActorFollowModel } from '../../../models/actor/actor-follow' 31import { ActorFollowModel } from '../../../models/actor/actor-follow'
32import { ServerFollowCreate } from '@shared/models'
32 33
33const serverFollowsRouter = express.Router() 34const serverFollowsRouter = express.Router()
34serverFollowsRouter.get('/following', 35serverFollowsRouter.get('/following',
@@ -45,10 +46,10 @@ serverFollowsRouter.post('/following',
45 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW), 46 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
46 followValidator, 47 followValidator,
47 setBodyHostsPort, 48 setBodyHostsPort,
48 asyncMiddleware(followInstance) 49 asyncMiddleware(addFollow)
49) 50)
50 51
51serverFollowsRouter.delete('/following/:host', 52serverFollowsRouter.delete('/following/:hostOrHandle',
52 authenticate, 53 authenticate,
53 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW), 54 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
54 asyncMiddleware(removeFollowingValidator), 55 asyncMiddleware(removeFollowingValidator),
@@ -125,8 +126,8 @@ async function listFollowers (req: express.Request, res: express.Response) {
125 return res.json(getFormattedObjects(resultList.data, resultList.total)) 126 return res.json(getFormattedObjects(resultList.data, resultList.total))
126} 127}
127 128
128async function followInstance (req: express.Request, res: express.Response) { 129async function addFollow (req: express.Request, res: express.Response) {
129 const hosts = req.body.hosts as string[] 130 const { hosts, handles } = req.body as ServerFollowCreate
130 const follower = await getServerActor() 131 const follower = await getServerActor()
131 132
132 for (const host of hosts) { 133 for (const host of hosts) {
@@ -139,6 +140,18 @@ async function followInstance (req: express.Request, res: express.Response) {
139 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) 140 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload })
140 } 141 }
141 142
143 for (const handle of handles) {
144 const [ name, host ] = handle.split('@')
145
146 const payload = {
147 host,
148 name,
149 followerActorId: follower.id
150 }
151
152 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload })
153 }
154
142 return res.status(HttpStatusCode.NO_CONTENT_204).end() 155 return res.status(HttpStatusCode.NO_CONTENT_204).end()
143} 156}
144 157
diff --git a/server/controllers/api/server/index.ts b/server/controllers/api/server/index.ts
index 6b8793a19..32fefefc0 100644
--- a/server/controllers/api/server/index.ts
+++ b/server/controllers/api/server/index.ts
@@ -1,11 +1,11 @@
1import * as express from 'express' 1import * as express from 'express'
2import { contactRouter } from './contact'
3import { debugRouter } from './debug'
2import { serverFollowsRouter } from './follows' 4import { serverFollowsRouter } from './follows'
3import { statsRouter } from './stats' 5import { logsRouter } from './logs'
4import { serverRedundancyRouter } from './redundancy' 6import { serverRedundancyRouter } from './redundancy'
5import { serverBlocklistRouter } from './server-blocklist' 7import { serverBlocklistRouter } from './server-blocklist'
6import { contactRouter } from './contact' 8import { statsRouter } from './stats'
7import { logsRouter } from './logs'
8import { debugRouter } from './debug'
9 9
10const serverRouter = express.Router() 10const serverRouter = express.Router()
11 11
diff --git a/server/controllers/api/server/logs.ts b/server/controllers/api/server/logs.ts
index 4b543d686..39eceb654 100644
--- a/server/controllers/api/server/logs.ts
+++ b/server/controllers/api/server/logs.ts
@@ -1,14 +1,13 @@
1import * as express from 'express' 1import * as express from 'express'
2import { UserRight } from '../../../../shared/models/users'
3import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares'
4import { mtimeSortFilesDesc } from '../../../../shared/core-utils/logs/logs'
5import { readdir, readFile } from 'fs-extra' 2import { readdir, readFile } from 'fs-extra'
6import { AUDIT_LOG_FILENAME, MAX_LOGS_OUTPUT_CHARACTERS, LOG_FILENAME } from '../../../initializers/constants'
7import { join } from 'path' 3import { join } from 'path'
8import { getAuditLogsValidator, getLogsValidator } from '../../../middlewares/validators/logs' 4import { logger, mtimeSortFilesDesc } from '@server/helpers/logger'
9import { LogLevel } from '../../../../shared/models/server/log-level.type' 5import { LogLevel } from '../../../../shared/models/server/log-level.type'
6import { UserRight } from '../../../../shared/models/users'
10import { CONFIG } from '../../../initializers/config' 7import { CONFIG } from '../../../initializers/config'
11import { logger } from '@server/helpers/logger' 8import { AUDIT_LOG_FILENAME, LOG_FILENAME, MAX_LOGS_OUTPUT_CHARACTERS } from '../../../initializers/constants'
9import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares'
10import { getAuditLogsValidator, getLogsValidator } from '../../../middlewares/validators/logs'
12 11
13const logsRouter = express.Router() 12const logsRouter = express.Router()
14 13
diff --git a/server/controllers/api/server/redundancy.ts b/server/controllers/api/server/redundancy.ts
index bc593ad43..99d1c762b 100644
--- a/server/controllers/api/server/redundancy.ts
+++ b/server/controllers/api/server/redundancy.ts
@@ -1,5 +1,10 @@
1import * as express from 'express' 1import * as express from 'express'
2import { JobQueue } from '@server/lib/job-queue'
3import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy'
4import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
2import { UserRight } from '../../../../shared/models/users' 5import { UserRight } from '../../../../shared/models/users'
6import { logger } from '../../../helpers/logger'
7import { removeRedundanciesOfServer, removeVideoRedundancy } from '../../../lib/redundancy'
3import { 8import {
4 asyncMiddleware, 9 asyncMiddleware,
5 authenticate, 10 authenticate,
@@ -10,16 +15,11 @@ import {
10 videoRedundanciesSortValidator 15 videoRedundanciesSortValidator
11} from '../../../middlewares' 16} from '../../../middlewares'
12import { 17import {
13 listVideoRedundanciesValidator,
14 updateServerRedundancyValidator,
15 addVideoRedundancyValidator, 18 addVideoRedundancyValidator,
16 removeVideoRedundancyValidator 19 listVideoRedundanciesValidator,
20 removeVideoRedundancyValidator,
21 updateServerRedundancyValidator
17} from '../../../middlewares/validators/redundancy' 22} from '../../../middlewares/validators/redundancy'
18import { removeRedundanciesOfServer, removeVideoRedundancy } from '../../../lib/redundancy'
19import { logger } from '../../../helpers/logger'
20import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy'
21import { JobQueue } from '@server/lib/job-queue'
22import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
23 23
24const serverRedundancyRouter = express.Router() 24const serverRedundancyRouter = express.Router()
25 25
diff --git a/server/controllers/api/server/server-blocklist.ts b/server/controllers/api/server/server-blocklist.ts
index a86bc7d19..b3ee50d85 100644
--- a/server/controllers/api/server/server-blocklist.ts
+++ b/server/controllers/api/server/server-blocklist.ts
@@ -1,8 +1,9 @@
1import 'multer' 1import 'multer'
2import * as express from 'express' 2import * as express from 'express'
3import { logger } from '@server/helpers/logger' 3import { logger } from '@server/helpers/logger'
4import { UserNotificationModel } from '@server/models/user/user-notification'
5import { getServerActor } from '@server/models/application/application' 4import { getServerActor } from '@server/models/application/application'
5import { UserNotificationModel } from '@server/models/user/user-notification'
6import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
6import { UserRight } from '../../../../shared/models/users' 7import { UserRight } from '../../../../shared/models/users'
7import { getFormattedObjects } from '../../../helpers/utils' 8import { getFormattedObjects } from '../../../helpers/utils'
8import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist' 9import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist'
@@ -25,7 +26,6 @@ import {
25} from '../../../middlewares/validators' 26} from '../../../middlewares/validators'
26import { AccountBlocklistModel } from '../../../models/account/account-blocklist' 27import { AccountBlocklistModel } from '../../../models/account/account-blocklist'
27import { ServerBlocklistModel } from '../../../models/server/server-blocklist' 28import { ServerBlocklistModel } from '../../../models/server/server-blocklist'
28import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
29 29
30const serverBlocklistRouter = express.Router() 30const serverBlocklistRouter = express.Router()
31 31
diff --git a/server/controllers/api/server/stats.ts b/server/controllers/api/server/stats.ts
index 3aea12450..397702548 100644
--- a/server/controllers/api/server/stats.ts
+++ b/server/controllers/api/server/stats.ts
@@ -2,12 +2,12 @@ import * as express from 'express'
2import { StatsManager } from '@server/lib/stat-manager' 2import { StatsManager } from '@server/lib/stat-manager'
3import { ROUTE_CACHE_LIFETIME } from '../../../initializers/constants' 3import { ROUTE_CACHE_LIFETIME } from '../../../initializers/constants'
4import { asyncMiddleware } from '../../../middlewares' 4import { asyncMiddleware } from '../../../middlewares'
5import { cacheRoute } from '../../../middlewares/cache' 5import { cacheRoute } from '../../../middlewares/cache/cache'
6 6
7const statsRouter = express.Router() 7const statsRouter = express.Router()
8 8
9statsRouter.get('/stats', 9statsRouter.get('/stats',
10 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.STATS)), 10 cacheRoute(ROUTE_CACHE_LIFETIME.STATS),
11 asyncMiddleware(getStats) 11 asyncMiddleware(getStats)
12) 12)
13 13
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts
index d907b49bf..be800e8b5 100644
--- a/server/controllers/api/users/index.ts
+++ b/server/controllers/api/users/index.ts
@@ -4,8 +4,8 @@ import { tokensRouter } from '@server/controllers/api/users/token'
4import { Hooks } from '@server/lib/plugins/hooks' 4import { Hooks } from '@server/lib/plugins/hooks'
5import { OAuthTokenModel } from '@server/models/oauth/oauth-token' 5import { OAuthTokenModel } from '@server/models/oauth/oauth-token'
6import { MUser, MUserAccountDefault } from '@server/types/models' 6import { MUser, MUserAccountDefault } from '@server/types/models'
7import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared' 7import { UserCreate, UserCreateResult, UserRight, UserRole, UserUpdate } from '../../../../shared'
8import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 8import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
9import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' 9import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
10import { UserRegister } from '../../../../shared/models/users/user-register.model' 10import { UserRegister } from '../../../../shared/models/users/user-register.model'
11import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger' 11import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger'
@@ -220,7 +220,7 @@ async function createUser (req: express.Request, res: express.Response) {
220 account: { 220 account: {
221 id: account.id 221 id: account.id
222 } 222 }
223 } 223 } as UserCreateResult
224 }) 224 })
225} 225}
226 226
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts
index 1f2b2f9dd..ac6faca9c 100644
--- a/server/controllers/api/users/me.ts
+++ b/server/controllers/api/users/me.ts
@@ -2,8 +2,9 @@ import 'multer'
2import * as express from 'express' 2import * as express from 'express'
3import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '@server/helpers/audit-logger' 3import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '@server/helpers/audit-logger'
4import { Hooks } from '@server/lib/plugins/hooks' 4import { Hooks } from '@server/lib/plugins/hooks'
5import { AttributesOnly } from '@shared/core-utils'
5import { ActorImageType, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../../shared' 6import { ActorImageType, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../../shared'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 7import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
7import { UserVideoQuota } from '../../../../shared/models/users/user-video-quota.model' 8import { UserVideoQuota } from '../../../../shared/models/users/user-video-quota.model'
8import { createReqFiles } from '../../../helpers/express-utils' 9import { createReqFiles } from '../../../helpers/express-utils'
9import { getFormattedObjects } from '../../../helpers/utils' 10import { getFormattedObjects } from '../../../helpers/utils'
@@ -31,7 +32,6 @@ import { AccountVideoRateModel } from '../../../models/account/account-video-rat
31import { UserModel } from '../../../models/user/user' 32import { UserModel } from '../../../models/user/user'
32import { VideoModel } from '../../../models/video/video' 33import { VideoModel } from '../../../models/video/video'
33import { VideoImportModel } from '../../../models/video/video-import' 34import { VideoImportModel } from '../../../models/video/video-import'
34import { AttributesOnly } from '@shared/core-utils'
35 35
36const auditLogger = auditLoggerFactory('users') 36const auditLogger = auditLoggerFactory('users')
37 37
diff --git a/server/controllers/api/users/my-blocklist.ts b/server/controllers/api/users/my-blocklist.ts
index a1561b751..24fff83e3 100644
--- a/server/controllers/api/users/my-blocklist.ts
+++ b/server/controllers/api/users/my-blocklist.ts
@@ -1,6 +1,10 @@
1import * as express from 'express'
2import 'multer' 1import 'multer'
2import * as express from 'express'
3import { logger } from '@server/helpers/logger'
4import { UserNotificationModel } from '@server/models/user/user-notification'
5import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
3import { getFormattedObjects } from '../../../helpers/utils' 6import { getFormattedObjects } from '../../../helpers/utils'
7import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist'
4import { 8import {
5 asyncMiddleware, 9 asyncMiddleware,
6 asyncRetryTransactionMiddleware, 10 asyncRetryTransactionMiddleware,
@@ -18,11 +22,7 @@ import {
18 unblockServerByAccountValidator 22 unblockServerByAccountValidator
19} from '../../../middlewares/validators' 23} from '../../../middlewares/validators'
20import { AccountBlocklistModel } from '../../../models/account/account-blocklist' 24import { AccountBlocklistModel } from '../../../models/account/account-blocklist'
21import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist'
22import { ServerBlocklistModel } from '../../../models/server/server-blocklist' 25import { ServerBlocklistModel } from '../../../models/server/server-blocklist'
23import { UserNotificationModel } from '@server/models/user/user-notification'
24import { logger } from '@server/helpers/logger'
25import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
26 26
27const myBlocklistRouter = express.Router() 27const myBlocklistRouter = express.Router()
28 28
diff --git a/server/controllers/api/users/my-history.ts b/server/controllers/api/users/my-history.ts
index cff1697ab..a6e723103 100644
--- a/server/controllers/api/users/my-history.ts
+++ b/server/controllers/api/users/my-history.ts
@@ -1,4 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
3import { getFormattedObjects } from '../../../helpers/utils'
4import { sequelizeTypescript } from '../../../initializers/database'
2import { 5import {
3 asyncMiddleware, 6 asyncMiddleware,
4 asyncRetryTransactionMiddleware, 7 asyncRetryTransactionMiddleware,
@@ -8,10 +11,7 @@ import {
8 userHistoryListValidator, 11 userHistoryListValidator,
9 userHistoryRemoveValidator 12 userHistoryRemoveValidator
10} from '../../../middlewares' 13} from '../../../middlewares'
11import { getFormattedObjects } from '../../../helpers/utils'
12import { UserVideoHistoryModel } from '../../../models/user/user-video-history' 14import { UserVideoHistoryModel } from '../../../models/user/user-video-history'
13import { sequelizeTypescript } from '../../../initializers/database'
14import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
15 15
16const myVideosHistoryRouter = express.Router() 16const myVideosHistoryRouter = express.Router()
17 17
diff --git a/server/controllers/api/users/my-notifications.ts b/server/controllers/api/users/my-notifications.ts
index 2909770da..3beee07c0 100644
--- a/server/controllers/api/users/my-notifications.ts
+++ b/server/controllers/api/users/my-notifications.ts
@@ -1,7 +1,7 @@
1import 'multer' 1import 'multer'
2import * as express from 'express' 2import * as express from 'express'
3import { UserNotificationModel } from '@server/models/user/user-notification' 3import { UserNotificationModel } from '@server/models/user/user-notification'
4import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
5import { UserNotificationSetting } from '../../../../shared/models/users' 5import { UserNotificationSetting } from '../../../../shared/models/users'
6import { getFormattedObjects } from '../../../helpers/utils' 6import { getFormattedObjects } from '../../../helpers/utils'
7import { 7import {
diff --git a/server/controllers/api/users/my-subscriptions.ts b/server/controllers/api/users/my-subscriptions.ts
index 46a73d49e..26a715704 100644
--- a/server/controllers/api/users/my-subscriptions.ts
+++ b/server/controllers/api/users/my-subscriptions.ts
@@ -1,9 +1,9 @@
1import 'multer' 1import 'multer'
2import * as express from 'express' 2import * as express from 'express'
3import { pickCommonVideoQuery } from '@server/helpers/query'
3import { sendUndoFollow } from '@server/lib/activitypub/send' 4import { sendUndoFollow } from '@server/lib/activitypub/send'
4import { VideoChannelModel } from '@server/models/video/video-channel' 5import { VideoChannelModel } from '@server/models/video/video-channel'
5import { VideosCommonQuery } from '@shared/models' 6import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' 7import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils'
8import { getFormattedObjects } from '../../../helpers/utils' 8import { getFormattedObjects } from '../../../helpers/utils'
9import { WEBSERVER } from '../../../initializers/constants' 9import { WEBSERVER } from '../../../initializers/constants'
@@ -170,20 +170,13 @@ async function getUserSubscriptions (req: express.Request, res: express.Response
170async function getUserSubscriptionVideos (req: express.Request, res: express.Response) { 170async function getUserSubscriptionVideos (req: express.Request, res: express.Response) {
171 const user = res.locals.oauth.token.User 171 const user = res.locals.oauth.token.User
172 const countVideos = getCountVideos(req) 172 const countVideos = getCountVideos(req)
173 const query = req.query as VideosCommonQuery 173 const query = pickCommonVideoQuery(req.query)
174 174
175 const resultList = await VideoModel.listForApi({ 175 const resultList = await VideoModel.listForApi({
176 start: query.start, 176 ...query,
177 count: query.count, 177
178 sort: query.sort,
179 includeLocalVideos: false, 178 includeLocalVideos: false,
180 categoryOneOf: query.categoryOneOf,
181 licenceOneOf: query.licenceOneOf,
182 languageOneOf: query.languageOneOf,
183 tagsOneOf: query.tagsOneOf,
184 tagsAllOf: query.tagsAllOf,
185 nsfw: buildNSFWFilter(res, query.nsfw), 179 nsfw: buildNSFWFilter(res, query.nsfw),
186 filter: query.filter,
187 withFiles: false, 180 withFiles: false,
188 followerActorId: user.Account.Actor.id, 181 followerActorId: user.Account.Actor.id,
189 user, 182 user,
diff --git a/server/controllers/api/users/my-video-playlists.ts b/server/controllers/api/users/my-video-playlists.ts
index d0bd99463..76e741ba5 100644
--- a/server/controllers/api/users/my-video-playlists.ts
+++ b/server/controllers/api/users/my-video-playlists.ts
@@ -1,8 +1,8 @@
1import * as express from 'express' 1import * as express from 'express'
2import { VideosExistInPlaylists } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
2import { asyncMiddleware, authenticate } from '../../../middlewares' 3import { asyncMiddleware, authenticate } from '../../../middlewares'
3import { doVideosInPlaylistExistValidator } from '../../../middlewares/validators/videos/video-playlists' 4import { doVideosInPlaylistExistValidator } from '../../../middlewares/validators/videos/video-playlists'
4import { VideoPlaylistModel } from '../../../models/video/video-playlist' 5import { VideoPlaylistModel } from '../../../models/video/video-playlist'
5import { VideosExistInPlaylists } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
6 6
7const myVideoPlaylistsRouter = express.Router() 7const myVideoPlaylistsRouter = express.Router()
8 8
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index bc8d203b0..7bdb33737 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -1,9 +1,10 @@
1import * as express from 'express' 1import * as express from 'express'
2import { pickCommonVideoQuery } from '@server/helpers/query'
2import { Hooks } from '@server/lib/plugins/hooks' 3import { Hooks } from '@server/lib/plugins/hooks'
3import { getServerActor } from '@server/models/application/application' 4import { getServerActor } from '@server/models/application/application'
4import { MChannelBannerAccountDefault } from '@server/types/models' 5import { MChannelBannerAccountDefault } from '@server/types/models'
5import { ActorImageType, VideoChannelCreate, VideoChannelUpdate, VideosCommonQuery } from '../../../shared' 6import { ActorImageType, VideoChannelCreate, VideoChannelUpdate } from '../../../shared'
6import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 7import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
7import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger' 8import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger'
8import { resetSequelizeInstance } from '../../helpers/database-utils' 9import { resetSequelizeInstance } from '../../helpers/database-utils'
9import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' 10import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
@@ -309,20 +310,13 @@ async function listVideoChannelVideos (req: express.Request, res: express.Respon
309 const videoChannelInstance = res.locals.videoChannel 310 const videoChannelInstance = res.locals.videoChannel
310 const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined 311 const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined
311 const countVideos = getCountVideos(req) 312 const countVideos = getCountVideos(req)
312 const query = req.query as VideosCommonQuery 313 const query = pickCommonVideoQuery(req.query)
313 314
314 const apiOptions = await Hooks.wrapObject({ 315 const apiOptions = await Hooks.wrapObject({
316 ...query,
317
315 followerActorId, 318 followerActorId,
316 start: query.start,
317 count: query.count,
318 sort: query.sort,
319 includeLocalVideos: true, 319 includeLocalVideos: true,
320 categoryOneOf: query.categoryOneOf,
321 licenceOneOf: query.licenceOneOf,
322 languageOneOf: query.languageOneOf,
323 tagsOneOf: query.tagsOneOf,
324 tagsAllOf: query.tagsAllOf,
325 filter: query.filter,
326 nsfw: buildNSFWFilter(res, query.nsfw), 320 nsfw: buildNSFWFilter(res, query.nsfw),
327 withFiles: false, 321 withFiles: false,
328 videoChannelId: videoChannelInstance.id, 322 videoChannelId: videoChannelInstance.id,
diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts
index 87a6f6bbe..4971d0a77 100644
--- a/server/controllers/api/video-playlist.ts
+++ b/server/controllers/api/video-playlist.ts
@@ -5,7 +5,8 @@ import { scheduleRefreshIfNeeded } from '@server/lib/activitypub/playlists'
5import { Hooks } from '@server/lib/plugins/hooks' 5import { Hooks } from '@server/lib/plugins/hooks'
6import { getServerActor } from '@server/models/application/application' 6import { getServerActor } from '@server/models/application/application'
7import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/types/models' 7import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/types/models'
8import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 8import { VideoPlaylistCreateResult, VideoPlaylistElementCreateResult } from '@shared/models'
9import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
9import { VideoPlaylistCreate } from '../../../shared/models/videos/playlist/video-playlist-create.model' 10import { VideoPlaylistCreate } from '../../../shared/models/videos/playlist/video-playlist-create.model'
10import { VideoPlaylistElementCreate } from '../../../shared/models/videos/playlist/video-playlist-element-create.model' 11import { VideoPlaylistElementCreate } from '../../../shared/models/videos/playlist/video-playlist-element-create.model'
11import { VideoPlaylistElementUpdate } from '../../../shared/models/videos/playlist/video-playlist-element-update.model' 12import { VideoPlaylistElementUpdate } from '../../../shared/models/videos/playlist/video-playlist-element-update.model'
@@ -202,7 +203,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) {
202 id: videoPlaylistCreated.id, 203 id: videoPlaylistCreated.id,
203 shortUUID: uuidToShort(videoPlaylistCreated.uuid), 204 shortUUID: uuidToShort(videoPlaylistCreated.uuid),
204 uuid: videoPlaylistCreated.uuid 205 uuid: videoPlaylistCreated.uuid
205 } 206 } as VideoPlaylistCreateResult
206 }) 207 })
207} 208}
208 209
@@ -338,8 +339,8 @@ async function addVideoInPlaylist (req: express.Request, res: express.Response)
338 return res.json({ 339 return res.json({
339 videoPlaylistElement: { 340 videoPlaylistElement: {
340 id: playlistElement.id 341 id: playlistElement.id
341 } 342 } as VideoPlaylistElementCreateResult
342 }).end() 343 })
343} 344}
344 345
345async function updateVideoPlaylistElement (req: express.Request, res: express.Response) { 346async function updateVideoPlaylistElement (req: express.Request, res: express.Response) {
diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts
index 530e17965..6bc768471 100644
--- a/server/controllers/api/videos/blacklist.ts
+++ b/server/controllers/api/videos/blacklist.ts
@@ -1,6 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { blacklistVideo, unblacklistVideo } from '@server/lib/video-blacklist' 2import { blacklistVideo, unblacklistVideo } from '@server/lib/video-blacklist'
3import { UserRight, VideoBlacklistCreate } from '../../../../shared' 3import { UserRight, VideoBlacklistCreate } from '../../../../shared'
4import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
5import { getFormattedObjects } from '../../../helpers/utils' 6import { getFormattedObjects } from '../../../helpers/utils'
6import { sequelizeTypescript } from '../../../initializers/database' 7import { sequelizeTypescript } from '../../../initializers/database'
@@ -19,7 +20,6 @@ import {
19 videosBlacklistUpdateValidator 20 videosBlacklistUpdateValidator
20} from '../../../middlewares' 21} from '../../../middlewares'
21import { VideoBlacklistModel } from '../../../models/video/video-blacklist' 22import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
22import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
23 23
24const blacklistRouter = express.Router() 24const blacklistRouter = express.Router()
25 25
diff --git a/server/controllers/api/videos/captions.ts b/server/controllers/api/videos/captions.ts
index ad7423a31..4008de60f 100644
--- a/server/controllers/api/videos/captions.ts
+++ b/server/controllers/api/videos/captions.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { MVideoCaption } from '@server/types/models' 2import { MVideoCaption } from '@server/types/models'
3import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' 4import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils'
5import { createReqFiles } from '../../../helpers/express-utils' 5import { createReqFiles } from '../../../helpers/express-utils'
6import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts
index e6f28c1cb..cb696f652 100644
--- a/server/controllers/api/videos/comment.ts
+++ b/server/controllers/api/videos/comment.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 2import { ResultList, ThreadsResultList, UserRight, VideoCommentCreate } from '../../../../shared/models'
3import { ResultList, ThreadsResultList, UserRight } from '../../../../shared/models' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { VideoCommentCreate } from '../../../../shared/models/videos/comment/video-comment.model' 4import { VideoCommentThreads } from '../../../../shared/models/videos/comment/video-comment.model'
5import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger' 5import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger'
6import { getFormattedObjects } from '../../../helpers/utils' 6import { getFormattedObjects } from '../../../helpers/utils'
7import { sequelizeTypescript } from '../../../initializers/database' 7import { sequelizeTypescript } from '../../../initializers/database'
@@ -136,7 +136,7 @@ async function listVideoThreads (req: express.Request, res: express.Response) {
136 return res.json({ 136 return res.json({
137 ...getFormattedObjects(resultList.data, resultList.total), 137 ...getFormattedObjects(resultList.data, resultList.total),
138 totalNotDeletedComments: resultList.totalNotDeletedComments 138 totalNotDeletedComments: resultList.totalNotDeletedComments
139 }) 139 } as VideoCommentThreads)
140} 140}
141 141
142async function listVideoThreadComments (req: express.Request, res: express.Response) { 142async function listVideoThreadComments (req: express.Request, res: express.Response) {
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 74b100e59..49490f79b 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -1,12 +1,12 @@
1import * as express from 'express' 1import * as express from 'express'
2import toInt from 'validator/lib/toInt' 2import toInt from 'validator/lib/toInt'
3import { pickCommonVideoQuery } from '@server/helpers/query'
3import { doJSONRequest } from '@server/helpers/requests' 4import { doJSONRequest } from '@server/helpers/requests'
4import { LiveManager } from '@server/lib/live' 5import { LiveManager } from '@server/lib/live'
5import { openapiOperationDoc } from '@server/middlewares/doc' 6import { openapiOperationDoc } from '@server/middlewares/doc'
6import { getServerActor } from '@server/models/application/application' 7import { getServerActor } from '@server/models/application/application'
7import { MVideoAccountLight } from '@server/types/models' 8import { MVideoAccountLight } from '@server/types/models'
8import { VideosCommonQuery } from '../../../../shared' 9import { HttpStatusCode } from '../../../../shared/models'
9import { HttpStatusCode } from '../../../../shared/core-utils/miscs'
10import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' 10import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
11import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' 11import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils'
12import { logger } from '../../../helpers/logger' 12import { logger } from '../../../helpers/logger'
@@ -211,22 +211,14 @@ async function getVideoFileMetadata (req: express.Request, res: express.Response
211} 211}
212 212
213async function listVideos (req: express.Request, res: express.Response) { 213async function listVideos (req: express.Request, res: express.Response) {
214 const query = req.query as VideosCommonQuery 214 const query = pickCommonVideoQuery(req.query)
215 const countVideos = getCountVideos(req) 215 const countVideos = getCountVideos(req)
216 216
217 const apiOptions = await Hooks.wrapObject({ 217 const apiOptions = await Hooks.wrapObject({
218 start: query.start, 218 ...query,
219 count: query.count, 219
220 sort: query.sort,
221 includeLocalVideos: true, 220 includeLocalVideos: true,
222 categoryOneOf: query.categoryOneOf,
223 licenceOneOf: query.licenceOneOf,
224 languageOneOf: query.languageOneOf,
225 tagsOneOf: query.tagsOneOf,
226 tagsAllOf: query.tagsAllOf,
227 nsfw: buildNSFWFilter(res, query.nsfw), 221 nsfw: buildNSFWFilter(res, query.nsfw),
228 isLive: query.isLive,
229 filter: query.filter,
230 withFiles: false, 222 withFiles: false,
231 user: res.locals.oauth ? res.locals.oauth.token.User : undefined, 223 user: res.locals.oauth ? res.locals.oauth.token.User : undefined,
232 countVideos 224 countVideos
diff --git a/server/controllers/api/videos/live.ts b/server/controllers/api/videos/live.ts
index d8c51c2d4..ed4da8f47 100644
--- a/server/controllers/api/videos/live.ts
+++ b/server/controllers/api/videos/live.ts
@@ -11,7 +11,7 @@ import { videoLiveAddValidator, videoLiveGetValidator, videoLiveUpdateValidator
11import { VideoLiveModel } from '@server/models/video/video-live' 11import { VideoLiveModel } from '@server/models/video/video-live'
12import { MVideoDetails, MVideoFullLight } from '@server/types/models' 12import { MVideoDetails, MVideoFullLight } from '@server/types/models'
13import { LiveVideoCreate, LiveVideoUpdate, VideoState } from '../../../../shared' 13import { LiveVideoCreate, LiveVideoUpdate, VideoState } from '../../../../shared'
14import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 14import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
15import { logger } from '../../../helpers/logger' 15import { logger } from '../../../helpers/logger'
16import { sequelizeTypescript } from '../../../initializers/database' 16import { sequelizeTypescript } from '../../../initializers/database'
17import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail' 17import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail'
diff --git a/server/controllers/api/videos/ownership.ts b/server/controllers/api/videos/ownership.ts
index 1bb96e046..f48acbc68 100644
--- a/server/controllers/api/videos/ownership.ts
+++ b/server/controllers/api/videos/ownership.ts
@@ -1,6 +1,12 @@
1import * as express from 'express' 1import * as express from 'express'
2import { MVideoFullLight } from '@server/types/models'
3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { VideoChangeOwnershipStatus, VideoState } from '../../../../shared/models/videos'
2import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { getFormattedObjects } from '../../../helpers/utils'
3import { sequelizeTypescript } from '../../../initializers/database' 7import { sequelizeTypescript } from '../../../initializers/database'
8import { sendUpdateVideo } from '../../../lib/activitypub/send'
9import { changeVideoChannelShare } from '../../../lib/activitypub/share'
4import { 10import {
5 asyncMiddleware, 11 asyncMiddleware,
6 asyncRetryTransactionMiddleware, 12 asyncRetryTransactionMiddleware,
@@ -11,15 +17,9 @@ import {
11 videosChangeOwnershipValidator, 17 videosChangeOwnershipValidator,
12 videosTerminateChangeOwnershipValidator 18 videosTerminateChangeOwnershipValidator
13} from '../../../middlewares' 19} from '../../../middlewares'
20import { VideoModel } from '../../../models/video/video'
14import { VideoChangeOwnershipModel } from '../../../models/video/video-change-ownership' 21import { VideoChangeOwnershipModel } from '../../../models/video/video-change-ownership'
15import { VideoChangeOwnershipStatus, VideoState } from '../../../../shared/models/videos'
16import { VideoChannelModel } from '../../../models/video/video-channel' 22import { VideoChannelModel } from '../../../models/video/video-channel'
17import { getFormattedObjects } from '../../../helpers/utils'
18import { changeVideoChannelShare } from '../../../lib/activitypub/share'
19import { sendUpdateVideo } from '../../../lib/activitypub/send'
20import { VideoModel } from '../../../models/video/video'
21import { MVideoFullLight } from '@server/types/models'
22import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
23 23
24const ownershipVideoRouter = express.Router() 24const ownershipVideoRouter = express.Router()
25 25
diff --git a/server/controllers/api/videos/rate.ts b/server/controllers/api/videos/rate.ts
index 84f42633e..96f6cd886 100644
--- a/server/controllers/api/videos/rate.ts
+++ b/server/controllers/api/videos/rate.ts
@@ -1,13 +1,13 @@
1import * as express from 'express' 1import * as express from 'express'
2import { UserVideoRateUpdate } from '../../../../shared' 2import { UserVideoRateUpdate } from '../../../../shared'
3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
3import { logger } from '../../../helpers/logger' 4import { logger } from '../../../helpers/logger'
4import { VIDEO_RATE_TYPES } from '../../../initializers/constants' 5import { VIDEO_RATE_TYPES } from '../../../initializers/constants'
6import { sequelizeTypescript } from '../../../initializers/database'
5import { getLocalRateUrl, sendVideoRateChange } from '../../../lib/activitypub/video-rates' 7import { getLocalRateUrl, sendVideoRateChange } from '../../../lib/activitypub/video-rates'
6import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoUpdateRateValidator } from '../../../middlewares' 8import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoUpdateRateValidator } from '../../../middlewares'
7import { AccountModel } from '../../../models/account/account' 9import { AccountModel } from '../../../models/account/account'
8import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 10import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
9import { sequelizeTypescript } from '../../../initializers/database'
10import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
11 11
12const rateVideoRouter = express.Router() 12const rateVideoRouter = express.Router()
13 13
diff --git a/server/controllers/api/videos/update.ts b/server/controllers/api/videos/update.ts
index 8affe71c6..49639060b 100644
--- a/server/controllers/api/videos/update.ts
+++ b/server/controllers/api/videos/update.ts
@@ -2,10 +2,11 @@ import * as express from 'express'
2import { Transaction } from 'sequelize/types' 2import { Transaction } from 'sequelize/types'
3import { changeVideoChannelShare } from '@server/lib/activitypub/share' 3import { changeVideoChannelShare } from '@server/lib/activitypub/share'
4import { buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' 4import { buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video'
5import { openapiOperationDoc } from '@server/middlewares/doc'
5import { FilteredModelAttributes } from '@server/types' 6import { FilteredModelAttributes } from '@server/types'
6import { MVideoFullLight } from '@server/types/models' 7import { MVideoFullLight } from '@server/types/models'
7import { VideoUpdate } from '../../../../shared' 8import { VideoUpdate } from '../../../../shared'
8import { HttpStatusCode } from '../../../../shared/core-utils/miscs' 9import { HttpStatusCode } from '../../../../shared/models'
9import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' 10import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
10import { resetSequelizeInstance } from '../../../helpers/database-utils' 11import { resetSequelizeInstance } from '../../../helpers/database-utils'
11import { createReqFiles } from '../../../helpers/express-utils' 12import { createReqFiles } from '../../../helpers/express-utils'
@@ -20,7 +21,6 @@ import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist'
20import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videosUpdateValidator } from '../../../middlewares' 21import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videosUpdateValidator } from '../../../middlewares'
21import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' 22import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update'
22import { VideoModel } from '../../../models/video/video' 23import { VideoModel } from '../../../models/video/video'
23import { openapiOperationDoc } from '@server/middlewares/doc'
24 24
25const lTags = loggerTagsFactory('api', 'video') 25const lTags = loggerTagsFactory('api', 'video')
26const auditLogger = auditLoggerFactory('videos') 26const auditLogger = auditLoggerFactory('videos')
diff --git a/server/controllers/api/videos/upload.ts b/server/controllers/api/videos/upload.ts
index bcd21ac99..408f677ff 100644
--- a/server/controllers/api/videos/upload.ts
+++ b/server/controllers/api/videos/upload.ts
@@ -6,12 +6,12 @@ import { uuidToShort } from '@server/helpers/uuid'
6import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' 6import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
7import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' 7import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url'
8import { addOptimizeOrMergeAudioJob, buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' 8import { addOptimizeOrMergeAudioJob, buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video'
9import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' 9import { generateWebTorrentVideoFilename, getVideoFilePath } from '@server/lib/video-paths'
10import { openapiOperationDoc } from '@server/middlewares/doc' 10import { openapiOperationDoc } from '@server/middlewares/doc'
11import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' 11import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models'
12import { uploadx } from '@uploadx/core' 12import { uploadx } from '@uploadx/core'
13import { VideoCreate, VideoState } from '../../../../shared' 13import { VideoCreate, VideoState } from '../../../../shared'
14import { HttpStatusCode } from '../../../../shared/core-utils/miscs' 14import { HttpStatusCode } from '../../../../shared/models'
15import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' 15import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
16import { retryTransactionWrapper } from '../../../helpers/database-utils' 16import { retryTransactionWrapper } from '../../../helpers/database-utils'
17import { createReqFiles } from '../../../helpers/express-utils' 17import { createReqFiles } from '../../../helpers/express-utils'
@@ -209,10 +209,12 @@ async function addVideo (options: {
209 }) 209 })
210 210
211 createTorrentFederate(video, videoFile) 211 createTorrentFederate(video, videoFile)
212 .then(() => {
213 if (video.state !== VideoState.TO_TRANSCODE) return
212 214
213 if (video.state === VideoState.TO_TRANSCODE) { 215 return addOptimizeOrMergeAudioJob(videoCreated, videoFile, user)
214 await addOptimizeOrMergeAudioJob(videoCreated, videoFile, user) 216 })
215 } 217 .catch(err => logger.error('Cannot add optimize/merge audio job for %s.', videoCreated.uuid, { err, ...lTags(videoCreated.uuid) }))
216 218
217 Hooks.runAction('action:api.video.uploaded', { video: videoCreated }) 219 Hooks.runAction('action:api.video.uploaded', { video: videoCreated })
218 220
@@ -240,7 +242,7 @@ async function buildNewFile (video: MVideo, videoPhysicalFile: express.VideoUplo
240 videoFile.resolution = (await getVideoFileResolution(videoPhysicalFile.path)).videoFileResolution 242 videoFile.resolution = (await getVideoFileResolution(videoPhysicalFile.path)).videoFileResolution
241 } 243 }
242 244
243 videoFile.filename = generateVideoFilename(video, false, videoFile.resolution, videoFile.extname) 245 videoFile.filename = generateWebTorrentVideoFilename(videoFile.resolution, videoFile.extname)
244 246
245 return videoFile 247 return videoFile
246} 248}
@@ -259,9 +261,9 @@ async function createTorrentAndSetInfoHashAsync (video: MVideo, fileArg: MVideoF
259 return refreshedFile.save() 261 return refreshedFile.save()
260} 262}
261 263
262function createTorrentFederate (video: MVideoFullLight, videoFile: MVideoFile): void { 264function createTorrentFederate (video: MVideoFullLight, videoFile: MVideoFile) {
263 // Create the torrent file in async way because it could be long 265 // Create the torrent file in async way because it could be long
264 createTorrentAndSetInfoHashAsync(video, videoFile) 266 return createTorrentAndSetInfoHashAsync(video, videoFile)
265 .catch(err => logger.error('Cannot create torrent file for video %s', video.url, { err, ...lTags(video.uuid) })) 267 .catch(err => logger.error('Cannot create torrent file for video %s', video.url, { err, ...lTags(video.uuid) }))
266 .then(() => VideoModel.loadAndPopulateAccountAndServerAndTags(video.id)) 268 .then(() => VideoModel.loadAndPopulateAccountAndServerAndTags(video.id))
267 .then(refreshedVideo => { 269 .then(refreshedVideo => {
diff --git a/server/controllers/api/videos/watching.ts b/server/controllers/api/videos/watching.ts
index 8b15525aa..05c75e543 100644
--- a/server/controllers/api/videos/watching.ts
+++ b/server/controllers/api/videos/watching.ts
@@ -1,5 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { UserWatchingVideo } from '../../../../shared' 2import { UserWatchingVideo } from '../../../../shared'
3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
3import { 4import {
4 asyncMiddleware, 5 asyncMiddleware,
5 asyncRetryTransactionMiddleware, 6 asyncRetryTransactionMiddleware,
@@ -8,7 +9,6 @@ import {
8 videoWatchingValidator 9 videoWatchingValidator
9} from '../../../middlewares' 10} from '../../../middlewares'
10import { UserVideoHistoryModel } from '../../../models/user/user-video-history' 11import { UserVideoHistoryModel } from '../../../models/user/user-video-history'
11import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
12 12
13const watchingRouter = express.Router() 13const watchingRouter = express.Router()
14 14
diff --git a/server/controllers/bots.ts b/server/controllers/bots.ts
index 9e92063d4..de0411608 100644
--- a/server/controllers/bots.ts
+++ b/server/controllers/bots.ts
@@ -1,20 +1,20 @@
1import * as express from 'express' 1import * as express from 'express'
2import { asyncMiddleware } from '../middlewares' 2import { truncate } from 'lodash'
3import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants'
4import { SitemapStream, streamToPromise } from 'sitemap' 3import { SitemapStream, streamToPromise } from 'sitemap'
4import { buildNSFWFilter } from '../helpers/express-utils'
5import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants'
6import { asyncMiddleware } from '../middlewares'
7import { cacheRoute } from '../middlewares/cache/cache'
8import { AccountModel } from '../models/account/account'
5import { VideoModel } from '../models/video/video' 9import { VideoModel } from '../models/video/video'
6import { VideoChannelModel } from '../models/video/video-channel' 10import { VideoChannelModel } from '../models/video/video-channel'
7import { AccountModel } from '../models/account/account'
8import { cacheRoute } from '../middlewares/cache'
9import { buildNSFWFilter } from '../helpers/express-utils'
10import { truncate } from 'lodash'
11 11
12const botsRouter = express.Router() 12const botsRouter = express.Router()
13 13
14// Special route that add OpenGraph and oEmbed tags 14// Special route that add OpenGraph and oEmbed tags
15// Do not use a template engine for a so little thing 15// Do not use a template engine for a so little thing
16botsRouter.use('/sitemap.xml', 16botsRouter.use('/sitemap.xml',
17 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.SITEMAP)), 17 cacheRoute(ROUTE_CACHE_LIFETIME.SITEMAP),
18 asyncMiddleware(getSitemap) 18 asyncMiddleware(getSitemap)
19) 19)
20 20
@@ -75,13 +75,13 @@ async function getSitemapLocalVideoUrls () {
75 }) 75 })
76 76
77 return data.map(v => ({ 77 return data.map(v => ({
78 url: WEBSERVER.URL + '/w/' + v.uuid, 78 url: WEBSERVER.URL + v.getWatchStaticPath(),
79 video: [ 79 video: [
80 { 80 {
81 title: v.name, 81 title: v.name,
82 // Sitemap description should be < 2000 characters 82 // Sitemap description should be < 2000 characters
83 description: truncate(v.description || v.name, { length: 2000, omission: '...' }), 83 description: truncate(v.description || v.name, { length: 2000, omission: '...' }),
84 player_loc: WEBSERVER.URL + '/videos/embed/' + v.uuid, 84 player_loc: WEBSERVER.URL + v.getEmbedStaticPath(),
85 thumbnail_loc: WEBSERVER.URL + v.getMiniatureStaticPath() 85 thumbnail_loc: WEBSERVER.URL + v.getMiniatureStaticPath()
86 } 86 }
87 ] 87 ]
diff --git a/server/controllers/client.ts b/server/controllers/client.ts
index eb1ee6cbd..ba3c54440 100644
--- a/server/controllers/client.ts
+++ b/server/controllers/client.ts
@@ -5,7 +5,7 @@ import { join } from 'path'
5import { logger } from '@server/helpers/logger' 5import { logger } from '@server/helpers/logger'
6import { CONFIG } from '@server/initializers/config' 6import { CONFIG } from '@server/initializers/config'
7import { Hooks } from '@server/lib/plugins/hooks' 7import { Hooks } from '@server/lib/plugins/hooks'
8import { HttpStatusCode } from '@shared/core-utils' 8import { HttpStatusCode } from '@shared/models'
9import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n' 9import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n'
10import { root } from '../helpers/core-utils' 10import { root } from '../helpers/core-utils'
11import { STATIC_MAX_AGE } from '../initializers/constants' 11import { STATIC_MAX_AGE } from '../initializers/constants'
diff --git a/server/controllers/download.ts b/server/controllers/download.ts
index 4293a32e2..ddacc1b68 100644
--- a/server/controllers/download.ts
+++ b/server/controllers/download.ts
@@ -5,8 +5,7 @@ import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache
5import { Hooks } from '@server/lib/plugins/hooks' 5import { Hooks } from '@server/lib/plugins/hooks'
6import { getVideoFilePath } from '@server/lib/video-paths' 6import { getVideoFilePath } from '@server/lib/video-paths'
7import { MStreamingPlaylist, MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' 7import { MStreamingPlaylist, MVideo, MVideoFile, MVideoFullLight } from '@server/types/models'
8import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 8import { HttpStatusCode, VideoStreamingPlaylistType } from '@shared/models'
9import { VideoStreamingPlaylistType } from '@shared/models'
10import { STATIC_DOWNLOAD_PATHS } from '../initializers/constants' 9import { STATIC_DOWNLOAD_PATHS } from '../initializers/constants'
11import { asyncMiddleware, videosDownloadValidator } from '../middlewares' 10import { asyncMiddleware, videosDownloadValidator } from '../middlewares'
12 11
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts
index 435b12193..9fa70a7c8 100644
--- a/server/controllers/feeds.ts
+++ b/server/controllers/feeds.ts
@@ -16,20 +16,20 @@ import {
16 videosSortValidator, 16 videosSortValidator,
17 videoSubscriptionFeedsValidator 17 videoSubscriptionFeedsValidator
18} from '../middlewares' 18} from '../middlewares'
19import { cacheRoute } from '../middlewares/cache' 19import { cacheRouteFactory } from '../middlewares/cache/cache'
20import { VideoModel } from '../models/video/video' 20import { VideoModel } from '../models/video/video'
21import { VideoCommentModel } from '../models/video/video-comment' 21import { VideoCommentModel } from '../models/video/video-comment'
22 22
23const feedsRouter = express.Router() 23const feedsRouter = express.Router()
24 24
25const cacheRoute = cacheRouteFactory({
26 headerBlacklist: [ 'Content-Type' ]
27})
28
25feedsRouter.get('/feeds/video-comments.:format', 29feedsRouter.get('/feeds/video-comments.:format',
26 feedsFormatValidator, 30 feedsFormatValidator,
27 setFeedFormatContentType, 31 setFeedFormatContentType,
28 asyncMiddleware(cacheRoute({ 32 cacheRoute(ROUTE_CACHE_LIFETIME.FEEDS),
29 headerBlacklist: [
30 'Content-Type'
31 ]
32 })(ROUTE_CACHE_LIFETIME.FEEDS)),
33 asyncMiddleware(videoFeedsValidator), 33 asyncMiddleware(videoFeedsValidator),
34 asyncMiddleware(videoCommentsFeedsValidator), 34 asyncMiddleware(videoCommentsFeedsValidator),
35 asyncMiddleware(generateVideoCommentsFeed) 35 asyncMiddleware(generateVideoCommentsFeed)
@@ -40,11 +40,7 @@ feedsRouter.get('/feeds/videos.:format',
40 setDefaultVideosSort, 40 setDefaultVideosSort,
41 feedsFormatValidator, 41 feedsFormatValidator,
42 setFeedFormatContentType, 42 setFeedFormatContentType,
43 asyncMiddleware(cacheRoute({ 43 cacheRoute(ROUTE_CACHE_LIFETIME.FEEDS),
44 headerBlacklist: [
45 'Content-Type'
46 ]
47 })(ROUTE_CACHE_LIFETIME.FEEDS)),
48 commonVideosFiltersValidator, 44 commonVideosFiltersValidator,
49 asyncMiddleware(videoFeedsValidator), 45 asyncMiddleware(videoFeedsValidator),
50 asyncMiddleware(generateVideoFeed) 46 asyncMiddleware(generateVideoFeed)
@@ -55,11 +51,7 @@ feedsRouter.get('/feeds/subscriptions.:format',
55 setDefaultVideosSort, 51 setDefaultVideosSort,
56 feedsFormatValidator, 52 feedsFormatValidator,
57 setFeedFormatContentType, 53 setFeedFormatContentType,
58 asyncMiddleware(cacheRoute({ 54 cacheRoute(ROUTE_CACHE_LIFETIME.FEEDS),
59 headerBlacklist: [
60 'Content-Type'
61 ]
62 })(ROUTE_CACHE_LIFETIME.FEEDS)),
63 commonVideosFiltersValidator, 55 commonVideosFiltersValidator,
64 asyncMiddleware(videoSubscriptionFeedsValidator), 56 asyncMiddleware(videoSubscriptionFeedsValidator),
65 asyncMiddleware(generateVideoFeedForSubscriptions) 57 asyncMiddleware(generateVideoFeedForSubscriptions)
@@ -294,7 +286,7 @@ function addVideosToFeed (feed, videos: VideoModel[]) {
294 feed.addItem({ 286 feed.addItem({
295 title: video.name, 287 title: video.name,
296 id: video.url, 288 id: video.url,
297 link: WEBSERVER.URL + '/w/' + video.uuid, 289 link: WEBSERVER.URL + video.getWatchStaticPath(),
298 description: video.getTruncatedDescription(), 290 description: video.getTruncatedDescription(),
299 content: video.description, 291 content: video.description,
300 author: [ 292 author: [
diff --git a/server/controllers/lazy-static.ts b/server/controllers/lazy-static.ts
index 9a7dacba0..632e4dcd8 100644
--- a/server/controllers/lazy-static.ts
+++ b/server/controllers/lazy-static.ts
@@ -1,7 +1,7 @@
1import * as cors from 'cors' 1import * as cors from 'cors'
2import * as express from 'express' 2import * as express from 'express'
3import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache' 3import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache'
4import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
5import { logger } from '../helpers/logger' 5import { logger } from '../helpers/logger'
6import { LAZY_STATIC_PATHS, STATIC_MAX_AGE } from '../initializers/constants' 6import { LAZY_STATIC_PATHS, STATIC_MAX_AGE } from '../initializers/constants'
7import { VideosCaptionCache, VideosPreviewCache } from '../lib/files-cache' 7import { VideosCaptionCache, VideosPreviewCache } from '../lib/files-cache'
diff --git a/server/controllers/live.ts b/server/controllers/live.ts
index f2686fb23..95d5c0135 100644
--- a/server/controllers/live.ts
+++ b/server/controllers/live.ts
@@ -2,7 +2,7 @@ import * as cors from 'cors'
2import * as express from 'express' 2import * as express from 'express'
3import { mapToJSON } from '@server/helpers/core-utils' 3import { mapToJSON } from '@server/helpers/core-utils'
4import { LiveSegmentShaStore } from '@server/lib/live' 4import { LiveSegmentShaStore } from '@server/lib/live'
5import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 5import { HttpStatusCode } from '@shared/models'
6 6
7const liveRouter = express.Router() 7const liveRouter = express.Router()
8 8
diff --git a/server/controllers/plugins.ts b/server/controllers/plugins.ts
index 7213e3f15..11ab3f10a 100644
--- a/server/controllers/plugins.ts
+++ b/server/controllers/plugins.ts
@@ -3,7 +3,7 @@ import { join } from 'path'
3import { logger } from '@server/helpers/logger' 3import { logger } from '@server/helpers/logger'
4import { optionalAuthenticate } from '@server/middlewares/auth' 4import { optionalAuthenticate } from '@server/middlewares/auth'
5import { getCompleteLocale, is18nLocale } from '../../shared/core-utils/i18n' 5import { getCompleteLocale, is18nLocale } from '../../shared/core-utils/i18n'
6import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
7import { PluginType } from '../../shared/models/plugins/plugin.type' 7import { PluginType } from '../../shared/models/plugins/plugin.type'
8import { isTestInstance } from '../helpers/core-utils' 8import { isTestInstance } from '../helpers/core-utils'
9import { PLUGIN_GLOBAL_CSS_PATH } from '../initializers/constants' 9import { PLUGIN_GLOBAL_CSS_PATH } from '../initializers/constants'
diff --git a/server/controllers/static.ts b/server/controllers/static.ts
index 35e024dda..912d7e36c 100644
--- a/server/controllers/static.ts
+++ b/server/controllers/static.ts
@@ -3,7 +3,7 @@ import * as express from 'express'
3import { join } from 'path' 3import { join } from 'path'
4import { serveIndexHTML } from '@server/lib/client-html' 4import { serveIndexHTML } from '@server/lib/client-html'
5import { ServerConfigManager } from '@server/lib/server-config-manager' 5import { ServerConfigManager } from '@server/lib/server-config-manager'
6import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '@shared/models'
7import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo/nodeinfo.model' 7import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo/nodeinfo.model'
8import { root } from '../helpers/core-utils' 8import { root } from '../helpers/core-utils'
9import { CONFIG, isEmailEnabled } from '../initializers/config' 9import { CONFIG, isEmailEnabled } from '../initializers/config'
@@ -19,7 +19,7 @@ import {
19} from '../initializers/constants' 19} from '../initializers/constants'
20import { getThemeOrDefault } from '../lib/plugins/theme-utils' 20import { getThemeOrDefault } from '../lib/plugins/theme-utils'
21import { asyncMiddleware } from '../middlewares' 21import { asyncMiddleware } from '../middlewares'
22import { cacheRoute } from '../middlewares/cache' 22import { cacheRoute } from '../middlewares/cache/cache'
23import { UserModel } from '../models/user/user' 23import { UserModel } from '../models/user/user'
24import { VideoModel } from '../models/video/video' 24import { VideoModel } from '../models/video/video'
25import { VideoCommentModel } from '../models/video/video-comment' 25import { VideoCommentModel } from '../models/video/video-comment'
@@ -66,7 +66,7 @@ staticRouter.use(
66 66
67// robots.txt service 67// robots.txt service
68staticRouter.get('/robots.txt', 68staticRouter.get('/robots.txt',
69 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.ROBOTS)), 69 cacheRoute(ROUTE_CACHE_LIFETIME.ROBOTS),
70 (_, res: express.Response) => { 70 (_, res: express.Response) => {
71 res.type('text/plain') 71 res.type('text/plain')
72 return res.send(CONFIG.INSTANCE.ROBOTS) 72 return res.send(CONFIG.INSTANCE.ROBOTS)
@@ -86,7 +86,7 @@ staticRouter.get('/security.txt',
86) 86)
87 87
88staticRouter.get('/.well-known/security.txt', 88staticRouter.get('/.well-known/security.txt',
89 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.SECURITYTXT)), 89 cacheRoute(ROUTE_CACHE_LIFETIME.SECURITYTXT),
90 (_, res: express.Response) => { 90 (_, res: express.Response) => {
91 res.type('text/plain') 91 res.type('text/plain')
92 return res.send(CONFIG.INSTANCE.SECURITYTXT + CONFIG.INSTANCE.SECURITYTXT_CONTACT) 92 return res.send(CONFIG.INSTANCE.SECURITYTXT + CONFIG.INSTANCE.SECURITYTXT_CONTACT)
@@ -95,7 +95,7 @@ staticRouter.get('/.well-known/security.txt',
95 95
96// nodeinfo service 96// nodeinfo service
97staticRouter.use('/.well-known/nodeinfo', 97staticRouter.use('/.well-known/nodeinfo',
98 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.NODEINFO)), 98 cacheRoute(ROUTE_CACHE_LIFETIME.NODEINFO),
99 (_, res: express.Response) => { 99 (_, res: express.Response) => {
100 return res.json({ 100 return res.json({
101 links: [ 101 links: [
@@ -108,13 +108,13 @@ staticRouter.use('/.well-known/nodeinfo',
108 } 108 }
109) 109)
110staticRouter.use('/nodeinfo/:version.json', 110staticRouter.use('/nodeinfo/:version.json',
111 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.NODEINFO)), 111 cacheRoute(ROUTE_CACHE_LIFETIME.NODEINFO),
112 asyncMiddleware(generateNodeinfo) 112 asyncMiddleware(generateNodeinfo)
113) 113)
114 114
115// dnt-policy.txt service (see https://www.eff.org/dnt-policy) 115// dnt-policy.txt service (see https://www.eff.org/dnt-policy)
116staticRouter.use('/.well-known/dnt-policy.txt', 116staticRouter.use('/.well-known/dnt-policy.txt',
117 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.DNT_POLICY)), 117 cacheRoute(ROUTE_CACHE_LIFETIME.DNT_POLICY),
118 (_, res: express.Response) => { 118 (_, res: express.Response) => {
119 res.type('text/plain') 119 res.type('text/plain')
120 120
diff --git a/server/helpers/custom-validators/follows.ts b/server/helpers/custom-validators/follows.ts
index fbef7ad87..8f65552c3 100644
--- a/server/helpers/custom-validators/follows.ts
+++ b/server/helpers/custom-validators/follows.ts
@@ -1,4 +1,4 @@
1import { exists } from './misc' 1import { exists, isArray } from './misc'
2import { FollowState } from '@shared/models' 2import { FollowState } from '@shared/models'
3 3
4function isFollowStateValid (value: FollowState) { 4function isFollowStateValid (value: FollowState) {
@@ -7,8 +7,24 @@ function isFollowStateValid (value: FollowState) {
7 return value === 'pending' || value === 'accepted' 7 return value === 'pending' || value === 'accepted'
8} 8}
9 9
10function isRemoteHandleValid (value: string) {
11 if (!exists(value)) return false
12 if (typeof value !== 'string') return false
13
14 return value.includes('@')
15}
16
17function isEachUniqueHandleValid (handles: string[]) {
18 return isArray(handles) &&
19 handles.every(handle => {
20 return isRemoteHandleValid(handle) && handles.indexOf(handle) === handles.lastIndexOf(handle)
21 })
22}
23
10// --------------------------------------------------------------------------- 24// ---------------------------------------------------------------------------
11 25
12export { 26export {
13 isFollowStateValid 27 isFollowStateValid,
28 isRemoteHandleValid,
29 isEachUniqueHandleValid
14} 30}
diff --git a/server/helpers/custom-validators/misc.ts b/server/helpers/custom-validators/misc.ts
index 528bfcfb8..c19a3e5eb 100644
--- a/server/helpers/custom-validators/misc.ts
+++ b/server/helpers/custom-validators/misc.ts
@@ -23,6 +23,10 @@ function isNotEmptyIntArray (value: any) {
23 return Array.isArray(value) && value.every(v => validator.isInt('' + v)) && value.length !== 0 23 return Array.isArray(value) && value.every(v => validator.isInt('' + v)) && value.length !== 0
24} 24}
25 25
26function isNotEmptyStringArray (value: any) {
27 return Array.isArray(value) && value.every(v => typeof v === 'string' && v.length !== 0) && value.length !== 0
28}
29
26function isArrayOf (value: any, validator: (value: any) => boolean) { 30function isArrayOf (value: any, validator: (value: any) => boolean) {
27 return isArray(value) && value.every(v => validator(v)) 31 return isArray(value) && value.every(v => validator(v))
28} 32}
@@ -39,6 +43,10 @@ function isUUIDValid (value: string) {
39 return exists(value) && validator.isUUID('' + value, 4) 43 return exists(value) && validator.isUUID('' + value, 4)
40} 44}
41 45
46function areUUIDsValid (values: string[]) {
47 return isArray(values) && values.every(v => isUUIDValid(v))
48}
49
42function isIdOrUUIDValid (value: string) { 50function isIdOrUUIDValid (value: string) {
43 return isIdValid(value) || isUUIDValid(value) 51 return isIdValid(value) || isUUIDValid(value)
44} 52}
@@ -132,6 +140,10 @@ function toCompleteUUID (value: string) {
132 return value 140 return value
133} 141}
134 142
143function toCompleteUUIDs (values: string[]) {
144 return values.map(v => toCompleteUUID(v))
145}
146
135function toIntOrNull (value: string) { 147function toIntOrNull (value: string) {
136 const v = toValueOrNull(value) 148 const v = toValueOrNull(value)
137 149
@@ -179,7 +191,9 @@ export {
179 isIntOrNull, 191 isIntOrNull,
180 isIdValid, 192 isIdValid,
181 isSafePath, 193 isSafePath,
194 isNotEmptyStringArray,
182 isUUIDValid, 195 isUUIDValid,
196 toCompleteUUIDs,
183 toCompleteUUID, 197 toCompleteUUID,
184 isIdOrUUIDValid, 198 isIdOrUUIDValid,
185 isDateValid, 199 isDateValid,
@@ -187,6 +201,7 @@ export {
187 toBooleanOrNull, 201 toBooleanOrNull,
188 isBooleanValid, 202 isBooleanValid,
189 toIntOrNull, 203 toIntOrNull,
204 areUUIDsValid,
190 toArray, 205 toArray,
191 toIntArray, 206 toIntArray,
192 isFileFieldValid, 207 isFileFieldValid,
diff --git a/server/helpers/custom-validators/servers.ts b/server/helpers/custom-validators/servers.ts
index adf1ea497..c0f8b6aeb 100644
--- a/server/helpers/custom-validators/servers.ts
+++ b/server/helpers/custom-validators/servers.ts
@@ -19,7 +19,6 @@ function isHostValid (host: string) {
19 19
20function isEachUniqueHostValid (hosts: string[]) { 20function isEachUniqueHostValid (hosts: string[]) {
21 return isArray(hosts) && 21 return isArray(hosts) &&
22 hosts.length !== 0 &&
23 hosts.every(host => { 22 hosts.every(host => {
24 return isHostValid(host) && hosts.indexOf(host) === hosts.lastIndexOf(host) 23 return isHostValid(host) && hosts.indexOf(host) === hosts.lastIndexOf(host)
25 }) 24 })
diff --git a/server/helpers/custom-validators/video-ownership.ts b/server/helpers/custom-validators/video-ownership.ts
index 0e1c63bad..cf15b385a 100644
--- a/server/helpers/custom-validators/video-ownership.ts
+++ b/server/helpers/custom-validators/video-ownership.ts
@@ -1,7 +1,7 @@
1import { Response } from 'express' 1import { Response } from 'express'
2import { MUserId } from '@server/types/models' 2import { MUserId } from '@server/types/models'
3import { MVideoChangeOwnershipFull } from '@server/types/models/video/video-change-ownership' 3import { MVideoChangeOwnershipFull } from '@server/types/models/video/video-change-ownership'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
5 5
6function checkUserCanTerminateOwnershipChange (user: MUserId, videoChangeOwnership: MVideoChangeOwnershipFull, res: Response) { 6function checkUserCanTerminateOwnershipChange (user: MUserId, videoChangeOwnership: MVideoChangeOwnershipFull, res: Response) {
7 if (videoChangeOwnership.NextOwner.userId === user.id) { 7 if (videoChangeOwnership.NextOwner.userId === user.id) {
diff --git a/server/helpers/database-utils.ts b/server/helpers/database-utils.ts
index b5dc70c17..ec35295df 100644
--- a/server/helpers/database-utils.ts
+++ b/server/helpers/database-utils.ts
@@ -1,12 +1,12 @@
1import * as retry from 'async/retry' 1import * as retry from 'async/retry'
2import * as Bluebird from 'bluebird' 2import * as Bluebird from 'bluebird'
3import { QueryTypes, Transaction } from 'sequelize' 3import { Transaction } from 'sequelize'
4import { Model } from 'sequelize-typescript' 4import { Model } from 'sequelize-typescript'
5import { sequelizeTypescript } from '@server/initializers/database' 5import { sequelizeTypescript } from '@server/initializers/database'
6import { logger } from './logger' 6import { logger } from './logger'
7 7
8function retryTransactionWrapper <T, A, B, C, D> ( 8function retryTransactionWrapper <T, A, B, C, D> (
9 functionToRetry: (arg1: A, arg2: B, arg3: C, arg4: D) => Promise<T> | Bluebird<T>, 9 functionToRetry: (arg1: A, arg2: B, arg3: C, arg4: D) => Promise<T>,
10 arg1: A, 10 arg1: A,
11 arg2: B, 11 arg2: B,
12 arg3: C, 12 arg3: C,
@@ -14,20 +14,20 @@ function retryTransactionWrapper <T, A, B, C, D> (
14): Promise<T> 14): Promise<T>
15 15
16function retryTransactionWrapper <T, A, B, C> ( 16function retryTransactionWrapper <T, A, B, C> (
17 functionToRetry: (arg1: A, arg2: B, arg3: C) => Promise<T> | Bluebird<T>, 17 functionToRetry: (arg1: A, arg2: B, arg3: C) => Promise<T>,
18 arg1: A, 18 arg1: A,
19 arg2: B, 19 arg2: B,
20 arg3: C 20 arg3: C
21): Promise<T> 21): Promise<T>
22 22
23function retryTransactionWrapper <T, A, B> ( 23function retryTransactionWrapper <T, A, B> (
24 functionToRetry: (arg1: A, arg2: B) => Promise<T> | Bluebird<T>, 24 functionToRetry: (arg1: A, arg2: B) => Promise<T>,
25 arg1: A, 25 arg1: A,
26 arg2: B 26 arg2: B
27): Promise<T> 27): Promise<T>
28 28
29function retryTransactionWrapper <T, A> ( 29function retryTransactionWrapper <T, A> (
30 functionToRetry: (arg1: A) => Promise<T> | Bluebird<T>, 30 functionToRetry: (arg1: A) => Promise<T>,
31 arg1: A 31 arg1: A
32): Promise<T> 32): Promise<T>
33 33
@@ -36,7 +36,7 @@ function retryTransactionWrapper <T> (
36): Promise<T> 36): Promise<T>
37 37
38function retryTransactionWrapper <T> ( 38function retryTransactionWrapper <T> (
39 functionToRetry: (...args: any[]) => Promise<T> | Bluebird<T>, 39 functionToRetry: (...args: any[]) => Promise<T>,
40 ...args: any[] 40 ...args: any[]
41): Promise<T> { 41): Promise<T> {
42 return transactionRetryer<T>(callback => { 42 return transactionRetryer<T>(callback => {
@@ -84,25 +84,15 @@ function resetSequelizeInstance (instance: Model<any>, savedFields: object) {
84 }) 84 })
85} 85}
86 86
87function deleteNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean } & Pick<Model, 'destroy'>> ( 87function filterNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean }> (
88 fromDatabase: T[], 88 fromDatabase: T[],
89 newModels: T[], 89 newModels: T[]
90 t: Transaction
91) { 90) {
92 return fromDatabase.filter(f => !newModels.find(newModel => newModel.hasSameUniqueKeysThan(f))) 91 return fromDatabase.filter(f => !newModels.find(newModel => newModel.hasSameUniqueKeysThan(f)))
93 .map(f => f.destroy({ transaction: t }))
94} 92}
95 93
96// Sequelize always skip the update if we only update updatedAt field 94function deleteAllModels <T extends Pick<Model, 'destroy'>> (models: T[], transaction: Transaction) {
97function setAsUpdated (table: string, id: number, transaction?: Transaction) { 95 return Promise.all(models.map(f => f.destroy({ transaction })))
98 return sequelizeTypescript.query(
99 `UPDATE "${table}" SET "updatedAt" = :updatedAt WHERE id = :id`,
100 {
101 replacements: { table, id, updatedAt: new Date() },
102 type: QueryTypes.UPDATE,
103 transaction
104 }
105 )
106} 96}
107 97
108// --------------------------------------------------------------------------- 98// ---------------------------------------------------------------------------
@@ -127,7 +117,7 @@ export {
127 transactionRetryer, 117 transactionRetryer,
128 updateInstanceWithAnother, 118 updateInstanceWithAnother,
129 afterCommitIfTransaction, 119 afterCommitIfTransaction,
130 deleteNonExistingModels, 120 filterNonExistingModels,
131 setAsUpdated, 121 deleteAllModels,
132 runInReadCommittedTransaction 122 runInReadCommittedTransaction
133} 123}
diff --git a/server/helpers/express-utils.ts b/server/helpers/express-utils.ts
index 0ff113274..c299b70f1 100644
--- a/server/helpers/express-utils.ts
+++ b/server/helpers/express-utils.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import * as multer from 'multer' 2import * as multer from 'multer'
3import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
4import { CONFIG } from '../initializers/config' 4import { CONFIG } from '../initializers/config'
5import { REMOTE_SCHEME } from '../initializers/constants' 5import { REMOTE_SCHEME } from '../initializers/constants'
6import { getLowercaseExtension } from './core-utils' 6import { getLowercaseExtension } from './core-utils'
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts
index 6f5a71b4a..61c8a6db2 100644
--- a/server/helpers/ffmpeg-utils.ts
+++ b/server/helpers/ffmpeg-utils.ts
@@ -212,14 +212,17 @@ async function transcode (options: TranscodeOptions) {
212 212
213async function getLiveTranscodingCommand (options: { 213async function getLiveTranscodingCommand (options: {
214 rtmpUrl: string 214 rtmpUrl: string
215
215 outPath: string 216 outPath: string
217 masterPlaylistName: string
218
216 resolutions: number[] 219 resolutions: number[]
217 fps: number 220 fps: number
218 221
219 availableEncoders: AvailableEncoders 222 availableEncoders: AvailableEncoders
220 profile: string 223 profile: string
221}) { 224}) {
222 const { rtmpUrl, outPath, resolutions, fps, availableEncoders, profile } = options 225 const { rtmpUrl, outPath, resolutions, fps, availableEncoders, profile, masterPlaylistName } = options
223 const input = rtmpUrl 226 const input = rtmpUrl
224 227
225 const command = getFFmpeg(input, 'live') 228 const command = getFFmpeg(input, 'live')
@@ -301,14 +304,14 @@ async function getLiveTranscodingCommand (options: {
301 304
302 command.complexFilter(complexFilter) 305 command.complexFilter(complexFilter)
303 306
304 addDefaultLiveHLSParams(command, outPath) 307 addDefaultLiveHLSParams(command, outPath, masterPlaylistName)
305 308
306 command.outputOption('-var_stream_map', varStreamMap.join(' ')) 309 command.outputOption('-var_stream_map', varStreamMap.join(' '))
307 310
308 return command 311 return command
309} 312}
310 313
311function getLiveMuxingCommand (rtmpUrl: string, outPath: string) { 314function getLiveMuxingCommand (rtmpUrl: string, outPath: string, masterPlaylistName: string) {
312 const command = getFFmpeg(rtmpUrl, 'live') 315 const command = getFFmpeg(rtmpUrl, 'live')
313 316
314 command.outputOption('-c:v copy') 317 command.outputOption('-c:v copy')
@@ -316,7 +319,7 @@ function getLiveMuxingCommand (rtmpUrl: string, outPath: string) {
316 command.outputOption('-map 0:a?') 319 command.outputOption('-map 0:a?')
317 command.outputOption('-map 0:v?') 320 command.outputOption('-map 0:v?')
318 321
319 addDefaultLiveHLSParams(command, outPath) 322 addDefaultLiveHLSParams(command, outPath, masterPlaylistName)
320 323
321 return command 324 return command
322} 325}
@@ -371,12 +374,12 @@ function addDefaultEncoderParams (options: {
371 } 374 }
372} 375}
373 376
374function addDefaultLiveHLSParams (command: ffmpeg.FfmpegCommand, outPath: string) { 377function addDefaultLiveHLSParams (command: ffmpeg.FfmpegCommand, outPath: string, masterPlaylistName: string) {
375 command.outputOption('-hls_time ' + VIDEO_LIVE.SEGMENT_TIME_SECONDS) 378 command.outputOption('-hls_time ' + VIDEO_LIVE.SEGMENT_TIME_SECONDS)
376 command.outputOption('-hls_list_size ' + VIDEO_LIVE.SEGMENTS_LIST_SIZE) 379 command.outputOption('-hls_list_size ' + VIDEO_LIVE.SEGMENTS_LIST_SIZE)
377 command.outputOption('-hls_flags delete_segments+independent_segments') 380 command.outputOption('-hls_flags delete_segments+independent_segments')
378 command.outputOption(`-hls_segment_filename ${join(outPath, '%v-%06d.ts')}`) 381 command.outputOption(`-hls_segment_filename ${join(outPath, '%v-%06d.ts')}`)
379 command.outputOption('-master_pl_name master.m3u8') 382 command.outputOption('-master_pl_name ' + masterPlaylistName)
380 command.outputOption(`-f hls`) 383 command.outputOption(`-f hls`)
381 384
382 command.output(join(outPath, '%v.m3u8')) 385 command.output(join(outPath, '%v.m3u8'))
@@ -700,6 +703,10 @@ async function runCommand (options: {
700 const { command, silent = false, job } = options 703 const { command, silent = false, job } = options
701 704
702 return new Promise<void>((res, rej) => { 705 return new Promise<void>((res, rej) => {
706 let shellCommand: string
707
708 command.on('start', cmdline => { shellCommand = cmdline })
709
703 command.on('error', (err, stdout, stderr) => { 710 command.on('error', (err, stdout, stderr) => {
704 if (silent !== true) logger.error('Error in ffmpeg.', { stdout, stderr }) 711 if (silent !== true) logger.error('Error in ffmpeg.', { stdout, stderr })
705 712
@@ -707,7 +714,7 @@ async function runCommand (options: {
707 }) 714 })
708 715
709 command.on('end', (stdout, stderr) => { 716 command.on('end', (stdout, stderr) => {
710 logger.debug('FFmpeg command ended.', { stdout, stderr }) 717 logger.debug('FFmpeg command ended.', { stdout, stderr, shellCommand })
711 718
712 res() 719 res()
713 }) 720 })
diff --git a/server/helpers/logger.ts b/server/helpers/logger.ts
index 29e06860d..20c3c3edb 100644
--- a/server/helpers/logger.ts
+++ b/server/helpers/logger.ts
@@ -1,5 +1,5 @@
1// Thanks http://tostring.it/2014/06/23/advanced-logging-with-nodejs/ 1// Thanks http://tostring.it/2014/06/23/advanced-logging-with-nodejs/
2import { mkdirpSync } from 'fs-extra' 2import { mkdirpSync, stat } from 'fs-extra'
3import { omit } from 'lodash' 3import { omit } from 'lodash'
4import * as path from 'path' 4import * as path from 'path'
5import { format as sqlFormat } from 'sql-formatter' 5import { format as sqlFormat } from 'sql-formatter'
@@ -158,6 +158,26 @@ function loggerTagsFactory (...defaultTags: string[]): LoggerTagsFn {
158 } 158 }
159} 159}
160 160
161async function mtimeSortFilesDesc (files: string[], basePath: string) {
162 const promises = []
163 const out: { file: string, mtime: number }[] = []
164
165 for (const file of files) {
166 const p = stat(basePath + '/' + file)
167 .then(stats => {
168 if (stats.isFile()) out.push({ file, mtime: stats.mtime.getTime() })
169 })
170
171 promises.push(p)
172 }
173
174 await Promise.all(promises)
175
176 out.sort((a, b) => b.mtime - a.mtime)
177
178 return out
179}
180
161// --------------------------------------------------------------------------- 181// ---------------------------------------------------------------------------
162 182
163export { 183export {
@@ -168,6 +188,7 @@ export {
168 labelFormatter, 188 labelFormatter,
169 consoleLoggerFormat, 189 consoleLoggerFormat,
170 jsonLoggerFormat, 190 jsonLoggerFormat,
191 mtimeSortFilesDesc,
171 logger, 192 logger,
172 loggerTagsFactory, 193 loggerTagsFactory,
173 bunyanLogger 194 bunyanLogger
diff --git a/server/helpers/query.ts b/server/helpers/query.ts
new file mode 100644
index 000000000..e711b15f2
--- /dev/null
+++ b/server/helpers/query.ts
@@ -0,0 +1,74 @@
1import { pick } from '@shared/core-utils'
2import {
3 VideoChannelsSearchQueryAfterSanitize,
4 VideoPlaylistsSearchQueryAfterSanitize,
5 VideosCommonQueryAfterSanitize,
6 VideosSearchQueryAfterSanitize
7} from '@shared/models'
8
9function pickCommonVideoQuery (query: VideosCommonQueryAfterSanitize) {
10 return pick(query, [
11 'start',
12 'count',
13 'sort',
14 'nsfw',
15 'isLive',
16 'categoryOneOf',
17 'licenceOneOf',
18 'languageOneOf',
19 'tagsOneOf',
20 'tagsAllOf',
21 'filter',
22 'skipCount'
23 ])
24}
25
26function pickSearchVideoQuery (query: VideosSearchQueryAfterSanitize) {
27 return {
28 ...pickCommonVideoQuery(query),
29
30 ...pick(query, [
31 'searchTarget',
32 'search',
33 'host',
34 'startDate',
35 'endDate',
36 'originallyPublishedStartDate',
37 'originallyPublishedEndDate',
38 'durationMin',
39 'durationMax',
40 'uuids'
41 ])
42 }
43}
44
45function pickSearchChannelQuery (query: VideoChannelsSearchQueryAfterSanitize) {
46 return pick(query, [
47 'searchTarget',
48 'search',
49 'start',
50 'count',
51 'sort',
52 'host',
53 'handles'
54 ])
55}
56
57function pickSearchPlaylistQuery (query: VideoPlaylistsSearchQueryAfterSanitize) {
58 return pick(query, [
59 'searchTarget',
60 'search',
61 'start',
62 'count',
63 'sort',
64 'host',
65 'uuids'
66 ])
67}
68
69export {
70 pickCommonVideoQuery,
71 pickSearchVideoQuery,
72 pickSearchPlaylistQuery,
73 pickSearchChannelQuery
74}
diff --git a/server/helpers/webtorrent.ts b/server/helpers/webtorrent.ts
index d8220ba9c..ecf63e93e 100644
--- a/server/helpers/webtorrent.ts
+++ b/server/helpers/webtorrent.ts
@@ -103,6 +103,11 @@ async function createTorrentAndSetInfoHash (
103 103
104 await writeFile(torrentPath, torrent) 104 await writeFile(torrentPath, torrent)
105 105
106 // Remove old torrent file if it existed
107 if (videoFile.hasTorrent()) {
108 await remove(join(CONFIG.STORAGE.TORRENTS_DIR, videoFile.torrentFilename))
109 }
110
106 const parsedTorrent = parseTorrent(torrent) 111 const parsedTorrent = parseTorrent(torrent)
107 videoFile.infoHash = parsedTorrent.infoHash 112 videoFile.infoHash = parsedTorrent.infoHash
108 videoFile.torrentFilename = torrentFilename 113 videoFile.torrentFilename = torrentFilename
diff --git a/server/helpers/youtube-dl.ts b/server/helpers/youtube-dl.ts
index fdd361390..3c80e7d41 100644
--- a/server/helpers/youtube-dl.ts
+++ b/server/helpers/youtube-dl.ts
@@ -3,7 +3,7 @@ import { ensureDir, move, pathExists, remove, writeFile } from 'fs-extra'
3import got from 'got' 3import got from 'got'
4import { join } from 'path' 4import { join } from 'path'
5import { CONFIG } from '@server/initializers/config' 5import { CONFIG } from '@server/initializers/config'
6import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
7import { VideoResolution } from '../../shared/models/videos' 7import { VideoResolution } from '../../shared/models/videos'
8import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '../initializers/constants' 8import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '../initializers/constants'
9import { peertubeTruncate, pipelinePromise, root } from './core-utils' 9import { peertubeTruncate, pipelinePromise, root } from './core-utils'
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index ab59320eb..5f121d9a4 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -2,7 +2,7 @@ import { CronRepeatOptions, EveryRepeatOptions } from 'bull'
2import { randomBytes } from 'crypto' 2import { randomBytes } from 'crypto'
3import { invert } from 'lodash' 3import { invert } from 'lodash'
4import { join } from 'path' 4import { join } from 'path'
5import { randomInt } from '../../shared/core-utils/miscs/miscs' 5import { randomInt } from '../../shared/core-utils/common/miscs'
6import { 6import {
7 AbuseState, 7 AbuseState,
8 JobType, 8 JobType,
@@ -24,7 +24,7 @@ import { CONFIG, registerConfigChangedHandler } from './config'
24 24
25// --------------------------------------------------------------------------- 25// ---------------------------------------------------------------------------
26 26
27const LAST_MIGRATION_VERSION = 650 27const LAST_MIGRATION_VERSION = 655
28 28
29// --------------------------------------------------------------------------- 29// ---------------------------------------------------------------------------
30 30
diff --git a/server/initializers/migrations/0655-streaming-playlist-filenames.ts b/server/initializers/migrations/0655-streaming-playlist-filenames.ts
new file mode 100644
index 000000000..9172a22c4
--- /dev/null
+++ b/server/initializers/migrations/0655-streaming-playlist-filenames.ts
@@ -0,0 +1,66 @@
1import * as Sequelize from 'sequelize'
2
3async function up (utils: {
4 transaction: Sequelize.Transaction
5 queryInterface: Sequelize.QueryInterface
6 sequelize: Sequelize.Sequelize
7 db: any
8}): Promise<void> {
9 {
10 for (const column of [ 'playlistUrl', 'segmentsSha256Url' ]) {
11 const data = {
12 type: Sequelize.STRING,
13 allowNull: true,
14 defaultValue: null
15 }
16
17 await utils.queryInterface.changeColumn('videoStreamingPlaylist', column, data)
18 }
19 }
20
21 {
22 await utils.sequelize.query(
23 `UPDATE "videoStreamingPlaylist" SET "playlistUrl" = NULL, "segmentsSha256Url" = NULL ` +
24 `WHERE "videoId" IN (SELECT id FROM video WHERE remote IS FALSE)`
25 )
26 }
27
28 {
29 for (const column of [ 'playlistFilename', 'segmentsSha256Filename' ]) {
30 const data = {
31 type: Sequelize.STRING,
32 allowNull: true,
33 defaultValue: null
34 }
35
36 await utils.queryInterface.addColumn('videoStreamingPlaylist', column, data)
37 }
38 }
39
40 {
41 await utils.sequelize.query(
42 `UPDATE "videoStreamingPlaylist" SET "playlistFilename" = 'master.m3u8', "segmentsSha256Filename" = 'segments-sha256.json'`
43 )
44 }
45
46 {
47 for (const column of [ 'playlistFilename', 'segmentsSha256Filename' ]) {
48 const data = {
49 type: Sequelize.STRING,
50 allowNull: false,
51 defaultValue: null
52 }
53
54 await utils.queryInterface.changeColumn('videoStreamingPlaylist', column, data)
55 }
56 }
57}
58
59function down (options) {
60 throw new Error('Not implemented.')
61}
62
63export {
64 up,
65 down
66}
diff --git a/server/lib/activitypub/actors/refresh.ts b/server/lib/activitypub/actors/refresh.ts
index b2fe3932f..0acaa9f62 100644
--- a/server/lib/activitypub/actors/refresh.ts
+++ b/server/lib/activitypub/actors/refresh.ts
@@ -4,7 +4,7 @@ import { PeerTubeRequestError } from '@server/helpers/requests'
4import { ActorLoadByUrlType } from '@server/lib/model-loaders' 4import { ActorLoadByUrlType } from '@server/lib/model-loaders'
5import { ActorModel } from '@server/models/actor/actor' 5import { ActorModel } from '@server/models/actor/actor'
6import { MActorAccountChannelId, MActorFull } from '@server/types/models' 6import { MActorAccountChannelId, MActorFull } from '@server/types/models'
7import { HttpStatusCode } from '@shared/core-utils' 7import { HttpStatusCode } from '@shared/models'
8import { fetchRemoteActor } from './shared' 8import { fetchRemoteActor } from './shared'
9import { APActorUpdater } from './updater' 9import { APActorUpdater } from './updater'
10import { getUrlFromWebfinger } from './webfinger' 10import { getUrlFromWebfinger } from './webfinger'
diff --git a/server/lib/activitypub/crawl.ts b/server/lib/activitypub/crawl.ts
index cd117f571..28ff5225a 100644
--- a/server/lib/activitypub/crawl.ts
+++ b/server/lib/activitypub/crawl.ts
@@ -1,3 +1,4 @@
1import { retryTransactionWrapper } from '@server/helpers/database-utils'
1import * as Bluebird from 'bluebird' 2import * as Bluebird from 'bluebird'
2import { URL } from 'url' 3import { URL } from 'url'
3import { ActivityPubOrderedCollection } from '../../../shared/models/activitypub' 4import { ActivityPubOrderedCollection } from '../../../shared/models/activitypub'
@@ -51,7 +52,7 @@ async function crawlCollectionPage <T> (argUrl: string, handler: HandlerFunction
51 } 52 }
52 } 53 }
53 54
54 if (cleaner) await cleaner(startDate) 55 if (cleaner) await retryTransactionWrapper(cleaner, startDate)
55} 56}
56 57
57export { 58export {
diff --git a/server/lib/activitypub/follow.ts b/server/lib/activitypub/follow.ts
index c1bd667e0..741b54df5 100644
--- a/server/lib/activitypub/follow.ts
+++ b/server/lib/activitypub/follow.ts
@@ -31,6 +31,21 @@ async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors, transact
31 } 31 }
32} 32}
33 33
34// If we only have an host, use a default account handle
35function getRemoteNameAndHost (handleOrHost: string) {
36 let name = SERVER_ACTOR_NAME
37 let host = handleOrHost
38
39 const splitted = handleOrHost.split('@')
40 if (splitted.length === 2) {
41 name = splitted[0]
42 host = splitted[1]
43 }
44
45 return { name, host }
46}
47
34export { 48export {
35 autoFollowBackIfNeeded 49 autoFollowBackIfNeeded,
50 getRemoteNameAndHost
36} 51}
diff --git a/server/lib/activitypub/playlists/refresh.ts b/server/lib/activitypub/playlists/refresh.ts
index ef3cb3fe4..493e8c7ec 100644
--- a/server/lib/activitypub/playlists/refresh.ts
+++ b/server/lib/activitypub/playlists/refresh.ts
@@ -2,7 +2,7 @@ import { logger, loggerTagsFactory } from '@server/helpers/logger'
2import { PeerTubeRequestError } from '@server/helpers/requests' 2import { PeerTubeRequestError } from '@server/helpers/requests'
3import { JobQueue } from '@server/lib/job-queue' 3import { JobQueue } from '@server/lib/job-queue'
4import { MVideoPlaylist, MVideoPlaylistOwner } from '@server/types/models' 4import { MVideoPlaylist, MVideoPlaylistOwner } from '@server/types/models'
5import { HttpStatusCode } from '@shared/core-utils' 5import { HttpStatusCode } from '@shared/models'
6import { createOrUpdateVideoPlaylist } from './create-update' 6import { createOrUpdateVideoPlaylist } from './create-update'
7import { fetchRemoteVideoPlaylist } from './shared' 7import { fetchRemoteVideoPlaylist } from './shared'
8 8
diff --git a/server/lib/activitypub/videos/refresh.ts b/server/lib/activitypub/videos/refresh.ts
index a7b82f286..3af08acf4 100644
--- a/server/lib/activitypub/videos/refresh.ts
+++ b/server/lib/activitypub/videos/refresh.ts
@@ -4,7 +4,7 @@ import { ActorFollowScoreCache } from '@server/lib/files-cache'
4import { VideoLoadByUrlType } from '@server/lib/model-loaders' 4import { VideoLoadByUrlType } from '@server/lib/model-loaders'
5import { VideoModel } from '@server/models/video/video' 5import { VideoModel } from '@server/models/video/video'
6import { MVideoAccountLightBlacklistAllFiles, MVideoThumbnail } from '@server/types/models' 6import { MVideoAccountLightBlacklistAllFiles, MVideoThumbnail } from '@server/types/models'
7import { HttpStatusCode } from '@shared/core-utils' 7import { HttpStatusCode } from '@shared/models'
8import { fetchRemoteVideo, SyncParam, syncVideoExternalAttributes } from './shared' 8import { fetchRemoteVideo, SyncParam, syncVideoExternalAttributes } from './shared'
9import { APVideoUpdater } from './updater' 9import { APVideoUpdater } from './updater'
10 10
diff --git a/server/lib/activitypub/videos/shared/abstract-builder.ts b/server/lib/activitypub/videos/shared/abstract-builder.ts
index e89c94bcd..f995fe637 100644
--- a/server/lib/activitypub/videos/shared/abstract-builder.ts
+++ b/server/lib/activitypub/videos/shared/abstract-builder.ts
@@ -1,6 +1,6 @@
1import { Transaction } from 'sequelize/types' 1import { Transaction } from 'sequelize/types'
2import { checkUrlsSameHost } from '@server/helpers/activitypub' 2import { checkUrlsSameHost } from '@server/helpers/activitypub'
3import { deleteNonExistingModels } from '@server/helpers/database-utils' 3import { deleteAllModels, filterNonExistingModels } from '@server/helpers/database-utils'
4import { logger, LoggerTagsFn } from '@server/helpers/logger' 4import { logger, LoggerTagsFn } from '@server/helpers/logger'
5import { updatePlaceholderThumbnail, updateVideoMiniatureFromUrl } from '@server/lib/thumbnail' 5import { updatePlaceholderThumbnail, updateVideoMiniatureFromUrl } from '@server/lib/thumbnail'
6import { setVideoTags } from '@server/lib/video' 6import { setVideoTags } from '@server/lib/video'
@@ -111,8 +111,7 @@ export abstract class APVideoAbstractBuilder {
111 const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) 111 const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a))
112 112
113 // Remove video files that do not exist anymore 113 // Remove video files that do not exist anymore
114 const destroyTasks = deleteNonExistingModels(video.VideoFiles || [], newVideoFiles, t) 114 await deleteAllModels(filterNonExistingModels(video.VideoFiles || [], newVideoFiles), t)
115 await Promise.all(destroyTasks)
116 115
117 // Update or add other one 116 // Update or add other one
118 const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'video', t)) 117 const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'video', t))
@@ -124,13 +123,11 @@ export abstract class APVideoAbstractBuilder {
124 const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) 123 const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a))
125 124
126 // Remove video playlists that do not exist anymore 125 // Remove video playlists that do not exist anymore
127 const destroyTasks = deleteNonExistingModels(video.VideoStreamingPlaylists || [], newStreamingPlaylists, t) 126 await deleteAllModels(filterNonExistingModels(video.VideoStreamingPlaylists || [], newStreamingPlaylists), t)
128 await Promise.all(destroyTasks)
129 127
130 video.VideoStreamingPlaylists = [] 128 video.VideoStreamingPlaylists = []
131 129
132 for (const playlistAttributes of streamingPlaylistAttributes) { 130 for (const playlistAttributes of streamingPlaylistAttributes) {
133
134 const streamingPlaylistModel = await this.insertOrReplaceStreamingPlaylist(playlistAttributes, t) 131 const streamingPlaylistModel = await this.insertOrReplaceStreamingPlaylist(playlistAttributes, t)
135 streamingPlaylistModel.Video = video 132 streamingPlaylistModel.Video = video
136 133
@@ -163,8 +160,7 @@ export abstract class APVideoAbstractBuilder {
163 160
164 const newVideoFiles: MVideoFile[] = getFileAttributesFromUrl(playlistModel, tagObjects).map(a => new VideoFileModel(a)) 161 const newVideoFiles: MVideoFile[] = getFileAttributesFromUrl(playlistModel, tagObjects).map(a => new VideoFileModel(a))
165 162
166 const destroyTasks = deleteNonExistingModels(oldStreamingPlaylistFiles, newVideoFiles, t) 163 await deleteAllModels(filterNonExistingModels(oldStreamingPlaylistFiles, newVideoFiles), t)
167 await Promise.all(destroyTasks)
168 164
169 // Update or add other one 165 // Update or add other one
170 const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'streaming-playlist', t)) 166 const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'streaming-playlist', t))
diff --git a/server/lib/activitypub/videos/shared/object-to-model-attributes.ts b/server/lib/activitypub/videos/shared/object-to-model-attributes.ts
index 85548428c..1fa16295d 100644
--- a/server/lib/activitypub/videos/shared/object-to-model-attributes.ts
+++ b/server/lib/activitypub/videos/shared/object-to-model-attributes.ts
@@ -7,10 +7,11 @@ import { logger } from '@server/helpers/logger'
7import { getExtFromMimetype } from '@server/helpers/video' 7import { getExtFromMimetype } from '@server/helpers/video'
8import { ACTIVITY_PUB, MIMETYPES, P2P_MEDIA_LOADER_PEER_VERSION, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '@server/initializers/constants' 8import { ACTIVITY_PUB, MIMETYPES, P2P_MEDIA_LOADER_PEER_VERSION, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '@server/initializers/constants'
9import { generateTorrentFileName } from '@server/lib/video-paths' 9import { generateTorrentFileName } from '@server/lib/video-paths'
10import { VideoCaptionModel } from '@server/models/video/video-caption'
10import { VideoFileModel } from '@server/models/video/video-file' 11import { VideoFileModel } from '@server/models/video/video-file'
11import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' 12import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
12import { FilteredModelAttributes } from '@server/types' 13import { FilteredModelAttributes } from '@server/types'
13import { MChannelId, MStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoId } from '@server/types/models' 14import { isStreamingPlaylist, MChannelId, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoId } from '@server/types/models'
14import { 15import {
15 ActivityHashTagObject, 16 ActivityHashTagObject,
16 ActivityMagnetUrlObject, 17 ActivityMagnetUrlObject,
@@ -23,7 +24,6 @@ import {
23 VideoPrivacy, 24 VideoPrivacy,
24 VideoStreamingPlaylistType 25 VideoStreamingPlaylistType
25} from '@shared/models' 26} from '@shared/models'
26import { VideoCaptionModel } from '@server/models/video/video-caption'
27 27
28function getThumbnailFromIcons (videoObject: VideoObject) { 28function getThumbnailFromIcons (videoObject: VideoObject) {
29 let validIcons = videoObject.icon.filter(i => i.width > THUMBNAILS_SIZE.minWidth) 29 let validIcons = videoObject.icon.filter(i => i.width > THUMBNAILS_SIZE.minWidth)
@@ -80,8 +80,8 @@ function getFileAttributesFromUrl (
80 80
81 const extname = getExtFromMimetype(MIMETYPES.VIDEO.MIMETYPE_EXT, fileUrl.mediaType) 81 const extname = getExtFromMimetype(MIMETYPES.VIDEO.MIMETYPE_EXT, fileUrl.mediaType)
82 const resolution = fileUrl.height 82 const resolution = fileUrl.height
83 const videoId = (videoOrPlaylist as MStreamingPlaylist).playlistUrl ? null : videoOrPlaylist.id 83 const videoId = isStreamingPlaylist(videoOrPlaylist) ? null : videoOrPlaylist.id
84 const videoStreamingPlaylistId = (videoOrPlaylist as MStreamingPlaylist).playlistUrl ? videoOrPlaylist.id : null 84 const videoStreamingPlaylistId = isStreamingPlaylist(videoOrPlaylist) ? videoOrPlaylist.id : null
85 85
86 const attribute = { 86 const attribute = {
87 extname, 87 extname,
@@ -130,8 +130,13 @@ function getStreamingPlaylistAttributesFromObject (video: MVideoId, videoObject:
130 130
131 const attribute = { 131 const attribute = {
132 type: VideoStreamingPlaylistType.HLS, 132 type: VideoStreamingPlaylistType.HLS,
133
134 playlistFilename: basename(playlistUrlObject.href),
133 playlistUrl: playlistUrlObject.href, 135 playlistUrl: playlistUrlObject.href,
136
137 segmentsSha256Filename: basename(segmentsSha256UrlObject.href),
134 segmentsSha256Url: segmentsSha256UrlObject.href, 138 segmentsSha256Url: segmentsSha256UrlObject.href,
139
135 p2pMediaLoaderInfohashes: VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(playlistUrlObject.href, files), 140 p2pMediaLoaderInfohashes: VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(playlistUrlObject.href, files),
136 p2pMediaLoaderPeerVersion: P2P_MEDIA_LOADER_PEER_VERSION, 141 p2pMediaLoaderPeerVersion: P2P_MEDIA_LOADER_PEER_VERSION,
137 videoId: video.id, 142 videoId: video.id,
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts
index 72194416d..a557c090f 100644
--- a/server/lib/client-html.ts
+++ b/server/lib/client-html.ts
@@ -5,7 +5,7 @@ import validator from 'validator'
5import { escapeHTML } from '@shared/core-utils/renderer' 5import { escapeHTML } from '@shared/core-utils/renderer'
6import { HTMLServerConfig } from '@shared/models' 6import { HTMLServerConfig } from '@shared/models'
7import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n' 7import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n'
8import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 8import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
9import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos' 9import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos'
10import { isTestInstance, sha256 } from '../helpers/core-utils' 10import { isTestInstance, sha256 } from '../helpers/core-utils'
11import { logger } from '../helpers/logger' 11import { logger } from '../helpers/logger'
@@ -162,7 +162,7 @@ class ClientHtml {
162 let customHtml = ClientHtml.addTitleTag(html, escapeHTML(videoPlaylist.name)) 162 let customHtml = ClientHtml.addTitleTag(html, escapeHTML(videoPlaylist.name))
163 customHtml = ClientHtml.addDescriptionTag(customHtml, mdToPlainText(videoPlaylist.description)) 163 customHtml = ClientHtml.addDescriptionTag(customHtml, mdToPlainText(videoPlaylist.description))
164 164
165 const url = videoPlaylist.getWatchUrl() 165 const url = WEBSERVER.URL + videoPlaylist.getWatchStaticPath()
166 const originUrl = videoPlaylist.url 166 const originUrl = videoPlaylist.url
167 const title = escapeHTML(videoPlaylist.name) 167 const title = escapeHTML(videoPlaylist.name)
168 const siteName = escapeHTML(CONFIG.INSTANCE.NAME) 168 const siteName = escapeHTML(CONFIG.INSTANCE.NAME)
diff --git a/server/lib/hls.ts b/server/lib/hls.ts
index 05be403f3..32b02bc26 100644
--- a/server/lib/hls.ts
+++ b/server/lib/hls.ts
@@ -1,7 +1,7 @@
1import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra' 1import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra'
2import { flatten, uniq } from 'lodash' 2import { flatten, uniq } from 'lodash'
3import { basename, dirname, join } from 'path' 3import { basename, dirname, join } from 'path'
4import { MVideoWithFile } from '@server/types/models' 4import { MStreamingPlaylistFilesVideo, MVideoWithFile } from '@server/types/models'
5import { sha256 } from '../helpers/core-utils' 5import { sha256 } from '../helpers/core-utils'
6import { getAudioStreamCodec, getVideoStreamCodec, getVideoStreamSize } from '../helpers/ffprobe-utils' 6import { getAudioStreamCodec, getVideoStreamCodec, getVideoStreamSize } from '../helpers/ffprobe-utils'
7import { logger } from '../helpers/logger' 7import { logger } from '../helpers/logger'
@@ -12,7 +12,7 @@ import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from
12import { sequelizeTypescript } from '../initializers/database' 12import { sequelizeTypescript } from '../initializers/database'
13import { VideoFileModel } from '../models/video/video-file' 13import { VideoFileModel } from '../models/video/video-file'
14import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' 14import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
15import { getVideoFilePath } from './video-paths' 15import { getHlsResolutionPlaylistFilename, getVideoFilePath } from './video-paths'
16 16
17async function updateStreamingPlaylistsInfohashesIfNeeded () { 17async function updateStreamingPlaylistsInfohashesIfNeeded () {
18 const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() 18 const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion()
@@ -22,25 +22,29 @@ async function updateStreamingPlaylistsInfohashesIfNeeded () {
22 await sequelizeTypescript.transaction(async t => { 22 await sequelizeTypescript.transaction(async t => {
23 const videoFiles = await VideoFileModel.listByStreamingPlaylist(playlist.id, t) 23 const videoFiles = await VideoFileModel.listByStreamingPlaylist(playlist.id, t)
24 24
25 playlist.p2pMediaLoaderInfohashes = VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(playlist.playlistUrl, videoFiles) 25 playlist.assignP2PMediaLoaderInfoHashes(playlist.Video, videoFiles)
26 playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION 26 playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION
27
27 await playlist.save({ transaction: t }) 28 await playlist.save({ transaction: t })
28 }) 29 })
29 } 30 }
30} 31}
31 32
32async function updateMasterHLSPlaylist (video: MVideoWithFile) { 33async function updateMasterHLSPlaylist (video: MVideoWithFile, playlist: MStreamingPlaylistFilesVideo) {
33 const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) 34 const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
35
34 const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ] 36 const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ]
35 const masterPlaylistPath = join(directory, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename())
36 const streamingPlaylist = video.getHLSPlaylist()
37 37
38 for (const file of streamingPlaylist.VideoFiles) { 38 const masterPlaylistPath = join(directory, playlist.playlistFilename)
39
40 for (const file of playlist.VideoFiles) {
41 const playlistFilename = getHlsResolutionPlaylistFilename(file.filename)
42
39 // If we did not generated a playlist for this resolution, skip 43 // If we did not generated a playlist for this resolution, skip
40 const filePlaylistPath = join(directory, VideoStreamingPlaylistModel.getHlsPlaylistFilename(file.resolution)) 44 const filePlaylistPath = join(directory, playlistFilename)
41 if (await pathExists(filePlaylistPath) === false) continue 45 if (await pathExists(filePlaylistPath) === false) continue
42 46
43 const videoFilePath = getVideoFilePath(streamingPlaylist, file) 47 const videoFilePath = getVideoFilePath(playlist, file)
44 48
45 const size = await getVideoStreamSize(videoFilePath) 49 const size = await getVideoStreamSize(videoFilePath)
46 50
@@ -58,29 +62,28 @@ async function updateMasterHLSPlaylist (video: MVideoWithFile) {
58 line += `,CODECS="${codecs.filter(c => !!c).join(',')}"` 62 line += `,CODECS="${codecs.filter(c => !!c).join(',')}"`
59 63
60 masterPlaylists.push(line) 64 masterPlaylists.push(line)
61 masterPlaylists.push(VideoStreamingPlaylistModel.getHlsPlaylistFilename(file.resolution)) 65 masterPlaylists.push(playlistFilename)
62 } 66 }
63 67
64 await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n') 68 await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n')
65} 69}
66 70
67async function updateSha256VODSegments (video: MVideoWithFile) { 71async function updateSha256VODSegments (video: MVideoWithFile, playlist: MStreamingPlaylistFilesVideo) {
68 const json: { [filename: string]: { [range: string]: string } } = {} 72 const json: { [filename: string]: { [range: string]: string } } = {}
69 73
70 const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) 74 const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
71 const hlsPlaylist = video.getHLSPlaylist()
72 75
73 // For all the resolutions available for this video 76 // For all the resolutions available for this video
74 for (const file of hlsPlaylist.VideoFiles) { 77 for (const file of playlist.VideoFiles) {
75 const rangeHashes: { [range: string]: string } = {} 78 const rangeHashes: { [range: string]: string } = {}
76 79
77 const videoPath = getVideoFilePath(hlsPlaylist, file) 80 const videoPath = getVideoFilePath(playlist, file)
78 const playlistPath = join(playlistDirectory, VideoStreamingPlaylistModel.getHlsPlaylistFilename(file.resolution)) 81 const resolutionPlaylistPath = join(playlistDirectory, getHlsResolutionPlaylistFilename(file.filename))
79 82
80 // Maybe the playlist is not generated for this resolution yet 83 // Maybe the playlist is not generated for this resolution yet
81 if (!await pathExists(playlistPath)) continue 84 if (!await pathExists(resolutionPlaylistPath)) continue
82 85
83 const playlistContent = await readFile(playlistPath) 86 const playlistContent = await readFile(resolutionPlaylistPath)
84 const ranges = getRangesFromPlaylist(playlistContent.toString()) 87 const ranges = getRangesFromPlaylist(playlistContent.toString())
85 88
86 const fd = await open(videoPath, 'r') 89 const fd = await open(videoPath, 'r')
@@ -96,7 +99,7 @@ async function updateSha256VODSegments (video: MVideoWithFile) {
96 json[videoFilename] = rangeHashes 99 json[videoFilename] = rangeHashes
97 } 100 }
98 101
99 const outputPath = join(playlistDirectory, VideoStreamingPlaylistModel.getHlsSha256SegmentsFilename()) 102 const outputPath = join(playlistDirectory, playlist.segmentsSha256Filename)
100 await outputJSON(outputPath, json) 103 await outputJSON(outputPath, json)
101} 104}
102 105
diff --git a/server/lib/job-queue/handlers/activitypub-cleaner.ts b/server/lib/job-queue/handlers/activitypub-cleaner.ts
index 1caca1dcc..56e2b0ceb 100644
--- a/server/lib/job-queue/handlers/activitypub-cleaner.ts
+++ b/server/lib/job-queue/handlers/activitypub-cleaner.ts
@@ -12,7 +12,7 @@ import { AP_CLEANER_CONCURRENCY } from '@server/initializers/constants'
12import { VideoModel } from '@server/models/video/video' 12import { VideoModel } from '@server/models/video/video'
13import { VideoCommentModel } from '@server/models/video/video-comment' 13import { VideoCommentModel } from '@server/models/video/video-comment'
14import { VideoShareModel } from '@server/models/video/video-share' 14import { VideoShareModel } from '@server/models/video/video-share'
15import { HttpStatusCode } from '@shared/core-utils' 15import { HttpStatusCode } from '@shared/models'
16import { logger } from '../../../helpers/logger' 16import { logger } from '../../../helpers/logger'
17import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 17import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
18 18
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts
index 187cb652e..4d199f247 100644
--- a/server/lib/job-queue/handlers/video-file-import.ts
+++ b/server/lib/job-queue/handlers/video-file-import.ts
@@ -2,7 +2,7 @@ import * as Bull from 'bull'
2import { copy, stat } from 'fs-extra' 2import { copy, stat } from 'fs-extra'
3import { getLowercaseExtension } from '@server/helpers/core-utils' 3import { getLowercaseExtension } from '@server/helpers/core-utils'
4import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' 4import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
5import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' 5import { generateWebTorrentVideoFilename, getVideoFilePath } from '@server/lib/video-paths'
6import { UserModel } from '@server/models/user/user' 6import { UserModel } from '@server/models/user/user'
7import { MVideoFullLight } from '@server/types/models' 7import { MVideoFullLight } from '@server/types/models'
8import { VideoFileImportPayload } from '@shared/models' 8import { VideoFileImportPayload } from '@shared/models'
@@ -61,8 +61,7 @@ async function updateVideoFile (video: MVideoFullLight, inputFilePath: string) {
61 61
62 if (currentVideoFile) { 62 if (currentVideoFile) {
63 // Remove old file and old torrent 63 // Remove old file and old torrent
64 await video.removeFile(currentVideoFile) 64 await video.removeFileAndTorrent(currentVideoFile)
65 await currentVideoFile.removeTorrent()
66 // Remove the old video file from the array 65 // Remove the old video file from the array
67 video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile) 66 video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile)
68 67
@@ -72,7 +71,7 @@ async function updateVideoFile (video: MVideoFullLight, inputFilePath: string) {
72 const newVideoFile = new VideoFileModel({ 71 const newVideoFile = new VideoFileModel({
73 resolution: videoFileResolution, 72 resolution: videoFileResolution,
74 extname: fileExt, 73 extname: fileExt,
75 filename: generateVideoFilename(video, false, videoFileResolution, fileExt), 74 filename: generateWebTorrentVideoFilename(videoFileResolution, fileExt),
76 size, 75 size,
77 fps, 76 fps,
78 videoId: video.id 77 videoId: video.id
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts
index 55498003d..6e425d09c 100644
--- a/server/lib/job-queue/handlers/video-import.ts
+++ b/server/lib/job-queue/handlers/video-import.ts
@@ -8,7 +8,7 @@ import { Hooks } from '@server/lib/plugins/hooks'
8import { ServerConfigManager } from '@server/lib/server-config-manager' 8import { ServerConfigManager } from '@server/lib/server-config-manager'
9import { isAbleToUploadVideo } from '@server/lib/user' 9import { isAbleToUploadVideo } from '@server/lib/user'
10import { addOptimizeOrMergeAudioJob } from '@server/lib/video' 10import { addOptimizeOrMergeAudioJob } from '@server/lib/video'
11import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' 11import { generateWebTorrentVideoFilename, getVideoFilePath } from '@server/lib/video-paths'
12import { ThumbnailModel } from '@server/models/video/thumbnail' 12import { ThumbnailModel } from '@server/models/video/thumbnail'
13import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/types/models/video/video-import' 13import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/types/models/video/video-import'
14import { 14import {
@@ -124,7 +124,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: MVid
124 extname: fileExt, 124 extname: fileExt,
125 resolution: videoFileResolution, 125 resolution: videoFileResolution,
126 size: stats.size, 126 size: stats.size,
127 filename: generateVideoFilename(videoImport.Video, false, videoFileResolution, fileExt), 127 filename: generateWebTorrentVideoFilename(videoFileResolution, fileExt),
128 fps, 128 fps,
129 videoId: videoImport.videoId 129 videoId: videoImport.videoId
130 } 130 }
diff --git a/server/lib/job-queue/handlers/video-live-ending.ts b/server/lib/job-queue/handlers/video-live-ending.ts
index 9eba41bf8..386ccdc7b 100644
--- a/server/lib/job-queue/handlers/video-live-ending.ts
+++ b/server/lib/job-queue/handlers/video-live-ending.ts
@@ -7,12 +7,12 @@ import { buildConcatenatedName, cleanupLive, LiveSegmentShaStore } from '@server
7import { generateVideoMiniature } from '@server/lib/thumbnail' 7import { generateVideoMiniature } from '@server/lib/thumbnail'
8import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/video-transcoding' 8import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/video-transcoding'
9import { publishAndFederateIfNeeded } from '@server/lib/video' 9import { publishAndFederateIfNeeded } from '@server/lib/video'
10import { getHLSDirectory } from '@server/lib/video-paths' 10import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename, getHLSDirectory } from '@server/lib/video-paths'
11import { VideoModel } from '@server/models/video/video' 11import { VideoModel } from '@server/models/video/video'
12import { VideoFileModel } from '@server/models/video/video-file' 12import { VideoFileModel } from '@server/models/video/video-file'
13import { VideoLiveModel } from '@server/models/video/video-live' 13import { VideoLiveModel } from '@server/models/video/video-live'
14import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' 14import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
15import { MVideo, MVideoLive } from '@server/types/models' 15import { MStreamingPlaylist, MVideo, MVideoLive } from '@server/types/models'
16import { ThumbnailType, VideoLiveEndingPayload, VideoState } from '@shared/models' 16import { ThumbnailType, VideoLiveEndingPayload, VideoState } from '@shared/models'
17import { logger } from '../../../helpers/logger' 17import { logger } from '../../../helpers/logger'
18 18
@@ -43,7 +43,7 @@ async function processVideoLiveEnding (job: Bull.Job) {
43 return cleanupLive(video, streamingPlaylist) 43 return cleanupLive(video, streamingPlaylist)
44 } 44 }
45 45
46 return saveLive(video, live) 46 return saveLive(video, live, streamingPlaylist)
47} 47}
48 48
49// --------------------------------------------------------------------------- 49// ---------------------------------------------------------------------------
@@ -54,14 +54,14 @@ export {
54 54
55// --------------------------------------------------------------------------- 55// ---------------------------------------------------------------------------
56 56
57async function saveLive (video: MVideo, live: MVideoLive) { 57async function saveLive (video: MVideo, live: MVideoLive, streamingPlaylist: MStreamingPlaylist) {
58 const hlsDirectory = getHLSDirectory(video, false) 58 const hlsDirectory = getHLSDirectory(video, false)
59 const replayDirectory = join(hlsDirectory, VIDEO_LIVE.REPLAY_DIRECTORY) 59 const replayDirectory = join(hlsDirectory, VIDEO_LIVE.REPLAY_DIRECTORY)
60 60
61 const rootFiles = await readdir(hlsDirectory) 61 const rootFiles = await readdir(hlsDirectory)
62 62
63 const playlistFiles = rootFiles.filter(file => { 63 const playlistFiles = rootFiles.filter(file => {
64 return file.endsWith('.m3u8') && file !== 'master.m3u8' 64 return file.endsWith('.m3u8') && file !== streamingPlaylist.playlistFilename
65 }) 65 })
66 66
67 await cleanupLiveFiles(hlsDirectory) 67 await cleanupLiveFiles(hlsDirectory)
@@ -80,7 +80,12 @@ async function saveLive (video: MVideo, live: MVideoLive) {
80 80
81 const hlsPlaylist = videoWithFiles.getHLSPlaylist() 81 const hlsPlaylist = videoWithFiles.getHLSPlaylist()
82 await VideoFileModel.removeHLSFilesOfVideoId(hlsPlaylist.id) 82 await VideoFileModel.removeHLSFilesOfVideoId(hlsPlaylist.id)
83
84 // Reset playlist
83 hlsPlaylist.VideoFiles = [] 85 hlsPlaylist.VideoFiles = []
86 hlsPlaylist.playlistFilename = generateHLSMasterPlaylistFilename()
87 hlsPlaylist.segmentsSha256Filename = generateHlsSha256SegmentsFilename()
88 await hlsPlaylist.save()
84 89
85 let durationDone = false 90 let durationDone = false
86 91
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts
index f5ba6f435..36d9594af 100644
--- a/server/lib/job-queue/handlers/video-transcoding.ts
+++ b/server/lib/job-queue/handlers/video-transcoding.ts
@@ -125,8 +125,7 @@ async function onHlsPlaylistGeneration (video: MVideoFullLight, user: MUser, pay
125 if (payload.isMaxQuality && CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false) { 125 if (payload.isMaxQuality && CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false) {
126 // Remove webtorrent files if not enabled 126 // Remove webtorrent files if not enabled
127 for (const file of video.VideoFiles) { 127 for (const file of video.VideoFiles) {
128 await video.removeFile(file) 128 await video.removeFileAndTorrent(file)
129 await file.removeTorrent()
130 await file.destroy() 129 await file.destroy()
131 } 130 }
132 131
diff --git a/server/lib/live/live-manager.ts b/server/lib/live/live-manager.ts
index 014cd3fcf..f106d69fb 100644
--- a/server/lib/live/live-manager.ts
+++ b/server/lib/live/live-manager.ts
@@ -4,24 +4,25 @@ import { isTestInstance } from '@server/helpers/core-utils'
4import { computeResolutionsToTranscode, getVideoFileFPS, getVideoFileResolution } from '@server/helpers/ffprobe-utils' 4import { computeResolutionsToTranscode, getVideoFileFPS, getVideoFileResolution } from '@server/helpers/ffprobe-utils'
5import { logger, loggerTagsFactory } from '@server/helpers/logger' 5import { logger, loggerTagsFactory } from '@server/helpers/logger'
6import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config' 6import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config'
7import { P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE, VIEW_LIFETIME, WEBSERVER } from '@server/initializers/constants' 7import { P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE, VIEW_LIFETIME } from '@server/initializers/constants'
8import { UserModel } from '@server/models/user/user' 8import { UserModel } from '@server/models/user/user'
9import { VideoModel } from '@server/models/video/video' 9import { VideoModel } from '@server/models/video/video'
10import { VideoLiveModel } from '@server/models/video/video-live' 10import { VideoLiveModel } from '@server/models/video/video-live'
11import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' 11import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
12import { MStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoLiveVideo } from '@server/types/models' 12import { MStreamingPlaylistVideo, MVideo, MVideoLiveVideo } from '@server/types/models'
13import { VideoState, VideoStreamingPlaylistType } from '@shared/models' 13import { VideoState, VideoStreamingPlaylistType } from '@shared/models'
14import { federateVideoIfNeeded } from '../activitypub/videos' 14import { federateVideoIfNeeded } from '../activitypub/videos'
15import { JobQueue } from '../job-queue' 15import { JobQueue } from '../job-queue'
16import { PeerTubeSocket } from '../peertube-socket' 16import { PeerTubeSocket } from '../peertube-socket'
17import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename } from '../video-paths'
17import { LiveQuotaStore } from './live-quota-store' 18import { LiveQuotaStore } from './live-quota-store'
18import { LiveSegmentShaStore } from './live-segment-sha-store' 19import { LiveSegmentShaStore } from './live-segment-sha-store'
19import { cleanupLive } from './live-utils' 20import { cleanupLive } from './live-utils'
20import { MuxingSession } from './shared' 21import { MuxingSession } from './shared'
21 22
22const NodeRtmpSession = require('node-media-server/node_rtmp_session') 23const NodeRtmpSession = require('node-media-server/src/node_rtmp_session')
23const context = require('node-media-server/node_core_ctx') 24const context = require('node-media-server/src/node_core_ctx')
24const nodeMediaServerLogger = require('node-media-server/node_core_logger') 25const nodeMediaServerLogger = require('node-media-server/src/node_core_logger')
25 26
26// Disable node media server logs 27// Disable node media server logs
27nodeMediaServerLogger.setLogType(0) 28nodeMediaServerLogger.setLogType(0)
@@ -392,19 +393,18 @@ class LiveManager {
392 return resolutionsEnabled.concat([ originResolution ]) 393 return resolutionsEnabled.concat([ originResolution ])
393 } 394 }
394 395
395 private async createLivePlaylist (video: MVideo, allResolutions: number[]) { 396 private async createLivePlaylist (video: MVideo, allResolutions: number[]): Promise<MStreamingPlaylistVideo> {
396 const playlistUrl = WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsMasterPlaylistStaticPath(video.uuid) 397 const playlist = await VideoStreamingPlaylistModel.loadOrGenerate(video)
397 const [ videoStreamingPlaylist ] = await VideoStreamingPlaylistModel.upsert({
398 videoId: video.id,
399 playlistUrl,
400 segmentsSha256Url: WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsSha256SegmentsStaticPath(video.uuid, video.isLive),
401 p2pMediaLoaderInfohashes: VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(playlistUrl, allResolutions),
402 p2pMediaLoaderPeerVersion: P2P_MEDIA_LOADER_PEER_VERSION,
403 398
404 type: VideoStreamingPlaylistType.HLS 399 playlist.playlistFilename = generateHLSMasterPlaylistFilename(true)
405 }, { returning: true }) as [ MStreamingPlaylist, boolean ] 400 playlist.segmentsSha256Filename = generateHlsSha256SegmentsFilename(true)
406 401
407 return Object.assign(videoStreamingPlaylist, { Video: video }) 402 playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION
403 playlist.type = VideoStreamingPlaylistType.HLS
404
405 playlist.assignP2PMediaLoaderInfoHashes(video, allResolutions)
406
407 return playlist.save()
408 } 408 }
409 409
410 static get Instance () { 410 static get Instance () {
diff --git a/server/lib/live/shared/muxing-session.ts b/server/lib/live/shared/muxing-session.ts
index 26467f060..709d6c615 100644
--- a/server/lib/live/shared/muxing-session.ts
+++ b/server/lib/live/shared/muxing-session.ts
@@ -112,13 +112,16 @@ class MuxingSession extends EventEmitter {
112 this.ffmpegCommand = CONFIG.LIVE.TRANSCODING.ENABLED 112 this.ffmpegCommand = CONFIG.LIVE.TRANSCODING.ENABLED
113 ? await getLiveTranscodingCommand({ 113 ? await getLiveTranscodingCommand({
114 rtmpUrl: this.rtmpUrl, 114 rtmpUrl: this.rtmpUrl,
115
115 outPath, 116 outPath,
117 masterPlaylistName: this.streamingPlaylist.playlistFilename,
118
116 resolutions: this.allResolutions, 119 resolutions: this.allResolutions,
117 fps: this.fps, 120 fps: this.fps,
118 availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), 121 availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(),
119 profile: CONFIG.LIVE.TRANSCODING.PROFILE 122 profile: CONFIG.LIVE.TRANSCODING.PROFILE
120 }) 123 })
121 : getLiveMuxingCommand(this.rtmpUrl, outPath) 124 : getLiveMuxingCommand(this.rtmpUrl, outPath, this.streamingPlaylist.playlistFilename)
122 125
123 logger.info('Running live muxing/transcoding for %s.', this.videoUUID, this.lTags) 126 logger.info('Running live muxing/transcoding for %s.', this.videoUUID, this.lTags)
124 127
@@ -182,7 +185,7 @@ class MuxingSession extends EventEmitter {
182 } 185 }
183 186
184 private watchMasterFile (outPath: string) { 187 private watchMasterFile (outPath: string) {
185 this.masterWatcher = chokidar.watch(outPath + '/master.m3u8') 188 this.masterWatcher = chokidar.watch(outPath + '/' + this.streamingPlaylist.playlistFilename)
186 189
187 this.masterWatcher.on('add', async () => { 190 this.masterWatcher.on('add', async () => {
188 this.emit('master-playlist-created', { videoId: this.videoId }) 191 this.emit('master-playlist-created', { videoId: this.videoId })
diff --git a/server/lib/moderation.ts b/server/lib/moderation.ts
index 14e00518e..a42ab5b7f 100644
--- a/server/lib/moderation.ts
+++ b/server/lib/moderation.ts
@@ -23,7 +23,7 @@ import { ActivityCreate } from '../../shared/models/activitypub'
23import { VideoObject } from '../../shared/models/activitypub/objects' 23import { VideoObject } from '../../shared/models/activitypub/objects'
24import { VideoCommentObject } from '../../shared/models/activitypub/objects/video-comment-object' 24import { VideoCommentObject } from '../../shared/models/activitypub/objects/video-comment-object'
25import { LiveVideoCreate, VideoCreate, VideoImportCreate } from '../../shared/models/videos' 25import { LiveVideoCreate, VideoCreate, VideoImportCreate } from '../../shared/models/videos'
26import { VideoCommentCreate } from '../../shared/models/videos/comment/video-comment.model' 26import { VideoCommentCreate } from '../../shared/models/videos/comment'
27import { ActorModel } from '../models/actor/actor' 27import { ActorModel } from '../models/actor/actor'
28import { UserModel } from '../models/user/user' 28import { UserModel } from '../models/user/user'
29import { VideoModel } from '../models/video/video' 29import { VideoModel } from '../models/video/video'
diff --git a/server/lib/plugins/register-helpers.ts b/server/lib/plugins/register-helpers.ts
index 09275f9ba..af533effd 100644
--- a/server/lib/plugins/register-helpers.ts
+++ b/server/lib/plugins/register-helpers.ts
@@ -1,13 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { logger } from '@server/helpers/logger' 2import { logger } from '@server/helpers/logger'
3import {
4 VIDEO_CATEGORIES,
5 VIDEO_LANGUAGES,
6 VIDEO_LICENCES,
7 VIDEO_PLAYLIST_PRIVACIES,
8 VIDEO_PRIVACIES
9} from '@server/initializers/constants'
10import { onExternalUserAuthenticated } from '@server/lib/auth/external-auth' 3import { onExternalUserAuthenticated } from '@server/lib/auth/external-auth'
4import { VideoConstantManagerFactory } from '@server/lib/plugins/video-constant-manager-factory'
11import { PluginModel } from '@server/models/server/plugin' 5import { PluginModel } from '@server/models/server/plugin'
12import { 6import {
13 RegisterServerAuthExternalOptions, 7 RegisterServerAuthExternalOptions,
@@ -18,41 +12,18 @@ import {
18} from '@server/types/plugins' 12} from '@server/types/plugins'
19import { 13import {
20 EncoderOptionsBuilder, 14 EncoderOptionsBuilder,
21 PluginPlaylistPrivacyManager,
22 PluginSettingsManager, 15 PluginSettingsManager,
23 PluginStorageManager, 16 PluginStorageManager,
24 PluginVideoCategoryManager,
25 PluginVideoLanguageManager,
26 PluginVideoLicenceManager,
27 PluginVideoPrivacyManager,
28 RegisterServerHookOptions, 17 RegisterServerHookOptions,
29 RegisterServerSettingOptions, 18 RegisterServerSettingOptions,
30 serverHookObject 19 serverHookObject,
20 VideoPlaylistPrivacy,
21 VideoPrivacy
31} from '@shared/models' 22} from '@shared/models'
32import { VideoTranscodingProfilesManager } from '../transcoding/video-transcoding-profiles' 23import { VideoTranscodingProfilesManager } from '../transcoding/video-transcoding-profiles'
33import { buildPluginHelpers } from './plugin-helpers-builder' 24import { buildPluginHelpers } from './plugin-helpers-builder'
34 25
35type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy'
36type VideoConstant = { [key in number | string]: string }
37
38type UpdatedVideoConstant = {
39 [name in AlterableVideoConstant]: {
40 [ npmName: string]: {
41 added: { key: number | string, label: string }[]
42 deleted: { key: number | string, label: string }[]
43 }
44 }
45}
46
47export class RegisterHelpers { 26export class RegisterHelpers {
48 private readonly updatedVideoConstants: UpdatedVideoConstant = {
49 playlistPrivacy: { },
50 privacy: { },
51 language: { },
52 licence: { },
53 category: { }
54 }
55
56 private readonly transcodingProfiles: { 27 private readonly transcodingProfiles: {
57 [ npmName: string ]: { 28 [ npmName: string ]: {
58 type: 'vod' | 'live' 29 type: 'vod' | 'live'
@@ -78,6 +49,7 @@ export class RegisterHelpers {
78 private readonly onSettingsChangeCallbacks: ((settings: any) => Promise<any>)[] = [] 49 private readonly onSettingsChangeCallbacks: ((settings: any) => Promise<any>)[] = []
79 50
80 private readonly router: express.Router 51 private readonly router: express.Router
52 private readonly videoConstantManagerFactory: VideoConstantManagerFactory
81 53
82 constructor ( 54 constructor (
83 private readonly npmName: string, 55 private readonly npmName: string,
@@ -85,6 +57,7 @@ export class RegisterHelpers {
85 private readonly onHookAdded: (options: RegisterServerHookOptions) => void 57 private readonly onHookAdded: (options: RegisterServerHookOptions) => void
86 ) { 58 ) {
87 this.router = express.Router() 59 this.router = express.Router()
60 this.videoConstantManagerFactory = new VideoConstantManagerFactory(this.npmName)
88 } 61 }
89 62
90 buildRegisterHelpers (): RegisterServerOptions { 63 buildRegisterHelpers (): RegisterServerOptions {
@@ -96,13 +69,13 @@ export class RegisterHelpers {
96 const settingsManager = this.buildSettingsManager() 69 const settingsManager = this.buildSettingsManager()
97 const storageManager = this.buildStorageManager() 70 const storageManager = this.buildStorageManager()
98 71
99 const videoLanguageManager = this.buildVideoLanguageManager() 72 const videoLanguageManager = this.videoConstantManagerFactory.createVideoConstantManager<string>('language')
100 73
101 const videoLicenceManager = this.buildVideoLicenceManager() 74 const videoLicenceManager = this.videoConstantManagerFactory.createVideoConstantManager<number>('licence')
102 const videoCategoryManager = this.buildVideoCategoryManager() 75 const videoCategoryManager = this.videoConstantManagerFactory.createVideoConstantManager<number>('category')
103 76
104 const videoPrivacyManager = this.buildVideoPrivacyManager() 77 const videoPrivacyManager = this.videoConstantManagerFactory.createVideoConstantManager<VideoPrivacy>('privacy')
105 const playlistPrivacyManager = this.buildPlaylistPrivacyManager() 78 const playlistPrivacyManager = this.videoConstantManagerFactory.createVideoConstantManager<VideoPlaylistPrivacy>('playlistPrivacy')
106 79
107 const transcodingManager = this.buildTranscodingManager() 80 const transcodingManager = this.buildTranscodingManager()
108 81
@@ -122,12 +95,38 @@ export class RegisterHelpers {
122 settingsManager, 95 settingsManager,
123 storageManager, 96 storageManager,
124 97
125 videoLanguageManager, 98 videoLanguageManager: {
126 videoCategoryManager, 99 ...videoLanguageManager,
127 videoLicenceManager, 100 /** @deprecated use `addConstant` instead **/
101 addLanguage: videoLanguageManager.addConstant,
102 /** @deprecated use `deleteConstant` instead **/
103 deleteLanguage: videoLanguageManager.deleteConstant
104 },
105 videoCategoryManager: {
106 ...videoCategoryManager,
107 /** @deprecated use `addConstant` instead **/
108 addCategory: videoCategoryManager.addConstant,
109 /** @deprecated use `deleteConstant` instead **/
110 deleteCategory: videoCategoryManager.deleteConstant
111 },
112 videoLicenceManager: {
113 ...videoLicenceManager,
114 /** @deprecated use `addConstant` instead **/
115 addLicence: videoLicenceManager.addConstant,
116 /** @deprecated use `deleteConstant` instead **/
117 deleteLicence: videoLicenceManager.deleteConstant
118 },
128 119
129 videoPrivacyManager, 120 videoPrivacyManager: {
130 playlistPrivacyManager, 121 ...videoPrivacyManager,
122 /** @deprecated use `deleteConstant` instead **/
123 deletePrivacy: videoPrivacyManager.deleteConstant
124 },
125 playlistPrivacyManager: {
126 ...playlistPrivacyManager,
127 /** @deprecated use `deleteConstant` instead **/
128 deletePlaylistPrivacy: playlistPrivacyManager.deleteConstant
129 },
131 130
132 transcodingManager, 131 transcodingManager,
133 132
@@ -141,29 +140,7 @@ export class RegisterHelpers {
141 } 140 }
142 141
143 reinitVideoConstants (npmName: string) { 142 reinitVideoConstants (npmName: string) {
144 const hash = { 143 this.videoConstantManagerFactory.resetVideoConstants(npmName)
145 language: VIDEO_LANGUAGES,
146 licence: VIDEO_LICENCES,
147 category: VIDEO_CATEGORIES,
148 privacy: VIDEO_PRIVACIES,
149 playlistPrivacy: VIDEO_PLAYLIST_PRIVACIES
150 }
151 const types: AlterableVideoConstant[] = [ 'language', 'licence', 'category', 'privacy', 'playlistPrivacy' ]
152
153 for (const type of types) {
154 const updatedConstants = this.updatedVideoConstants[type][npmName]
155 if (!updatedConstants) continue
156
157 for (const added of updatedConstants.added) {
158 delete hash[type][added.key]
159 }
160
161 for (const deleted of updatedConstants.deleted) {
162 hash[type][deleted.key] = deleted.label
163 }
164
165 delete this.updatedVideoConstants[type][npmName]
166 }
167 } 144 }
168 145
169 reinitTranscodingProfilesAndEncoders (npmName: string) { 146 reinitTranscodingProfilesAndEncoders (npmName: string) {
@@ -291,119 +268,6 @@ export class RegisterHelpers {
291 } 268 }
292 } 269 }
293 270
294 private buildVideoLanguageManager (): PluginVideoLanguageManager {
295 return {
296 addLanguage: (key: string, label: string) => {
297 return this.addConstant({ npmName: this.npmName, type: 'language', obj: VIDEO_LANGUAGES, key, label })
298 },
299
300 deleteLanguage: (key: string) => {
301 return this.deleteConstant({ npmName: this.npmName, type: 'language', obj: VIDEO_LANGUAGES, key })
302 }
303 }
304 }
305
306 private buildVideoCategoryManager (): PluginVideoCategoryManager {
307 return {
308 addCategory: (key: number, label: string) => {
309 return this.addConstant({ npmName: this.npmName, type: 'category', obj: VIDEO_CATEGORIES, key, label })
310 },
311
312 deleteCategory: (key: number) => {
313 return this.deleteConstant({ npmName: this.npmName, type: 'category', obj: VIDEO_CATEGORIES, key })
314 }
315 }
316 }
317
318 private buildVideoPrivacyManager (): PluginVideoPrivacyManager {
319 return {
320 deletePrivacy: (key: number) => {
321 return this.deleteConstant({ npmName: this.npmName, type: 'privacy', obj: VIDEO_PRIVACIES, key })
322 }
323 }
324 }
325
326 private buildPlaylistPrivacyManager (): PluginPlaylistPrivacyManager {
327 return {
328 deletePlaylistPrivacy: (key: number) => {
329 return this.deleteConstant({ npmName: this.npmName, type: 'playlistPrivacy', obj: VIDEO_PLAYLIST_PRIVACIES, key })
330 }
331 }
332 }
333
334 private buildVideoLicenceManager (): PluginVideoLicenceManager {
335 return {
336 addLicence: (key: number, label: string) => {
337 return this.addConstant({ npmName: this.npmName, type: 'licence', obj: VIDEO_LICENCES, key, label })
338 },
339
340 deleteLicence: (key: number) => {
341 return this.deleteConstant({ npmName: this.npmName, type: 'licence', obj: VIDEO_LICENCES, key })
342 }
343 }
344 }
345
346 private addConstant<T extends string | number> (parameters: {
347 npmName: string
348 type: AlterableVideoConstant
349 obj: VideoConstant
350 key: T
351 label: string
352 }) {
353 const { npmName, type, obj, key, label } = parameters
354
355 if (obj[key]) {
356 logger.warn('Cannot add %s %s by plugin %s: key already exists.', type, npmName, key)
357 return false
358 }
359
360 if (!this.updatedVideoConstants[type][npmName]) {
361 this.updatedVideoConstants[type][npmName] = {
362 added: [],
363 deleted: []
364 }
365 }
366
367 this.updatedVideoConstants[type][npmName].added.push({ key, label })
368 obj[key] = label
369
370 return true
371 }
372
373 private deleteConstant<T extends string | number> (parameters: {
374 npmName: string
375 type: AlterableVideoConstant
376 obj: VideoConstant
377 key: T
378 }) {
379 const { npmName, type, obj, key } = parameters
380
381 if (!obj[key]) {
382 logger.warn('Cannot delete %s by plugin %s: key %s does not exist.', type, npmName, key)
383 return false
384 }
385
386 if (!this.updatedVideoConstants[type][npmName]) {
387 this.updatedVideoConstants[type][npmName] = {
388 added: [],
389 deleted: []
390 }
391 }
392
393 const updatedConstants = this.updatedVideoConstants[type][npmName]
394
395 const alreadyAdded = updatedConstants.added.find(a => a.key === key)
396 if (alreadyAdded) {
397 updatedConstants.added.filter(a => a.key !== key)
398 } else if (obj[key]) {
399 updatedConstants.deleted.push({ key, label: obj[key] })
400 }
401
402 delete obj[key]
403
404 return true
405 }
406
407 private buildTranscodingManager () { 271 private buildTranscodingManager () {
408 const self = this 272 const self = this
409 273
diff --git a/server/lib/plugins/video-constant-manager-factory.ts b/server/lib/plugins/video-constant-manager-factory.ts
new file mode 100644
index 000000000..f04dde29f
--- /dev/null
+++ b/server/lib/plugins/video-constant-manager-factory.ts
@@ -0,0 +1,139 @@
1import { logger } from '@server/helpers/logger'
2import {
3 VIDEO_CATEGORIES,
4 VIDEO_LANGUAGES,
5 VIDEO_LICENCES,
6 VIDEO_PLAYLIST_PRIVACIES,
7 VIDEO_PRIVACIES
8} from '@server/initializers/constants'
9import { ConstantManager } from '@shared/models/plugins/server/plugin-constant-manager.model'
10
11type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy'
12type VideoConstant = Record<number | string, string>
13
14type UpdatedVideoConstant = {
15 [name in AlterableVideoConstant]: {
16 [ npmName: string]: {
17 added: VideoConstant[]
18 deleted: VideoConstant[]
19 }
20 }
21}
22
23const constantsHash: { [key in AlterableVideoConstant]: VideoConstant } = {
24 language: VIDEO_LANGUAGES,
25 licence: VIDEO_LICENCES,
26 category: VIDEO_CATEGORIES,
27 privacy: VIDEO_PRIVACIES,
28 playlistPrivacy: VIDEO_PLAYLIST_PRIVACIES
29}
30
31export class VideoConstantManagerFactory {
32 private readonly updatedVideoConstants: UpdatedVideoConstant = {
33 playlistPrivacy: { },
34 privacy: { },
35 language: { },
36 licence: { },
37 category: { }
38 }
39
40 constructor (
41 private readonly npmName: string
42 ) {}
43
44 public resetVideoConstants (npmName: string) {
45 const types: AlterableVideoConstant[] = [ 'language', 'licence', 'category', 'privacy', 'playlistPrivacy' ]
46 for (const type of types) {
47 this.resetConstants({ npmName, type })
48 }
49 }
50
51 private resetConstants (parameters: { npmName: string, type: AlterableVideoConstant }) {
52 const { npmName, type } = parameters
53 const updatedConstants = this.updatedVideoConstants[type][npmName]
54
55 if (!updatedConstants) return
56
57 for (const added of updatedConstants.added) {
58 delete constantsHash[type][added.key]
59 }
60
61 for (const deleted of updatedConstants.deleted) {
62 constantsHash[type][deleted.key] = deleted.label
63 }
64
65 delete this.updatedVideoConstants[type][npmName]
66 }
67
68 public createVideoConstantManager<K extends number | string>(type: AlterableVideoConstant): ConstantManager<K> {
69 const { npmName } = this
70 return {
71 addConstant: (key: K, label: string) => this.addConstant({ npmName, type, key, label }),
72 deleteConstant: (key: K) => this.deleteConstant({ npmName, type, key }),
73 getConstantValue: (key: K) => constantsHash[type][key],
74 getConstants: () => constantsHash[type] as Record<K, string>,
75 resetConstants: () => this.resetConstants({ npmName, type })
76 }
77 }
78
79 private addConstant<T extends string | number> (parameters: {
80 npmName: string
81 type: AlterableVideoConstant
82 key: T
83 label: string
84 }) {
85 const { npmName, type, key, label } = parameters
86 const obj = constantsHash[type]
87
88 if (obj[key]) {
89 logger.warn('Cannot add %s %s by plugin %s: key already exists.', type, npmName, key)
90 return false
91 }
92
93 if (!this.updatedVideoConstants[type][npmName]) {
94 this.updatedVideoConstants[type][npmName] = {
95 added: [],
96 deleted: []
97 }
98 }
99
100 this.updatedVideoConstants[type][npmName].added.push({ key: key, label } as VideoConstant)
101 obj[key] = label
102
103 return true
104 }
105
106 private deleteConstant<T extends string | number> (parameters: {
107 npmName: string
108 type: AlterableVideoConstant
109 key: T
110 }) {
111 const { npmName, type, key } = parameters
112 const obj = constantsHash[type]
113
114 if (!obj[key]) {
115 logger.warn('Cannot delete %s by plugin %s: key %s does not exist.', type, npmName, key)
116 return false
117 }
118
119 if (!this.updatedVideoConstants[type][npmName]) {
120 this.updatedVideoConstants[type][npmName] = {
121 added: [],
122 deleted: []
123 }
124 }
125
126 const updatedConstants = this.updatedVideoConstants[type][npmName]
127
128 const alreadyAdded = updatedConstants.added.find(a => a.key === key)
129 if (alreadyAdded) {
130 updatedConstants.added.filter(a => a.key !== key)
131 } else if (obj[key]) {
132 updatedConstants.deleted.push({ key, label: obj[key] } as VideoConstant)
133 }
134
135 delete obj[key]
136
137 return true
138 }
139}
diff --git a/server/lib/schedulers/plugins-check-scheduler.ts b/server/lib/schedulers/plugins-check-scheduler.ts
index 9a1ae3ec5..c95e109b0 100644
--- a/server/lib/schedulers/plugins-check-scheduler.ts
+++ b/server/lib/schedulers/plugins-check-scheduler.ts
@@ -1,12 +1,12 @@
1import { chunk } from 'lodash'
2import { compareSemVer } from '@shared/core-utils'
1import { logger } from '../../helpers/logger' 3import { logger } from '../../helpers/logger'
2import { AbstractScheduler } from './abstract-scheduler'
3import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
4import { CONFIG } from '../../initializers/config' 4import { CONFIG } from '../../initializers/config'
5import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
5import { PluginModel } from '../../models/server/plugin' 6import { PluginModel } from '../../models/server/plugin'
6import { chunk } from 'lodash'
7import { getLatestPluginsVersion } from '../plugins/plugin-index'
8import { compareSemVer } from '../../../shared/core-utils/miscs/miscs'
9import { Notifier } from '../notifier' 7import { Notifier } from '../notifier'
8import { getLatestPluginsVersion } from '../plugins/plugin-index'
9import { AbstractScheduler } from './abstract-scheduler'
10 10
11export class PluginsCheckScheduler extends AbstractScheduler { 11export class PluginsCheckScheduler extends AbstractScheduler {
12 12
diff --git a/server/lib/schedulers/videos-redundancy-scheduler.ts b/server/lib/schedulers/videos-redundancy-scheduler.ts
index b5a5eb697..103ab1fab 100644
--- a/server/lib/schedulers/videos-redundancy-scheduler.ts
+++ b/server/lib/schedulers/videos-redundancy-scheduler.ts
@@ -267,7 +267,8 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
267 logger.info('Duplicating %s streaming playlist in videos redundancy with "%s" strategy.', video.url, strategy) 267 logger.info('Duplicating %s streaming playlist in videos redundancy with "%s" strategy.', video.url, strategy)
268 268
269 const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid) 269 const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid)
270 await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT) 270 const masterPlaylistUrl = playlist.getMasterPlaylistUrl(video)
271 await downloadPlaylistSegments(masterPlaylistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT)
271 272
272 const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({ 273 const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({
273 expiresOn, 274 expiresOn,
@@ -282,7 +283,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
282 283
283 await sendCreateCacheFile(serverActor, video, createdModel) 284 await sendCreateCacheFile(serverActor, video, createdModel)
284 285
285 logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url) 286 logger.info('Duplicated playlist %s -> %s.', masterPlaylistUrl, createdModel.url)
286 } 287 }
287 288
288 private async extendsExpirationOf (redundancy: MVideoRedundancyVideo, expiresAfterMs: number) { 289 private async extendsExpirationOf (redundancy: MVideoRedundancyVideo, expiresAfterMs: number) {
@@ -330,7 +331,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
330 private buildEntryLogId (object: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo) { 331 private buildEntryLogId (object: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo) {
331 if (isMVideoRedundancyFileVideo(object)) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}` 332 if (isMVideoRedundancyFileVideo(object)) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}`
332 333
333 return `${object.VideoStreamingPlaylist.playlistUrl}` 334 return `${object.VideoStreamingPlaylist.getMasterPlaylistUrl(object.VideoStreamingPlaylist.Video)}`
334 } 335 }
335 336
336 private getTotalFileSizes (files: MVideoFile[], playlists: MStreamingPlaylistFiles[]) { 337 private getTotalFileSizes (files: MVideoFile[], playlists: MStreamingPlaylistFiles[]) {
diff --git a/server/lib/transcoding/video-transcoding.ts b/server/lib/transcoding/video-transcoding.ts
index 1ad63baf3..d2a556360 100644
--- a/server/lib/transcoding/video-transcoding.ts
+++ b/server/lib/transcoding/video-transcoding.ts
@@ -10,11 +10,18 @@ import { transcode, TranscodeOptions, TranscodeOptionsType } from '../../helpers
10import { canDoQuickTranscode, getDurationFromVideoFile, getMetadataFromFile, getVideoFileFPS } from '../../helpers/ffprobe-utils' 10import { canDoQuickTranscode, getDurationFromVideoFile, getMetadataFromFile, getVideoFileFPS } from '../../helpers/ffprobe-utils'
11import { logger } from '../../helpers/logger' 11import { logger } from '../../helpers/logger'
12import { CONFIG } from '../../initializers/config' 12import { CONFIG } from '../../initializers/config'
13import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../../initializers/constants' 13import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../../initializers/constants'
14import { VideoFileModel } from '../../models/video/video-file' 14import { VideoFileModel } from '../../models/video/video-file'
15import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' 15import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist'
16import { updateMasterHLSPlaylist, updateSha256VODSegments } from '../hls' 16import { updateMasterHLSPlaylist, updateSha256VODSegments } from '../hls'
17import { generateVideoFilename, generateVideoStreamingPlaylistName, getVideoFilePath } from '../video-paths' 17import {
18 generateHLSMasterPlaylistFilename,
19 generateHlsSha256SegmentsFilename,
20 generateHLSVideoFilename,
21 generateWebTorrentVideoFilename,
22 getHlsResolutionPlaylistFilename,
23 getVideoFilePath
24} from '../video-paths'
18import { VideoTranscodingProfilesManager } from './video-transcoding-profiles' 25import { VideoTranscodingProfilesManager } from './video-transcoding-profiles'
19 26
20/** 27/**
@@ -60,7 +67,7 @@ async function optimizeOriginalVideofile (video: MVideoFullLight, inputVideoFile
60 67
61 // Important to do this before getVideoFilename() to take in account the new filename 68 // Important to do this before getVideoFilename() to take in account the new filename
62 inputVideoFile.extname = newExtname 69 inputVideoFile.extname = newExtname
63 inputVideoFile.filename = generateVideoFilename(video, false, resolution, newExtname) 70 inputVideoFile.filename = generateWebTorrentVideoFilename(resolution, newExtname)
64 71
65 const videoOutputPath = getVideoFilePath(video, inputVideoFile) 72 const videoOutputPath = getVideoFilePath(video, inputVideoFile)
66 73
@@ -86,7 +93,7 @@ async function transcodeNewWebTorrentResolution (video: MVideoFullLight, resolut
86 const newVideoFile = new VideoFileModel({ 93 const newVideoFile = new VideoFileModel({
87 resolution, 94 resolution,
88 extname, 95 extname,
89 filename: generateVideoFilename(video, false, resolution, extname), 96 filename: generateWebTorrentVideoFilename(resolution, extname),
90 size: 0, 97 size: 0,
91 videoId: video.id 98 videoId: video.id
92 }) 99 })
@@ -169,7 +176,7 @@ async function mergeAudioVideofile (video: MVideoFullLight, resolution: VideoRes
169 176
170 // Important to do this before getVideoFilename() to take in account the new file extension 177 // Important to do this before getVideoFilename() to take in account the new file extension
171 inputVideoFile.extname = newExtname 178 inputVideoFile.extname = newExtname
172 inputVideoFile.filename = generateVideoFilename(video, false, inputVideoFile.resolution, newExtname) 179 inputVideoFile.filename = generateWebTorrentVideoFilename(inputVideoFile.resolution, newExtname)
173 180
174 const videoOutputPath = getVideoFilePath(video, inputVideoFile) 181 const videoOutputPath = getVideoFilePath(video, inputVideoFile)
175 // ffmpeg generated a new video file, so update the video duration 182 // ffmpeg generated a new video file, so update the video duration
@@ -271,15 +278,15 @@ async function generateHlsPlaylistCommon (options: {
271 const videoTranscodedBasePath = join(transcodeDirectory, type) 278 const videoTranscodedBasePath = join(transcodeDirectory, type)
272 await ensureDir(videoTranscodedBasePath) 279 await ensureDir(videoTranscodedBasePath)
273 280
274 const videoFilename = generateVideoStreamingPlaylistName(video.uuid, resolution) 281 const videoFilename = generateHLSVideoFilename(resolution)
275 const playlistFilename = VideoStreamingPlaylistModel.getHlsPlaylistFilename(resolution) 282 const resolutionPlaylistFilename = getHlsResolutionPlaylistFilename(videoFilename)
276 const playlistFileTranscodePath = join(videoTranscodedBasePath, playlistFilename) 283 const resolutionPlaylistFileTranscodePath = join(videoTranscodedBasePath, resolutionPlaylistFilename)
277 284
278 const transcodeOptions = { 285 const transcodeOptions = {
279 type, 286 type,
280 287
281 inputPath, 288 inputPath,
282 outputPath: playlistFileTranscodePath, 289 outputPath: resolutionPlaylistFileTranscodePath,
283 290
284 availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), 291 availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(),
285 profile: CONFIG.TRANSCODING.PROFILE, 292 profile: CONFIG.TRANSCODING.PROFILE,
@@ -299,19 +306,23 @@ async function generateHlsPlaylistCommon (options: {
299 306
300 await transcode(transcodeOptions) 307 await transcode(transcodeOptions)
301 308
302 const playlistUrl = WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsMasterPlaylistStaticPath(video.uuid)
303
304 // Create or update the playlist 309 // Create or update the playlist
305 const [ videoStreamingPlaylist ] = await VideoStreamingPlaylistModel.upsert({ 310 const playlist = await VideoStreamingPlaylistModel.loadOrGenerate(video)
306 videoId: video.id, 311
307 playlistUrl, 312 if (!playlist.playlistFilename) {
308 segmentsSha256Url: WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsSha256SegmentsStaticPath(video.uuid, video.isLive), 313 playlist.playlistFilename = generateHLSMasterPlaylistFilename(video.isLive)
309 p2pMediaLoaderInfohashes: [], 314 }
310 p2pMediaLoaderPeerVersion: P2P_MEDIA_LOADER_PEER_VERSION, 315
316 if (!playlist.segmentsSha256Filename) {
317 playlist.segmentsSha256Filename = generateHlsSha256SegmentsFilename(video.isLive)
318 }
319
320 playlist.p2pMediaLoaderInfohashes = []
321 playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION
311 322
312 type: VideoStreamingPlaylistType.HLS 323 playlist.type = VideoStreamingPlaylistType.HLS
313 }, { returning: true }) as [ MStreamingPlaylistFilesVideo, boolean ] 324
314 videoStreamingPlaylist.Video = video 325 await playlist.save()
315 326
316 // Build the new playlist file 327 // Build the new playlist file
317 const extname = extnameUtil(videoFilename) 328 const extname = extnameUtil(videoFilename)
@@ -319,20 +330,20 @@ async function generateHlsPlaylistCommon (options: {
319 resolution, 330 resolution,
320 extname, 331 extname,
321 size: 0, 332 size: 0,
322 filename: generateVideoFilename(video, true, resolution, extname), 333 filename: videoFilename,
323 fps: -1, 334 fps: -1,
324 videoStreamingPlaylistId: videoStreamingPlaylist.id 335 videoStreamingPlaylistId: playlist.id
325 }) 336 })
326 337
327 const videoFilePath = getVideoFilePath(videoStreamingPlaylist, newVideoFile) 338 const videoFilePath = getVideoFilePath(playlist, newVideoFile)
328 339
329 // Move files from tmp transcoded directory to the appropriate place 340 // Move files from tmp transcoded directory to the appropriate place
330 const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) 341 const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
331 await ensureDir(baseHlsDirectory) 342 await ensureDir(baseHlsDirectory)
332 343
333 // Move playlist file 344 // Move playlist file
334 const playlistPath = join(baseHlsDirectory, playlistFilename) 345 const resolutionPlaylistPath = join(baseHlsDirectory, resolutionPlaylistFilename)
335 await move(playlistFileTranscodePath, playlistPath, { overwrite: true }) 346 await move(resolutionPlaylistFileTranscodePath, resolutionPlaylistPath, { overwrite: true })
336 // Move video file 347 // Move video file
337 await move(join(videoTranscodedBasePath, videoFilename), videoFilePath, { overwrite: true }) 348 await move(join(videoTranscodedBasePath, videoFilename), videoFilePath, { overwrite: true })
338 349
@@ -342,20 +353,20 @@ async function generateHlsPlaylistCommon (options: {
342 newVideoFile.fps = await getVideoFileFPS(videoFilePath) 353 newVideoFile.fps = await getVideoFileFPS(videoFilePath)
343 newVideoFile.metadata = await getMetadataFromFile(videoFilePath) 354 newVideoFile.metadata = await getMetadataFromFile(videoFilePath)
344 355
345 await createTorrentAndSetInfoHash(videoStreamingPlaylist, newVideoFile) 356 await createTorrentAndSetInfoHash(playlist, newVideoFile)
346 357
347 await VideoFileModel.customUpsert(newVideoFile, 'streaming-playlist', undefined) 358 await VideoFileModel.customUpsert(newVideoFile, 'streaming-playlist', undefined)
348 videoStreamingPlaylist.VideoFiles = await videoStreamingPlaylist.$get('VideoFiles')
349 359
350 videoStreamingPlaylist.p2pMediaLoaderInfohashes = VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes( 360 const playlistWithFiles = playlist as MStreamingPlaylistFilesVideo
351 playlistUrl, videoStreamingPlaylist.VideoFiles 361 playlistWithFiles.VideoFiles = await playlist.$get('VideoFiles')
352 ) 362 playlist.assignP2PMediaLoaderInfoHashes(video, playlistWithFiles.VideoFiles)
353 await videoStreamingPlaylist.save() 363
364 await playlist.save()
354 365
355 video.setHLSPlaylist(videoStreamingPlaylist) 366 video.setHLSPlaylist(playlist)
356 367
357 await updateMasterHLSPlaylist(video) 368 await updateMasterHLSPlaylist(video, playlistWithFiles)
358 await updateSha256VODSegments(video) 369 await updateSha256VODSegments(video, playlistWithFiles)
359 370
360 return playlistPath 371 return resolutionPlaylistPath
361} 372}
diff --git a/server/lib/video-paths.ts b/server/lib/video-paths.ts
index 1708c479a..1e4382108 100644
--- a/server/lib/video-paths.ts
+++ b/server/lib/video-paths.ts
@@ -3,29 +3,17 @@ import { extractVideo } from '@server/helpers/video'
3import { CONFIG } from '@server/initializers/config' 3import { CONFIG } from '@server/initializers/config'
4import { HLS_REDUNDANCY_DIRECTORY, HLS_STREAMING_PLAYLIST_DIRECTORY, STATIC_PATHS, WEBSERVER } from '@server/initializers/constants' 4import { HLS_REDUNDANCY_DIRECTORY, HLS_STREAMING_PLAYLIST_DIRECTORY, STATIC_PATHS, WEBSERVER } from '@server/initializers/constants'
5import { isStreamingPlaylist, MStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoUUID } from '@server/types/models' 5import { isStreamingPlaylist, MStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoUUID } from '@server/types/models'
6import { buildUUID } from '@server/helpers/uuid'
7import { removeFragmentedMP4Ext } from '@shared/core-utils'
6 8
7// ################## Video file name ################## 9// ################## Video file name ##################
8 10
9function generateVideoFilename (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, isHls: boolean, resolution: number, extname: string) { 11function generateWebTorrentVideoFilename (resolution: number, extname: string) {
10 const video = extractVideo(videoOrPlaylist) 12 return buildUUID() + '-' + resolution + extname
11
12 // FIXME: use a generated uuid instead, that will break compatibility with PeerTube < 3.1
13 // const uuid = uuidv4()
14 const uuid = video.uuid
15
16 if (isHls) {
17 return generateVideoStreamingPlaylistName(uuid, resolution)
18 }
19
20 return generateWebTorrentVideoName(uuid, resolution, extname)
21} 13}
22 14
23function generateVideoStreamingPlaylistName (uuid: string, resolution: number) { 15function generateHLSVideoFilename (resolution: number) {
24 return `${uuid}-${resolution}-fragmented.mp4` 16 return `${buildUUID()}-${resolution}-fragmented.mp4`
25}
26
27function generateWebTorrentVideoName (uuid: string, resolution: number, extname: string) {
28 return uuid + '-' + resolution + extname
29} 17}
30 18
31function getVideoFilePath (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, videoFile: MVideoFile, isRedundancy = false) { 19function getVideoFilePath (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, videoFile: MVideoFile, isRedundancy = false) {
@@ -63,15 +51,28 @@ function getHLSDirectory (video: MVideoUUID, isRedundancy = false) {
63 return join(baseDir, video.uuid) 51 return join(baseDir, video.uuid)
64} 52}
65 53
54function getHlsResolutionPlaylistFilename (videoFilename: string) {
55 // Video file name already contain resolution
56 return removeFragmentedMP4Ext(videoFilename) + '.m3u8'
57}
58
59function generateHLSMasterPlaylistFilename (isLive = false) {
60 if (isLive) return 'master.m3u8'
61
62 return buildUUID() + '-master.m3u8'
63}
64
65function generateHlsSha256SegmentsFilename (isLive = false) {
66 if (isLive) return 'segments-sha256.json'
67
68 return buildUUID() + '-segments-sha256.json'
69}
70
66// ################## Torrents ################## 71// ################## Torrents ##################
67 72
68function generateTorrentFileName (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, resolution: number) { 73function generateTorrentFileName (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, resolution: number) {
69 const video = extractVideo(videoOrPlaylist)
70 const extension = '.torrent' 74 const extension = '.torrent'
71 75 const uuid = buildUUID()
72 // FIXME: use a generated uuid instead, that will break compatibility with PeerTube < 3.1
73 // const uuid = uuidv4()
74 const uuid = video.uuid
75 76
76 if (isStreamingPlaylist(videoOrPlaylist)) { 77 if (isStreamingPlaylist(videoOrPlaylist)) {
77 return `${uuid}-${resolution}-${videoOrPlaylist.getStringType()}${extension}` 78 return `${uuid}-${resolution}-${videoOrPlaylist.getStringType()}${extension}`
@@ -95,15 +96,18 @@ function getLocalVideoFileMetadataUrl (video: MVideoUUID, videoFile: MVideoFile)
95// --------------------------------------------------------------------------- 96// ---------------------------------------------------------------------------
96 97
97export { 98export {
98 generateVideoStreamingPlaylistName, 99 generateHLSVideoFilename,
99 generateWebTorrentVideoName, 100 generateWebTorrentVideoFilename,
100 generateVideoFilename, 101
101 getVideoFilePath, 102 getVideoFilePath,
102 103
103 generateTorrentFileName, 104 generateTorrentFileName,
104 getTorrentFilePath, 105 getTorrentFilePath,
105 106
106 getHLSDirectory, 107 getHLSDirectory,
108 generateHLSMasterPlaylistFilename,
109 generateHlsSha256SegmentsFilename,
110 getHlsResolutionPlaylistFilename,
107 111
108 getLocalVideoFileMetadataUrl, 112 getLocalVideoFileMetadataUrl,
109 113
diff --git a/server/lib/video.ts b/server/lib/video.ts
index daf998704..61fee4949 100644
--- a/server/lib/video.ts
+++ b/server/lib/video.ts
@@ -5,7 +5,7 @@ import { sequelizeTypescript } from '@server/initializers/database'
5import { TagModel } from '@server/models/video/tag' 5import { TagModel } from '@server/models/video/tag'
6import { VideoModel } from '@server/models/video/video' 6import { VideoModel } from '@server/models/video/video'
7import { FilteredModelAttributes } from '@server/types' 7import { FilteredModelAttributes } from '@server/types'
8import { MThumbnail, MUserId, MVideo, MVideoFile, MVideoTag, MVideoThumbnail, MVideoUUID } from '@server/types/models' 8import { MThumbnail, MUserId, MVideoFile, MVideoTag, MVideoThumbnail, MVideoUUID } from '@server/types/models'
9import { ThumbnailType, VideoCreate, VideoPrivacy, VideoTranscodingPayload } from '@shared/models' 9import { ThumbnailType, VideoCreate, VideoPrivacy, VideoTranscodingPayload } from '@shared/models'
10import { federateVideoIfNeeded } from './activitypub/videos' 10import { federateVideoIfNeeded } from './activitypub/videos'
11import { JobQueue } from './job-queue/job-queue' 11import { JobQueue } from './job-queue/job-queue'
@@ -105,7 +105,7 @@ async function publishAndFederateIfNeeded (video: MVideoUUID, wasLive = false) {
105 } 105 }
106} 106}
107 107
108async function addOptimizeOrMergeAudioJob (video: MVideo, videoFile: MVideoFile, user: MUserId) { 108async function addOptimizeOrMergeAudioJob (video: MVideoUUID, videoFile: MVideoFile, user: MUserId) {
109 let dataInput: VideoTranscodingPayload 109 let dataInput: VideoTranscodingPayload
110 110
111 if (videoFile.isAudio()) { 111 if (videoFile.isAudio()) {
diff --git a/server/middlewares/activitypub.ts b/server/middlewares/activitypub.ts
index 6b43b7764..6ef90b275 100644
--- a/server/middlewares/activitypub.ts
+++ b/server/middlewares/activitypub.ts
@@ -2,7 +2,7 @@ import { NextFunction, Request, Response } from 'express'
2import { getAPId } from '@server/helpers/activitypub' 2import { getAPId } from '@server/helpers/activitypub'
3import { isActorDeleteActivityValid } from '@server/helpers/custom-validators/activitypub/actor' 3import { isActorDeleteActivityValid } from '@server/helpers/custom-validators/activitypub/actor'
4import { ActivityDelete, ActivityPubSignature } from '../../shared' 4import { ActivityDelete, ActivityPubSignature } from '../../shared'
5import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 5import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
6import { logger } from '../helpers/logger' 6import { logger } from '../helpers/logger'
7import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../helpers/peertube-crypto' 7import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../helpers/peertube-crypto'
8import { ACCEPT_HEADERS, ACTIVITY_PUB, HTTP_SIGNATURE } from '../initializers/constants' 8import { ACCEPT_HEADERS, ACTIVITY_PUB, HTTP_SIGNATURE } from '../initializers/constants'
diff --git a/server/middlewares/auth.ts b/server/middlewares/auth.ts
index 176461cc2..9e6327b23 100644
--- a/server/middlewares/auth.ts
+++ b/server/middlewares/auth.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { Socket } from 'socket.io' 2import { Socket } from 'socket.io'
3import { getAccessToken } from '@server/lib/auth/oauth-model' 3import { getAccessToken } from '@server/lib/auth/oauth-model'
4import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
5import { logger } from '../helpers/logger' 5import { logger } from '../helpers/logger'
6import { handleOAuthAuthenticate } from '../lib/auth/oauth' 6import { handleOAuthAuthenticate } from '../lib/auth/oauth'
7 7
diff --git a/server/middlewares/cache.ts b/server/middlewares/cache.ts
deleted file mode 100644
index 0708ee8e8..000000000
--- a/server/middlewares/cache.ts
+++ /dev/null
@@ -1,28 +0,0 @@
1import { Redis } from '../lib/redis'
2import * as apicache from 'apicache'
3import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
4
5// Ensure Redis is initialized
6Redis.Instance.init()
7
8const defaultOptions = {
9 redisClient: Redis.Instance.getClient(),
10 appendKey: () => Redis.Instance.getPrefix(),
11 statusCodes: {
12 exclude: [
13 HttpStatusCode.FORBIDDEN_403,
14 HttpStatusCode.NOT_FOUND_404
15 ]
16 }
17}
18
19const cacheRoute = (extraOptions = {}) => apicache.options({
20 ...defaultOptions,
21 ...extraOptions
22}).middleware
23
24// ---------------------------------------------------------------------------
25
26export {
27 cacheRoute
28}
diff --git a/server/middlewares/cache/cache.ts b/server/middlewares/cache/cache.ts
new file mode 100644
index 000000000..48162a0ae
--- /dev/null
+++ b/server/middlewares/cache/cache.ts
@@ -0,0 +1,32 @@
1import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
2import { Redis } from '../../lib/redis'
3import { ApiCache, APICacheOptions } from './shared'
4
5// Ensure Redis is initialized
6Redis.Instance.init()
7
8const defaultOptions: APICacheOptions = {
9 excludeStatus: [
10 HttpStatusCode.FORBIDDEN_403,
11 HttpStatusCode.NOT_FOUND_404
12 ]
13}
14
15function cacheRoute (duration: string) {
16 const instance = new ApiCache(defaultOptions)
17
18 return instance.buildMiddleware(duration)
19}
20
21function cacheRouteFactory (options: APICacheOptions) {
22 const instance = new ApiCache({ ...defaultOptions, ...options })
23
24 return instance.buildMiddleware.bind(instance)
25}
26
27// ---------------------------------------------------------------------------
28
29export {
30 cacheRoute,
31 cacheRouteFactory
32}
diff --git a/server/middlewares/cache/index.ts b/server/middlewares/cache/index.ts
new file mode 100644
index 000000000..79b512828
--- /dev/null
+++ b/server/middlewares/cache/index.ts
@@ -0,0 +1 @@
export * from './cache'
diff --git a/server/middlewares/cache/shared/api-cache.ts b/server/middlewares/cache/shared/api-cache.ts
new file mode 100644
index 000000000..f9f7b1b67
--- /dev/null
+++ b/server/middlewares/cache/shared/api-cache.ts
@@ -0,0 +1,269 @@
1// Thanks: https://github.com/kwhitley/apicache
2// We duplicated the library because it is unmaintened and prevent us to upgrade to recent NodeJS versions
3
4import * as express from 'express'
5import { OutgoingHttpHeaders } from 'http'
6import { isTestInstance, parseDurationToMs } from '@server/helpers/core-utils'
7import { logger } from '@server/helpers/logger'
8import { Redis } from '@server/lib/redis'
9import { HttpStatusCode } from '@shared/models'
10
11export interface APICacheOptions {
12 headerBlacklist?: string[]
13 excludeStatus?: HttpStatusCode[]
14}
15
16interface CacheObject {
17 status: number
18 headers: OutgoingHttpHeaders
19 data: any
20 encoding: BufferEncoding
21 timestamp: number
22}
23
24export class ApiCache {
25
26 private readonly options: APICacheOptions
27 private readonly timers: { [ id: string ]: NodeJS.Timeout } = {}
28
29 private index: { all: string[] } = { all: [] }
30
31 constructor (options: APICacheOptions) {
32 this.options = {
33 headerBlacklist: [],
34 excludeStatus: [],
35
36 ...options
37 }
38 }
39
40 buildMiddleware (strDuration: string) {
41 const duration = parseDurationToMs(strDuration)
42
43 return (req: express.Request, res: express.Response, next: express.NextFunction) => {
44 const key = Redis.Instance.getPrefix() + 'api-cache-' + req.originalUrl
45 const redis = Redis.Instance.getClient()
46
47 if (!redis.connected) return this.makeResponseCacheable(res, next, key, duration)
48
49 try {
50 redis.hgetall(key, (err, obj) => {
51 if (!err && obj && obj.response) {
52 return this.sendCachedResponse(req, res, JSON.parse(obj.response), duration)
53 }
54
55 return this.makeResponseCacheable(res, next, key, duration)
56 })
57 } catch (err) {
58 return this.makeResponseCacheable(res, next, key, duration)
59 }
60 }
61 }
62
63 private shouldCacheResponse (response: express.Response) {
64 if (!response) return false
65 if (this.options.excludeStatus.includes(response.statusCode)) return false
66
67 return true
68 }
69
70 private addIndexEntries (key: string) {
71 this.index.all.unshift(key)
72 }
73
74 private filterBlacklistedHeaders (headers: OutgoingHttpHeaders) {
75 return Object.keys(headers)
76 .filter(key => !this.options.headerBlacklist.includes(key))
77 .reduce((acc, header) => {
78 acc[header] = headers[header]
79
80 return acc
81 }, {})
82 }
83
84 private createCacheObject (status: number, headers: OutgoingHttpHeaders, data: any, encoding: BufferEncoding) {
85 return {
86 status,
87 headers: this.filterBlacklistedHeaders(headers),
88 data,
89 encoding,
90
91 // Seconds since epoch, used to properly decrement max-age headers in cached responses.
92 timestamp: new Date().getTime() / 1000
93 } as CacheObject
94 }
95
96 private cacheResponse (key: string, value: object, duration: number) {
97 const redis = Redis.Instance.getClient()
98
99 if (redis.connected) {
100 try {
101 redis.hset(key, 'response', JSON.stringify(value))
102 redis.hset(key, 'duration', duration + '')
103 redis.expire(key, duration / 1000)
104 } catch (err) {
105 logger.error('Cannot set cache in redis.', { err })
106 }
107 }
108
109 // add automatic cache clearing from duration, includes max limit on setTimeout
110 this.timers[key] = setTimeout(() => this.clear(key), Math.min(duration, 2147483647))
111 }
112
113 private accumulateContent (res: express.Response, content: any) {
114 if (!content) return
115
116 if (typeof content === 'string') {
117 res.locals.apicache.content = (res.locals.apicache.content || '') + content
118 return
119 }
120
121 if (Buffer.isBuffer(content)) {
122 let oldContent = res.locals.apicache.content
123
124 if (typeof oldContent === 'string') {
125 oldContent = Buffer.from(oldContent)
126 }
127
128 if (!oldContent) {
129 oldContent = Buffer.alloc(0)
130 }
131
132 res.locals.apicache.content = Buffer.concat(
133 [ oldContent, content ],
134 oldContent.length + content.length
135 )
136
137 return
138 }
139
140 res.locals.apicache.content = content
141 }
142
143 private makeResponseCacheable (res: express.Response, next: express.NextFunction, key: string, duration: number) {
144 const self = this
145
146 res.locals.apicache = {
147 write: res.write,
148 writeHead: res.writeHead,
149 end: res.end,
150 cacheable: true,
151 content: undefined,
152 headers: {}
153 }
154
155 // Patch express
156 res.writeHead = function () {
157 if (self.shouldCacheResponse(res)) {
158 res.setHeader('cache-control', 'max-age=' + (duration / 1000).toFixed(0))
159 } else {
160 res.setHeader('cache-control', 'no-cache, no-store, must-revalidate')
161 }
162
163 res.locals.apicache.headers = Object.assign({}, res.getHeaders())
164 return res.locals.apicache.writeHead.apply(this, arguments as any)
165 }
166
167 res.write = function (chunk: any) {
168 self.accumulateContent(res, chunk)
169 return res.locals.apicache.write.apply(this, arguments as any)
170 }
171
172 res.end = function (content: any, encoding: BufferEncoding) {
173 if (self.shouldCacheResponse(res)) {
174 self.accumulateContent(res, content)
175
176 if (res.locals.apicache.cacheable && res.locals.apicache.content) {
177 self.addIndexEntries(key)
178
179 const headers = res.locals.apicache.headers || res.getHeaders()
180 const cacheObject = self.createCacheObject(
181 res.statusCode,
182 headers,
183 res.locals.apicache.content,
184 encoding
185 )
186 self.cacheResponse(key, cacheObject, duration)
187 }
188 }
189
190 res.locals.apicache.end.apply(this, arguments as any)
191 } as any
192
193 next()
194 }
195
196 private sendCachedResponse (request: express.Request, response: express.Response, cacheObject: CacheObject, duration: number) {
197 const headers = response.getHeaders()
198
199 if (isTestInstance()) {
200 Object.assign(headers, {
201 'x-api-cache-cached': 'true'
202 })
203 }
204
205 Object.assign(headers, this.filterBlacklistedHeaders(cacheObject.headers || {}), {
206 // Set properly decremented max-age header
207 // This ensures that max-age is in sync with the cache expiration
208 'cache-control':
209 'max-age=' +
210 Math.max(
211 0,
212 (duration / 1000 - (new Date().getTime() / 1000 - cacheObject.timestamp))
213 ).toFixed(0)
214 })
215
216 // unstringify buffers
217 let data = cacheObject.data
218 if (data && data.type === 'Buffer') {
219 data = typeof data.data === 'number'
220 ? Buffer.alloc(data.data)
221 : Buffer.from(data.data)
222 }
223
224 // Test Etag against If-None-Match for 304
225 const cachedEtag = cacheObject.headers.etag
226 const requestEtag = request.headers['if-none-match']
227
228 if (requestEtag && cachedEtag === requestEtag) {
229 response.writeHead(304, headers)
230 return response.end()
231 }
232
233 response.writeHead(cacheObject.status || 200, headers)
234
235 return response.end(data, cacheObject.encoding)
236 }
237
238 private clear (target: string) {
239 const redis = Redis.Instance.getClient()
240
241 if (target) {
242 clearTimeout(this.timers[target])
243 delete this.timers[target]
244
245 try {
246 redis.del(target)
247 } catch (err) {
248 logger.error('Cannot delete %s in redis cache.', target, { err })
249 }
250
251 this.index.all = this.index.all.filter(key => key !== target)
252 } else {
253 for (const key of this.index.all) {
254 clearTimeout(this.timers[key])
255 delete this.timers[key]
256
257 try {
258 redis.del(key)
259 } catch (err) {
260 logger.error('Cannot delete %s in redis cache.', key, { err })
261 }
262 }
263
264 this.index.all = []
265 }
266
267 return this.index
268 }
269}
diff --git a/server/middlewares/cache/shared/index.ts b/server/middlewares/cache/shared/index.ts
new file mode 100644
index 000000000..c707eaf7a
--- /dev/null
+++ b/server/middlewares/cache/shared/index.ts
@@ -0,0 +1 @@
export * from './api-cache'
diff --git a/server/middlewares/error.ts b/server/middlewares/error.ts
index e3eb1c8f5..af5a9c29a 100644
--- a/server/middlewares/error.ts
+++ b/server/middlewares/error.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { ProblemDocument, ProblemDocumentExtension } from 'http-problem-details' 2import { ProblemDocument, ProblemDocumentExtension } from 'http-problem-details'
3import { HttpStatusCode } from '@shared/core-utils' 3import { HttpStatusCode } from '@shared/models'
4 4
5function apiFailMiddleware (req: express.Request, res: express.Response, next: express.NextFunction) { 5function apiFailMiddleware (req: express.Request, res: express.Response, next: express.NextFunction) {
6 res.fail = options => { 6 res.fail = options => {
diff --git a/server/middlewares/index.ts b/server/middlewares/index.ts
index 413653dac..a0035f623 100644
--- a/server/middlewares/index.ts
+++ b/server/middlewares/index.ts
@@ -1,4 +1,5 @@
1export * from './validators' 1export * from './validators'
2export * from './cache'
2export * from './activitypub' 3export * from './activitypub'
3export * from './async' 4export * from './async'
4export * from './auth' 5export * from './auth'
diff --git a/server/middlewares/servers.ts b/server/middlewares/servers.ts
index 9aa56bc93..cf70d901e 100644
--- a/server/middlewares/servers.ts
+++ b/server/middlewares/servers.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
2import { getHostWithPort } from '../helpers/express-utils' 3import { getHostWithPort } from '../helpers/express-utils'
3import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
4 4
5function setBodyHostsPort (req: express.Request, res: express.Response, next: express.NextFunction) { 5function setBodyHostsPort (req: express.Request, res: express.Response, next: express.NextFunction) {
6 if (!req.body.hosts) return next() 6 if (!req.body.hosts) return next()
diff --git a/server/middlewares/user-right.ts b/server/middlewares/user-right.ts
index d1888c2d3..c8c694f05 100644
--- a/server/middlewares/user-right.ts
+++ b/server/middlewares/user-right.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { UserRight } from '../../shared' 2import { UserRight } from '../../shared'
3import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
3import { logger } from '../helpers/logger' 4import { logger } from '../helpers/logger'
4import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
5 5
6function ensureUserHasRight (userRight: UserRight) { 6function ensureUserHasRight (userRight: UserRight) {
7 return function (req: express.Request, res: express.Response, next: express.NextFunction) { 7 return function (req: express.Request, res: express.Response, next: express.NextFunction) {
diff --git a/server/middlewares/validators/abuse.ts b/server/middlewares/validators/abuse.ts
index c048bc6af..f4d9c3af2 100644
--- a/server/middlewares/validators/abuse.ts
+++ b/server/middlewares/validators/abuse.ts
@@ -16,7 +16,7 @@ import { exists, isIdOrUUIDValid, isIdValid, toCompleteUUID, toIntOrNull } from
16import { logger } from '@server/helpers/logger' 16import { logger } from '@server/helpers/logger'
17import { AbuseMessageModel } from '@server/models/abuse/abuse-message' 17import { AbuseMessageModel } from '@server/models/abuse/abuse-message'
18import { AbuseCreate, UserRight } from '@shared/models' 18import { AbuseCreate, UserRight } from '@shared/models'
19import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 19import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
20import { areValidationErrors, doesAbuseExist, doesAccountIdExist, doesCommentIdExist, doesVideoExist } from './shared' 20import { areValidationErrors, doesAbuseExist, doesAccountIdExist, doesCommentIdExist, doesVideoExist } from './shared'
21 21
22const abuseReportValidator = [ 22const abuseReportValidator = [
diff --git a/server/middlewares/validators/activitypub/activity.ts b/server/middlewares/validators/activitypub/activity.ts
index cc6acd4b1..d24e4427b 100644
--- a/server/middlewares/validators/activitypub/activity.ts
+++ b/server/middlewares/validators/activitypub/activity.ts
@@ -1,8 +1,8 @@
1import * as express from 'express' 1import * as express from 'express'
2import { getServerActor } from '@server/models/application/application'
3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
2import { isRootActivityValid } from '../../../helpers/custom-validators/activitypub/activity' 4import { isRootActivityValid } from '../../../helpers/custom-validators/activitypub/activity'
3import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
4import { getServerActor } from '@server/models/application/application'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6 6
7async function activityPubValidator (req: express.Request, res: express.Response, next: express.NextFunction) { 7async function activityPubValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
8 logger.debug('Checking activity pub parameters') 8 logger.debug('Checking activity pub parameters')
diff --git a/server/middlewares/validators/blocklist.ts b/server/middlewares/validators/blocklist.ts
index 826b16fc8..f15b293e9 100644
--- a/server/middlewares/validators/blocklist.ts
+++ b/server/middlewares/validators/blocklist.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param } from 'express-validator' 2import { body, param } from 'express-validator'
3import { getServerActor } from '@server/models/application/application' 3import { getServerActor } from '@server/models/application/application'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
5import { isHostValid } from '../../helpers/custom-validators/servers' 5import { isHostValid } from '../../helpers/custom-validators/servers'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
7import { WEBSERVER } from '../../initializers/constants' 7import { WEBSERVER } from '../../initializers/constants'
diff --git a/server/middlewares/validators/bulk.ts b/server/middlewares/validators/bulk.ts
index 9bb95f5b7..6fec58149 100644
--- a/server/middlewares/validators/bulk.ts
+++ b/server/middlewares/validators/bulk.ts
@@ -1,8 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body } from 'express-validator' 2import { body } from 'express-validator'
3import { isBulkRemoveCommentsOfScopeValid } from '@server/helpers/custom-validators/bulk' 3import { isBulkRemoveCommentsOfScopeValid } from '@server/helpers/custom-validators/bulk'
4import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode, UserRight } from '@shared/models'
5import { UserRight } from '@shared/models'
6import { BulkRemoveCommentsOfBody } from '@shared/models/bulk/bulk-remove-comments-of-body.model' 5import { BulkRemoveCommentsOfBody } from '@shared/models/bulk/bulk-remove-comments-of-body.model'
7import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
8import { areValidationErrors, doesAccountNameWithHostExist } from './shared' 7import { areValidationErrors, doesAccountNameWithHostExist } from './shared'
diff --git a/server/middlewares/validators/feeds.ts b/server/middlewares/validators/feeds.ts
index 51b8fdd19..d29bebf64 100644
--- a/server/middlewares/validators/feeds.ts
+++ b/server/middlewares/validators/feeds.ts
@@ -1,7 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { param, query } from 'express-validator' 2import { param, query } from 'express-validator'
3 3import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
5import { isValidRSSFeed } from '../../helpers/custom-validators/feeds' 4import { isValidRSSFeed } from '../../helpers/custom-validators/feeds'
6import { exists, isIdOrUUIDValid, isIdValid, toCompleteUUID } from '../../helpers/custom-validators/misc' 5import { exists, isIdOrUUIDValid, isIdValid, toCompleteUUID } from '../../helpers/custom-validators/misc'
7import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts
index 205baca48..16abdd096 100644
--- a/server/middlewares/validators/follows.ts
+++ b/server/middlewares/validators/follows.ts
@@ -1,18 +1,20 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { isFollowStateValid } from '@server/helpers/custom-validators/follows' 3import { isEachUniqueHandleValid, isFollowStateValid, isRemoteHandleValid } from '@server/helpers/custom-validators/follows'
4import { loadActorUrlOrGetFromWebfinger } from '@server/lib/activitypub/actors' 4import { loadActorUrlOrGetFromWebfinger } from '@server/lib/activitypub/actors'
5import { getRemoteNameAndHost } from '@server/lib/activitypub/follow'
5import { getServerActor } from '@server/models/application/application' 6import { getServerActor } from '@server/models/application/application'
6import { MActorFollowActorsDefault } from '@server/types/models' 7import { MActorFollowActorsDefault } from '@server/types/models'
7import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 8import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
8import { isTestInstance } from '../../helpers/core-utils' 9import { isTestInstance } from '../../helpers/core-utils'
9import { isActorTypeValid, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' 10import { isActorTypeValid, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
10import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers' 11import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers'
11import { logger } from '../../helpers/logger' 12import { logger } from '../../helpers/logger'
12import { SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants' 13import { WEBSERVER } from '../../initializers/constants'
13import { ActorModel } from '../../models/actor/actor' 14import { ActorModel } from '../../models/actor/actor'
14import { ActorFollowModel } from '../../models/actor/actor-follow' 15import { ActorFollowModel } from '../../models/actor/actor-follow'
15import { areValidationErrors } from './shared' 16import { areValidationErrors } from './shared'
17import { ServerFollowCreate } from '@shared/models'
16 18
17const listFollowsValidator = [ 19const listFollowsValidator = [
18 query('state') 20 query('state')
@@ -30,29 +32,46 @@ const listFollowsValidator = [
30] 32]
31 33
32const followValidator = [ 34const followValidator = [
33 body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'), 35 body('hosts')
36 .toArray()
37 .custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
38
39 body('handles')
40 .toArray()
41 .custom(isEachUniqueHandleValid).withMessage('Should have an array of handles'),
34 42
35 (req: express.Request, res: express.Response, next: express.NextFunction) => { 43 (req: express.Request, res: express.Response, next: express.NextFunction) => {
36 // Force https if the administrator wants to make friends 44 // Force https if the administrator wants to follow remote actors
37 if (isTestInstance() === false && WEBSERVER.SCHEME === 'http') { 45 if (isTestInstance() === false && WEBSERVER.SCHEME === 'http') {
38 return res 46 return res
39 .status(HttpStatusCode.INTERNAL_SERVER_ERROR_500) 47 .status(HttpStatusCode.INTERNAL_SERVER_ERROR_500)
40 .json({ 48 .json({
41 error: 'Cannot follow on a non HTTPS web server.' 49 error: 'Cannot follow on a non HTTPS web server.'
42 }) 50 })
43 .end()
44 } 51 }
45 52
46 logger.debug('Checking follow parameters', { parameters: req.body }) 53 logger.debug('Checking follow parameters', { parameters: req.body })
47 54
48 if (areValidationErrors(req, res)) return 55 if (areValidationErrors(req, res)) return
49 56
57 const body: ServerFollowCreate = req.body
58 if (body.hosts.length === 0 && body.handles.length === 0) {
59
60 return res
61 .status(HttpStatusCode.BAD_REQUEST_400)
62 .json({
63 error: 'You must provide at least one handle or one host.'
64 })
65 }
66
50 return next() 67 return next()
51 } 68 }
52] 69]
53 70
54const removeFollowingValidator = [ 71const removeFollowingValidator = [
55 param('host').custom(isHostValid).withMessage('Should have a valid host'), 72 param('hostOrHandle')
73 .custom(value => isHostValid(value) || isRemoteHandleValid(value))
74 .withMessage('Should have a valid host/handle'),
56 75
57 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 76 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
58 logger.debug('Checking unfollowing parameters', { parameters: req.params }) 77 logger.debug('Checking unfollowing parameters', { parameters: req.params })
@@ -60,12 +79,14 @@ const removeFollowingValidator = [
60 if (areValidationErrors(req, res)) return 79 if (areValidationErrors(req, res)) return
61 80
62 const serverActor = await getServerActor() 81 const serverActor = await getServerActor()
63 const follow = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI(serverActor.id, SERVER_ACTOR_NAME, req.params.host) 82
83 const { name, host } = getRemoteNameAndHost(req.params.hostOrHandle)
84 const follow = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI(serverActor.id, name, host)
64 85
65 if (!follow) { 86 if (!follow) {
66 return res.fail({ 87 return res.fail({
67 status: HttpStatusCode.NOT_FOUND_404, 88 status: HttpStatusCode.NOT_FOUND_404,
68 message: `Following ${req.params.host} not found.` 89 message: `Follow ${req.params.hostOrHandle} not found.`
69 }) 90 })
70 } 91 }
71 92
diff --git a/server/middlewares/validators/oembed.ts b/server/middlewares/validators/oembed.ts
index 0a82e6932..e5fc0c277 100644
--- a/server/middlewares/validators/oembed.ts
+++ b/server/middlewares/validators/oembed.ts
@@ -4,7 +4,7 @@ import { join } from 'path'
4import { loadVideo } from '@server/lib/model-loaders' 4import { loadVideo } from '@server/lib/model-loaders'
5import { VideoPlaylistModel } from '@server/models/video/video-playlist' 5import { VideoPlaylistModel } from '@server/models/video/video-playlist'
6import { VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models' 6import { VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
7import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 7import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
8import { isTestInstance } from '../../helpers/core-utils' 8import { isTestInstance } from '../../helpers/core-utils'
9import { isIdOrUUIDValid, toCompleteUUID } from '../../helpers/custom-validators/misc' 9import { isIdOrUUIDValid, toCompleteUUID } from '../../helpers/custom-validators/misc'
10import { logger } from '../../helpers/logger' 10import { logger } from '../../helpers/logger'
diff --git a/server/middlewares/validators/plugins.ts b/server/middlewares/validators/plugins.ts
index 8c76d2e36..3fb2176b9 100644
--- a/server/middlewares/validators/plugins.ts
+++ b/server/middlewares/validators/plugins.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param, query, ValidationChain } from 'express-validator' 2import { body, param, query, ValidationChain } from 'express-validator'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
4import { PluginType } from '../../../shared/models/plugins/plugin.type' 4import { PluginType } from '../../../shared/models/plugins/plugin.type'
5import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/server/api/install-plugin.model' 5import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/server/api/install-plugin.model'
6import { exists, isBooleanValid, isSafePath, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' 6import { exists, isBooleanValid, isSafePath, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc'
diff --git a/server/middlewares/validators/redundancy.ts b/server/middlewares/validators/redundancy.ts
index 116c8c611..f1b2ff5cd 100644
--- a/server/middlewares/validators/redundancy.ts
+++ b/server/middlewares/validators/redundancy.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { isVideoRedundancyTarget } from '@server/helpers/custom-validators/video-redundancies' 3import { isVideoRedundancyTarget } from '@server/helpers/custom-validators/video-redundancies'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
5import { 5import {
6 exists, 6 exists,
7 isBooleanValid, 7 isBooleanValid,
diff --git a/server/middlewares/validators/search.ts b/server/middlewares/validators/search.ts
index 7bbf81048..27d0e541d 100644
--- a/server/middlewares/validators/search.ts
+++ b/server/middlewares/validators/search.ts
@@ -1,13 +1,18 @@
1import * as express from 'express' 1import * as express from 'express'
2import { query } from 'express-validator' 2import { query } from 'express-validator'
3import { isSearchTargetValid } from '@server/helpers/custom-validators/search' 3import { isSearchTargetValid } from '@server/helpers/custom-validators/search'
4import { isDateValid } from '../../helpers/custom-validators/misc' 4import { isHostValid } from '@server/helpers/custom-validators/servers'
5import { areUUIDsValid, isDateValid, isNotEmptyStringArray, toCompleteUUIDs } from '../../helpers/custom-validators/misc'
5import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
6import { areValidationErrors } from './shared' 7import { areValidationErrors } from './shared'
7 8
8const videosSearchValidator = [ 9const videosSearchValidator = [
9 query('search').optional().not().isEmpty().withMessage('Should have a valid search'), 10 query('search').optional().not().isEmpty().withMessage('Should have a valid search'),
10 11
12 query('host')
13 .optional()
14 .custom(isHostValid).withMessage('Should have a valid host'),
15
11 query('startDate') 16 query('startDate')
12 .optional() 17 .optional()
13 .custom(isDateValid).withMessage('Should have a start date that conforms to ISO 8601'), 18 .custom(isDateValid).withMessage('Should have a start date that conforms to ISO 8601'),
@@ -22,8 +27,18 @@ const videosSearchValidator = [
22 .optional() 27 .optional()
23 .custom(isDateValid).withMessage('Should have a published end date that conforms to ISO 8601'), 28 .custom(isDateValid).withMessage('Should have a published end date that conforms to ISO 8601'),
24 29
25 query('durationMin').optional().isInt().withMessage('Should have a valid min duration'), 30 query('durationMin')
26 query('durationMax').optional().isInt().withMessage('Should have a valid max duration'), 31 .optional()
32 .isInt().withMessage('Should have a valid min duration'),
33 query('durationMax')
34 .optional()
35 .isInt().withMessage('Should have a valid max duration'),
36
37 query('uuids')
38 .optional()
39 .toArray()
40 .customSanitizer(toCompleteUUIDs)
41 .custom(areUUIDsValid).withMessage('Should have valid uuids'),
27 42
28 query('searchTarget').optional().custom(isSearchTargetValid).withMessage('Should have a valid search target'), 43 query('searchTarget').optional().custom(isSearchTargetValid).withMessage('Should have a valid search target'),
29 44
@@ -37,8 +52,22 @@ const videosSearchValidator = [
37] 52]
38 53
39const videoChannelsListSearchValidator = [ 54const videoChannelsListSearchValidator = [
40 query('search').not().isEmpty().withMessage('Should have a valid search'), 55 query('search')
41 query('searchTarget').optional().custom(isSearchTargetValid).withMessage('Should have a valid search target'), 56 .optional()
57 .not().isEmpty().withMessage('Should have a valid search'),
58
59 query('host')
60 .optional()
61 .custom(isHostValid).withMessage('Should have a valid host'),
62
63 query('searchTarget')
64 .optional()
65 .custom(isSearchTargetValid).withMessage('Should have a valid search target'),
66
67 query('handles')
68 .optional()
69 .toArray()
70 .custom(isNotEmptyStringArray).withMessage('Should have valid handles'),
42 71
43 (req: express.Request, res: express.Response, next: express.NextFunction) => { 72 (req: express.Request, res: express.Response, next: express.NextFunction) => {
44 logger.debug('Checking video channels search query', { parameters: req.query }) 73 logger.debug('Checking video channels search query', { parameters: req.query })
@@ -50,8 +79,23 @@ const videoChannelsListSearchValidator = [
50] 79]
51 80
52const videoPlaylistsListSearchValidator = [ 81const videoPlaylistsListSearchValidator = [
53 query('search').not().isEmpty().withMessage('Should have a valid search'), 82 query('search')
54 query('searchTarget').optional().custom(isSearchTargetValid).withMessage('Should have a valid search target'), 83 .optional()
84 .not().isEmpty().withMessage('Should have a valid search'),
85
86 query('host')
87 .optional()
88 .custom(isHostValid).withMessage('Should have a valid host'),
89
90 query('searchTarget')
91 .optional()
92 .custom(isSearchTargetValid).withMessage('Should have a valid search target'),
93
94 query('uuids')
95 .optional()
96 .toArray()
97 .customSanitizer(toCompleteUUIDs)
98 .custom(areUUIDsValid).withMessage('Should have valid uuids'),
55 99
56 (req: express.Request, res: express.Response, next: express.NextFunction) => { 100 (req: express.Request, res: express.Response, next: express.NextFunction) => {
57 logger.debug('Checking video playlists search query', { parameters: req.query }) 101 logger.debug('Checking video playlists search query', { parameters: req.query })
diff --git a/server/middlewares/validators/server.ts b/server/middlewares/validators/server.ts
index fc7239b25..29fdc13d2 100644
--- a/server/middlewares/validators/server.ts
+++ b/server/middlewares/validators/server.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body } from 'express-validator' 2import { body } from 'express-validator'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
4import { isHostValid, isValidContactBody } from '../../helpers/custom-validators/servers' 4import { isHostValid, isValidContactBody } from '../../helpers/custom-validators/servers'
5import { isUserDisplayNameValid } from '../../helpers/custom-validators/users' 5import { isUserDisplayNameValid } from '../../helpers/custom-validators/users'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
diff --git a/server/middlewares/validators/shared/abuses.ts b/server/middlewares/validators/shared/abuses.ts
index 4a20a55fa..2b8d86ba5 100644
--- a/server/middlewares/validators/shared/abuses.ts
+++ b/server/middlewares/validators/shared/abuses.ts
@@ -1,6 +1,6 @@
1import { Response } from 'express' 1import { Response } from 'express'
2import { AbuseModel } from '@server/models/abuse/abuse' 2import { AbuseModel } from '@server/models/abuse/abuse'
3import { HttpStatusCode } from '@shared/core-utils' 3import { HttpStatusCode } from '@shared/models'
4 4
5async function doesAbuseExist (abuseId: number | string, res: Response) { 5async function doesAbuseExist (abuseId: number | string, res: Response) {
6 const abuse = await AbuseModel.loadByIdWithReporter(parseInt(abuseId + '', 10)) 6 const abuse = await AbuseModel.loadByIdWithReporter(parseInt(abuseId + '', 10))
diff --git a/server/middlewares/validators/shared/accounts.ts b/server/middlewares/validators/shared/accounts.ts
index 04da15441..fe4f83aa0 100644
--- a/server/middlewares/validators/shared/accounts.ts
+++ b/server/middlewares/validators/shared/accounts.ts
@@ -2,7 +2,7 @@ import { Response } from 'express'
2import { AccountModel } from '@server/models/account/account' 2import { AccountModel } from '@server/models/account/account'
3import { UserModel } from '@server/models/user/user' 3import { UserModel } from '@server/models/user/user'
4import { MAccountDefault } from '@server/types/models' 4import { MAccountDefault } from '@server/types/models'
5import { HttpStatusCode } from '@shared/core-utils' 5import { HttpStatusCode } from '@shared/models'
6 6
7function doesAccountIdExist (id: number | string, res: Response, sendNotFound = true) { 7function doesAccountIdExist (id: number | string, res: Response, sendNotFound = true) {
8 const promise = AccountModel.load(parseInt(id + '', 10)) 8 const promise = AccountModel.load(parseInt(id + '', 10))
diff --git a/server/middlewares/validators/shared/video-blacklists.ts b/server/middlewares/validators/shared/video-blacklists.ts
index 01491c10f..f85b39b23 100644
--- a/server/middlewares/validators/shared/video-blacklists.ts
+++ b/server/middlewares/validators/shared/video-blacklists.ts
@@ -1,6 +1,6 @@
1import { Response } from 'express' 1import { Response } from 'express'
2import { VideoBlacklistModel } from '@server/models/video/video-blacklist' 2import { VideoBlacklistModel } from '@server/models/video/video-blacklist'
3import { HttpStatusCode } from '@shared/core-utils' 3import { HttpStatusCode } from '@shared/models'
4 4
5async function doesVideoBlacklistExist (videoId: number, res: Response) { 5async function doesVideoBlacklistExist (videoId: number, res: Response) {
6 const videoBlacklist = await VideoBlacklistModel.loadByVideoId(videoId) 6 const videoBlacklist = await VideoBlacklistModel.loadByVideoId(videoId)
diff --git a/server/middlewares/validators/shared/video-captions.ts b/server/middlewares/validators/shared/video-captions.ts
index 80f6c5a52..831b366ea 100644
--- a/server/middlewares/validators/shared/video-captions.ts
+++ b/server/middlewares/validators/shared/video-captions.ts
@@ -1,7 +1,7 @@
1import { Response } from 'express' 1import { Response } from 'express'
2import { VideoCaptionModel } from '@server/models/video/video-caption' 2import { VideoCaptionModel } from '@server/models/video/video-caption'
3import { MVideoId } from '@server/types/models' 3import { MVideoId } from '@server/types/models'
4import { HttpStatusCode } from '@shared/core-utils' 4import { HttpStatusCode } from '@shared/models'
5 5
6async function doesVideoCaptionExist (video: MVideoId, language: string, res: Response) { 6async function doesVideoCaptionExist (video: MVideoId, language: string, res: Response) {
7 const videoCaption = await VideoCaptionModel.loadByVideoIdAndLanguage(video.id, language) 7 const videoCaption = await VideoCaptionModel.loadByVideoIdAndLanguage(video.id, language)
diff --git a/server/middlewares/validators/shared/video-channels.ts b/server/middlewares/validators/shared/video-channels.ts
index fe2e663b7..3fc3d012a 100644
--- a/server/middlewares/validators/shared/video-channels.ts
+++ b/server/middlewares/validators/shared/video-channels.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { VideoChannelModel } from '@server/models/video/video-channel' 2import { VideoChannelModel } from '@server/models/video/video-channel'
3import { MChannelBannerAccountDefault } from '@server/types/models' 3import { MChannelBannerAccountDefault } from '@server/types/models'
4import { HttpStatusCode } from '@shared/core-utils' 4import { HttpStatusCode } from '@shared/models'
5 5
6async function doesLocalVideoChannelNameExist (name: string, res: express.Response) { 6async function doesLocalVideoChannelNameExist (name: string, res: express.Response) {
7 const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name) 7 const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name)
diff --git a/server/middlewares/validators/shared/video-comments.ts b/server/middlewares/validators/shared/video-comments.ts
index 83ea15c98..60132fb6e 100644
--- a/server/middlewares/validators/shared/video-comments.ts
+++ b/server/middlewares/validators/shared/video-comments.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { VideoCommentModel } from '@server/models/video/video-comment' 2import { VideoCommentModel } from '@server/models/video/video-comment'
3import { MVideoId } from '@server/types/models' 3import { MVideoId } from '@server/types/models'
4import { HttpStatusCode } from '@shared/core-utils' 4import { HttpStatusCode } from '@shared/models'
5 5
6async function doesVideoCommentThreadExist (idArg: number | string, video: MVideoId, res: express.Response) { 6async function doesVideoCommentThreadExist (idArg: number | string, video: MVideoId, res: express.Response) {
7 const id = parseInt(idArg + '', 10) 7 const id = parseInt(idArg + '', 10)
diff --git a/server/middlewares/validators/shared/video-imports.ts b/server/middlewares/validators/shared/video-imports.ts
index 0f984bc17..50b49ffcb 100644
--- a/server/middlewares/validators/shared/video-imports.ts
+++ b/server/middlewares/validators/shared/video-imports.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { VideoImportModel } from '@server/models/video/video-import' 2import { VideoImportModel } from '@server/models/video/video-import'
3import { HttpStatusCode } from '@shared/core-utils' 3import { HttpStatusCode } from '@shared/models'
4 4
5async function doesVideoImportExist (id: number, res: express.Response) { 5async function doesVideoImportExist (id: number, res: express.Response) {
6 const videoImport = await VideoImportModel.loadAndPopulateVideo(id) 6 const videoImport = await VideoImportModel.loadAndPopulateVideo(id)
diff --git a/server/middlewares/validators/shared/video-ownerships.ts b/server/middlewares/validators/shared/video-ownerships.ts
index fc27006ce..93a23ef40 100644
--- a/server/middlewares/validators/shared/video-ownerships.ts
+++ b/server/middlewares/validators/shared/video-ownerships.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { VideoChangeOwnershipModel } from '@server/models/video/video-change-ownership' 2import { VideoChangeOwnershipModel } from '@server/models/video/video-change-ownership'
3import { HttpStatusCode } from '@shared/core-utils' 3import { HttpStatusCode } from '@shared/models'
4 4
5async function doesChangeVideoOwnershipExist (idArg: number | string, res: express.Response) { 5async function doesChangeVideoOwnershipExist (idArg: number | string, res: express.Response) {
6 const id = parseInt(idArg + '', 10) 6 const id = parseInt(idArg + '', 10)
diff --git a/server/middlewares/validators/shared/video-playlists.ts b/server/middlewares/validators/shared/video-playlists.ts
index d762859a8..3f6768179 100644
--- a/server/middlewares/validators/shared/video-playlists.ts
+++ b/server/middlewares/validators/shared/video-playlists.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { VideoPlaylistModel } from '@server/models/video/video-playlist' 2import { VideoPlaylistModel } from '@server/models/video/video-playlist'
3import { MVideoPlaylist } from '@server/types/models' 3import { MVideoPlaylist } from '@server/types/models'
4import { HttpStatusCode } from '@shared/core-utils' 4import { HttpStatusCode } from '@shared/models'
5 5
6export type VideoPlaylistFetchType = 'summary' | 'all' 6export type VideoPlaylistFetchType = 'summary' | 'all'
7async function doesVideoPlaylistExist (id: number | string, res: express.Response, fetchType: VideoPlaylistFetchType = 'summary') { 7async function doesVideoPlaylistExist (id: number | string, res: express.Response, fetchType: VideoPlaylistFetchType = 'summary') {
diff --git a/server/middlewares/validators/shared/videos.ts b/server/middlewares/validators/shared/videos.ts
index 2c66c1a3a..71b81654f 100644
--- a/server/middlewares/validators/shared/videos.ts
+++ b/server/middlewares/validators/shared/videos.ts
@@ -12,8 +12,7 @@ import {
12 MVideoImmutable, 12 MVideoImmutable,
13 MVideoThumbnail 13 MVideoThumbnail
14} from '@server/types/models' 14} from '@server/types/models'
15import { HttpStatusCode } from '@shared/core-utils' 15import { HttpStatusCode, UserRight } from '@shared/models'
16import { UserRight } from '@shared/models'
17 16
18async function doesVideoExist (id: number | string, res: Response, fetchType: VideoLoadType = 'all') { 17async function doesVideoExist (id: number | string, res: Response, fetchType: VideoLoadType = 'all') {
19 const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined 18 const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined
diff --git a/server/middlewares/validators/themes.ts b/server/middlewares/validators/themes.ts
index d4716257f..2953b9505 100644
--- a/server/middlewares/validators/themes.ts
+++ b/server/middlewares/validators/themes.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { param } from 'express-validator' 2import { param } from 'express-validator'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
4import { isSafePath } from '../../helpers/custom-validators/misc' 4import { isSafePath } from '../../helpers/custom-validators/misc'
5import { isPluginNameValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins' 5import { isPluginNameValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
diff --git a/server/middlewares/validators/user-subscriptions.ts b/server/middlewares/validators/user-subscriptions.ts
index ab7962923..df5777771 100644
--- a/server/middlewares/validators/user-subscriptions.ts
+++ b/server/middlewares/validators/user-subscriptions.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
4import { areValidActorHandles, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' 4import { areValidActorHandles, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
5import { toArray } from '../../helpers/custom-validators/misc' 5import { toArray } from '../../helpers/custom-validators/misc'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index 698d7d814..748b89f8f 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -3,7 +3,7 @@ import { body, param, query } from 'express-validator'
3import { omit } from 'lodash' 3import { omit } from 'lodash'
4import { Hooks } from '@server/lib/plugins/hooks' 4import { Hooks } from '@server/lib/plugins/hooks'
5import { MUserDefault } from '@server/types/models' 5import { MUserDefault } from '@server/types/models'
6import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
7import { UserRole } from '../../../shared/models/users' 7import { UserRole } from '../../../shared/models/users'
8import { UserRegister } from '../../../shared/models/users/user-register.model' 8import { UserRegister } from '../../../shared/models/users/user-register.model'
9import { isActorPreferredUsernameValid } from '../../helpers/custom-validators/activitypub/actor' 9import { isActorPreferredUsernameValid } from '../../helpers/custom-validators/activitypub/actor'
diff --git a/server/middlewares/validators/videos/video-blacklist.ts b/server/middlewares/validators/videos/video-blacklist.ts
index 21141d84d..3a4937b7b 100644
--- a/server/middlewares/validators/videos/video-blacklist.ts
+++ b/server/middlewares/validators/videos/video-blacklist.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, query } from 'express-validator' 2import { body, query } from 'express-validator'
3import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { isBooleanValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc' 4import { isBooleanValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc'
5import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../../helpers/custom-validators/video-blacklist' 5import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../../helpers/custom-validators/video-blacklist'
6import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts
index e7df185e4..ea10fe425 100644
--- a/server/middlewares/validators/videos/video-channels.ts
+++ b/server/middlewares/validators/videos/video-channels.ts
@@ -3,7 +3,7 @@ import { body, param, query } from 'express-validator'
3import { VIDEO_CHANNELS } from '@server/initializers/constants' 3import { VIDEO_CHANNELS } from '@server/initializers/constants'
4import { MChannelAccountDefault, MUser } from '@server/types/models' 4import { MChannelAccountDefault, MUser } from '@server/types/models'
5import { UserRight } from '../../../../shared' 5import { UserRight } from '../../../../shared'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
7import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' 7import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor'
8import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' 8import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc'
9import { 9import {
diff --git a/server/middlewares/validators/videos/video-comments.ts b/server/middlewares/validators/videos/video-comments.ts
index 885506ebe..61c2ed92f 100644
--- a/server/middlewares/validators/videos/video-comments.ts
+++ b/server/middlewares/validators/videos/video-comments.ts
@@ -2,7 +2,7 @@ import * as express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { MUserAccountUrl } from '@server/types/models' 3import { MUserAccountUrl } from '@server/types/models'
4import { UserRight } from '../../../../shared' 4import { UserRight } from '../../../../shared'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 5import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
6import { exists, isBooleanValid, isIdValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' 6import { exists, isBooleanValid, isIdValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc'
7import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments' 7import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments'
8import { logger } from '../../../helpers/logger' 8import { logger } from '../../../helpers/logger'
diff --git a/server/middlewares/validators/videos/video-imports.ts b/server/middlewares/validators/videos/video-imports.ts
index 85dc647ce..52b839e56 100644
--- a/server/middlewares/validators/videos/video-imports.ts
+++ b/server/middlewares/validators/videos/video-imports.ts
@@ -2,7 +2,7 @@ import * as express from 'express'
2import { body } from 'express-validator' 2import { body } from 'express-validator'
3import { isPreImportVideoAccepted } from '@server/lib/moderation' 3import { isPreImportVideoAccepted } from '@server/lib/moderation'
4import { Hooks } from '@server/lib/plugins/hooks' 4import { Hooks } from '@server/lib/plugins/hooks'
5import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 5import { HttpStatusCode } from '@shared/models'
6import { VideoImportCreate } from '@shared/models/videos/import/video-import-create.model' 6import { VideoImportCreate } from '@shared/models/videos/import/video-import-create.model'
7import { isIdValid, toIntOrNull } from '../../../helpers/custom-validators/misc' 7import { isIdValid, toIntOrNull } from '../../../helpers/custom-validators/misc'
8import { isVideoImportTargetUrlValid, isVideoImportTorrentFile } from '../../../helpers/custom-validators/video-imports' 8import { isVideoImportTargetUrlValid, isVideoImportTorrentFile } from '../../../helpers/custom-validators/video-imports'
diff --git a/server/middlewares/validators/videos/video-live.ts b/server/middlewares/validators/videos/video-live.ts
index 7cfb935e3..97e8b4510 100644
--- a/server/middlewares/validators/videos/video-live.ts
+++ b/server/middlewares/validators/videos/video-live.ts
@@ -5,8 +5,7 @@ import { isLocalLiveVideoAccepted } from '@server/lib/moderation'
5import { Hooks } from '@server/lib/plugins/hooks' 5import { Hooks } from '@server/lib/plugins/hooks'
6import { VideoModel } from '@server/models/video/video' 6import { VideoModel } from '@server/models/video/video'
7import { VideoLiveModel } from '@server/models/video/video-live' 7import { VideoLiveModel } from '@server/models/video/video-live'
8import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 8import { HttpStatusCode, ServerErrorCode, UserRight, VideoState } from '@shared/models'
9import { ServerErrorCode, UserRight, VideoState } from '@shared/models'
10import { isBooleanValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc' 9import { isBooleanValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc'
11import { isVideoNameValid } from '../../../helpers/custom-validators/videos' 10import { isVideoNameValid } from '../../../helpers/custom-validators/videos'
12import { cleanUpReqFiles } from '../../../helpers/express-utils' 11import { cleanUpReqFiles } from '../../../helpers/express-utils'
diff --git a/server/middlewares/validators/videos/video-ownership-changes.ts b/server/middlewares/validators/videos/video-ownership-changes.ts
index 54ac46c99..a7f0b72c3 100644
--- a/server/middlewares/validators/videos/video-ownership-changes.ts
+++ b/server/middlewares/validators/videos/video-ownership-changes.ts
@@ -6,8 +6,14 @@ import { logger } from '@server/helpers/logger'
6import { isAbleToUploadVideo } from '@server/lib/user' 6import { isAbleToUploadVideo } from '@server/lib/user'
7import { AccountModel } from '@server/models/account/account' 7import { AccountModel } from '@server/models/account/account'
8import { MVideoWithAllFiles } from '@server/types/models' 8import { MVideoWithAllFiles } from '@server/types/models'
9import { HttpStatusCode } from '@shared/core-utils' 9import {
10import { ServerErrorCode, UserRight, VideoChangeOwnershipAccept, VideoChangeOwnershipStatus, VideoState } from '@shared/models' 10 HttpStatusCode,
11 ServerErrorCode,
12 UserRight,
13 VideoChangeOwnershipAccept,
14 VideoChangeOwnershipStatus,
15 VideoState
16} from '@shared/models'
11import { 17import {
12 areValidationErrors, 18 areValidationErrors,
13 checkUserCanManageVideo, 19 checkUserCanManageVideo,
diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts
index 5ee7ee0ce..ab84b4814 100644
--- a/server/middlewares/validators/videos/video-playlists.ts
+++ b/server/middlewares/validators/videos/video-playlists.ts
@@ -3,7 +3,7 @@ import { body, param, query, ValidationChain } from 'express-validator'
3import { ExpressPromiseHandler } from '@server/types/express' 3import { ExpressPromiseHandler } from '@server/types/express'
4import { MUserAccountId } from '@server/types/models' 4import { MUserAccountId } from '@server/types/models'
5import { UserRight, VideoPlaylistCreate, VideoPlaylistUpdate } from '../../../../shared' 5import { UserRight, VideoPlaylistCreate, VideoPlaylistUpdate } from '../../../../shared'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
7import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' 7import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
8import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model' 8import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
9import { 9import {
diff --git a/server/middlewares/validators/videos/video-rates.ts b/server/middlewares/validators/videos/video-rates.ts
index 5d5dfb222..5fe78b39e 100644
--- a/server/middlewares/validators/videos/video-rates.ts
+++ b/server/middlewares/validators/videos/video-rates.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { VideoRateType } from '../../../../shared/models/videos' 4import { VideoRateType } from '../../../../shared/models/videos'
5import { isAccountNameValid } from '../../../helpers/custom-validators/accounts' 5import { isAccountNameValid } from '../../../helpers/custom-validators/accounts'
6import { isIdValid } from '../../../helpers/custom-validators/misc' 6import { isIdValid } from '../../../helpers/custom-validators/misc'
diff --git a/server/middlewares/validators/videos/video-shares.ts b/server/middlewares/validators/videos/video-shares.ts
index 7e54b6fc0..3b8d61768 100644
--- a/server/middlewares/validators/videos/video-shares.ts
+++ b/server/middlewares/validators/videos/video-shares.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { param } from 'express-validator' 2import { param } from 'express-validator'
3import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { isIdValid } from '../../../helpers/custom-validators/misc' 4import { isIdValid } from '../../../helpers/custom-validators/misc'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { VideoShareModel } from '../../../models/video/video-share' 6import { VideoShareModel } from '../../../models/video/video-share'
diff --git a/server/middlewares/validators/videos/video-watch.ts b/server/middlewares/validators/videos/video-watch.ts
index 43306f7cd..431515eb1 100644
--- a/server/middlewares/validators/videos/video-watch.ts
+++ b/server/middlewares/validators/videos/video-watch.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body } from 'express-validator' 2import { body } from 'express-validator'
3import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { toIntOrNull } from '../../../helpers/custom-validators/misc' 4import { toIntOrNull } from '../../../helpers/custom-validators/misc'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared' 6import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared'
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts
index 49e10e2b5..374a59c50 100644
--- a/server/middlewares/validators/videos/videos.ts
+++ b/server/middlewares/validators/videos/videos.ts
@@ -6,7 +6,7 @@ import { getServerActor } from '@server/models/application/application'
6import { ExpressPromiseHandler } from '@server/types/express' 6import { ExpressPromiseHandler } from '@server/types/express'
7import { MUserAccountId, MVideoFullLight } from '@server/types/models' 7import { MUserAccountId, MVideoFullLight } from '@server/types/models'
8import { ServerErrorCode, UserRight, VideoPrivacy } from '../../../../shared' 8import { ServerErrorCode, UserRight, VideoPrivacy } from '../../../../shared'
9import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 9import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
10import { 10import {
11 exists, 11 exists,
12 isBooleanValid, 12 isBooleanValid,
diff --git a/server/middlewares/validators/webfinger.ts b/server/middlewares/validators/webfinger.ts
index bcdd136c6..131360820 100644
--- a/server/middlewares/validators/webfinger.ts
+++ b/server/middlewares/validators/webfinger.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { query } from 'express-validator' 2import { query } from 'express-validator'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
4import { isWebfingerLocalResourceValid } from '../../helpers/custom-validators/webfinger' 4import { isWebfingerLocalResourceValid } from '../../helpers/custom-validators/webfinger'
5import { getHostWithPort } from '../../helpers/express-utils' 5import { getHostWithPort } from '../../helpers/express-utils'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index 665ecd595..37194a119 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -52,6 +52,7 @@ export enum ScopeNames {
52export type SummaryOptions = { 52export type SummaryOptions = {
53 actorRequired?: boolean // Default: true 53 actorRequired?: boolean // Default: true
54 whereActor?: WhereOptions 54 whereActor?: WhereOptions
55 whereServer?: WhereOptions
55 withAccountBlockerIds?: number[] 56 withAccountBlockerIds?: number[]
56} 57}
57 58
@@ -65,12 +66,11 @@ export type SummaryOptions = {
65})) 66}))
66@Scopes(() => ({ 67@Scopes(() => ({
67 [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => { 68 [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => {
68 const whereActor = options.whereActor || undefined
69
70 const serverInclude: IncludeOptions = { 69 const serverInclude: IncludeOptions = {
71 attributes: [ 'host' ], 70 attributes: [ 'host' ],
72 model: ServerModel.unscoped(), 71 model: ServerModel.unscoped(),
73 required: false 72 required: !!options.whereServer,
73 where: options.whereServer
74 } 74 }
75 75
76 const queryInclude: Includeable[] = [ 76 const queryInclude: Includeable[] = [
@@ -78,7 +78,7 @@ export type SummaryOptions = {
78 attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ], 78 attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
79 model: ActorModel.unscoped(), 79 model: ActorModel.unscoped(),
80 required: options.actorRequired ?? true, 80 required: options.actorRequired ?? true,
81 where: whereActor, 81 where: options.whereActor,
82 include: [ 82 include: [
83 serverInclude, 83 serverInclude,
84 84
diff --git a/server/models/actor/actor-follow.ts b/server/models/actor/actor-follow.ts
index 3a09e51d6..283856d3f 100644
--- a/server/models/actor/actor-follow.ts
+++ b/server/models/actor/actor-follow.ts
@@ -20,7 +20,6 @@ import {
20} from 'sequelize-typescript' 20} from 'sequelize-typescript'
21import { isActivityPubUrlValid } from '@server/helpers/custom-validators/activitypub/misc' 21import { isActivityPubUrlValid } from '@server/helpers/custom-validators/activitypub/misc'
22import { getServerActor } from '@server/models/application/application' 22import { getServerActor } from '@server/models/application/application'
23import { VideoModel } from '@server/models/video/video'
24import { 23import {
25 MActorFollowActorsDefault, 24 MActorFollowActorsDefault,
26 MActorFollowActorsDefaultSubscription, 25 MActorFollowActorsDefaultSubscription,
@@ -36,6 +35,7 @@ import { logger } from '../../helpers/logger'
36import { ACTOR_FOLLOW_SCORE, CONSTRAINTS_FIELDS, FOLLOW_STATES, SERVER_ACTOR_NAME } from '../../initializers/constants' 35import { ACTOR_FOLLOW_SCORE, CONSTRAINTS_FIELDS, FOLLOW_STATES, SERVER_ACTOR_NAME } from '../../initializers/constants'
37import { AccountModel } from '../account/account' 36import { AccountModel } from '../account/account'
38import { ServerModel } from '../server/server' 37import { ServerModel } from '../server/server'
38import { doesExist } from '../shared/query'
39import { createSafeIn, getFollowsSort, getSort, searchAttribute, throwIfNotValid } from '../utils' 39import { createSafeIn, getFollowsSort, getSort, searchAttribute, throwIfNotValid } from '../utils'
40import { VideoChannelModel } from '../video/video-channel' 40import { VideoChannelModel } from '../video/video-channel'
41import { ActorModel, unusedActorAttributesForAPI } from './actor' 41import { ActorModel, unusedActorAttributesForAPI } from './actor'
@@ -166,14 +166,8 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
166 166
167 static isFollowedBy (actorId: number, followerActorId: number) { 167 static isFollowedBy (actorId: number, followerActorId: number) {
168 const query = 'SELECT 1 FROM "actorFollow" WHERE "actorId" = $followerActorId AND "targetActorId" = $actorId LIMIT 1' 168 const query = 'SELECT 1 FROM "actorFollow" WHERE "actorId" = $followerActorId AND "targetActorId" = $actorId LIMIT 1'
169 const options = {
170 type: QueryTypes.SELECT as QueryTypes.SELECT,
171 bind: { actorId, followerActorId },
172 raw: true
173 }
174 169
175 return VideoModel.sequelize.query(query, options) 170 return doesExist(query, { actorId, followerActorId })
176 .then(results => results.length === 1)
177 } 171 }
178 172
179 static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Transaction): Promise<MActorFollowActorsDefault> { 173 static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Transaction): Promise<MActorFollowActorsDefault> {
@@ -324,13 +318,13 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
324 318
325 const followWhere = state ? { state } : {} 319 const followWhere = state ? { state } : {}
326 const followingWhere: WhereOptions = {} 320 const followingWhere: WhereOptions = {}
327 const followingServerWhere: WhereOptions = {}
328 321
329 if (search) { 322 if (search) {
330 Object.assign(followingServerWhere, { 323 Object.assign(followWhere, {
331 host: { 324 [Op.or]: [
332 [Op.iLike]: '%' + search + '%' 325 searchAttribute(options.search, '$ActorFollowing.preferredUsername$'),
333 } 326 searchAttribute(options.search, '$ActorFollowing.Server.host$')
327 ]
334 }) 328 })
335 } 329 }
336 330
@@ -361,8 +355,7 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
361 include: [ 355 include: [
362 { 356 {
363 model: ServerModel, 357 model: ServerModel,
364 required: true, 358 required: true
365 where: followingServerWhere
366 } 359 }
367 ] 360 ]
368 } 361 }
@@ -391,13 +384,13 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
391 384
392 const followWhere = state ? { state } : {} 385 const followWhere = state ? { state } : {}
393 const followerWhere: WhereOptions = {} 386 const followerWhere: WhereOptions = {}
394 const followerServerWhere: WhereOptions = {}
395 387
396 if (search) { 388 if (search) {
397 Object.assign(followerServerWhere, { 389 Object.assign(followWhere, {
398 host: { 390 [Op.or]: [
399 [Op.iLike]: '%' + search + '%' 391 searchAttribute(search, '$ActorFollower.preferredUsername$'),
400 } 392 searchAttribute(search, '$ActorFollower.Server.host$')
393 ]
401 }) 394 })
402 } 395 }
403 396
@@ -420,8 +413,7 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
420 include: [ 413 include: [
421 { 414 {
422 model: ServerModel, 415 model: ServerModel,
423 required: true, 416 required: true
424 where: followerServerWhere
425 } 417 }
426 ] 418 ]
427 }, 419 },
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts
index ccda023e0..d645be248 100644
--- a/server/models/redundancy/video-redundancy.ts
+++ b/server/models/redundancy/video-redundancy.ts
@@ -160,8 +160,8 @@ export class VideoRedundancyModel extends Model<Partial<AttributesOnly<VideoRedu
160 const logIdentifier = `${videoFile.Video.uuid}-${videoFile.resolution}` 160 const logIdentifier = `${videoFile.Video.uuid}-${videoFile.resolution}`
161 logger.info('Removing duplicated video file %s.', logIdentifier) 161 logger.info('Removing duplicated video file %s.', logIdentifier)
162 162
163 videoFile.Video.removeFile(videoFile, true) 163 videoFile.Video.removeFileAndTorrent(videoFile, true)
164 .catch(err => logger.error('Cannot delete %s files.', logIdentifier, { err })) 164 .catch(err => logger.error('Cannot delete %s files.', logIdentifier, { err }))
165 } 165 }
166 166
167 if (instance.videoStreamingPlaylistId) { 167 if (instance.videoStreamingPlaylistId) {
diff --git a/server/models/shared/index.ts b/server/models/shared/index.ts
new file mode 100644
index 000000000..5b97510e0
--- /dev/null
+++ b/server/models/shared/index.ts
@@ -0,0 +1,2 @@
1export * from './query'
2export * from './update'
diff --git a/server/models/shared/query.ts b/server/models/shared/query.ts
new file mode 100644
index 000000000..036cc13c6
--- /dev/null
+++ b/server/models/shared/query.ts
@@ -0,0 +1,17 @@
1import { BindOrReplacements, QueryTypes } from 'sequelize'
2import { sequelizeTypescript } from '@server/initializers/database'
3
4function doesExist (query: string, bind?: BindOrReplacements) {
5 const options = {
6 type: QueryTypes.SELECT as QueryTypes.SELECT,
7 bind,
8 raw: true
9 }
10
11 return sequelizeTypescript.query(query, options)
12 .then(results => results.length === 1)
13}
14
15export {
16 doesExist
17}
diff --git a/server/models/shared/update.ts b/server/models/shared/update.ts
new file mode 100644
index 000000000..d338211e3
--- /dev/null
+++ b/server/models/shared/update.ts
@@ -0,0 +1,18 @@
1import { QueryTypes, Transaction } from 'sequelize'
2import { sequelizeTypescript } from '@server/initializers/database'
3
4// Sequelize always skip the update if we only update updatedAt field
5function setAsUpdated (table: string, id: number, transaction?: Transaction) {
6 return sequelizeTypescript.query(
7 `UPDATE "${table}" SET "updatedAt" = :updatedAt WHERE id = :id`,
8 {
9 replacements: { table, id, updatedAt: new Date() },
10 type: QueryTypes.UPDATE,
11 transaction
12 }
13 )
14}
15
16export {
17 setAsUpdated
18}
diff --git a/server/models/user/user-notification.ts b/server/models/user/user-notification.ts
index a7f84e9ca..04c5513a9 100644
--- a/server/models/user/user-notification.ts
+++ b/server/models/user/user-notification.ts
@@ -1,5 +1,6 @@
1import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize' 1import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize'
2import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 2import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
3import { uuidToShort } from '@server/helpers/uuid'
3import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user' 4import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user'
4import { AttributesOnly } from '@shared/core-utils' 5import { AttributesOnly } from '@shared/core-utils'
5import { UserNotification, UserNotificationType } from '../../../shared' 6import { UserNotification, UserNotificationType } from '../../../shared'
@@ -615,6 +616,7 @@ export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNoti
615 return { 616 return {
616 id: video.id, 617 id: video.id,
617 uuid: video.uuid, 618 uuid: video.uuid,
619 shortUUID: uuidToShort(video.uuid),
618 name: video.name 620 name: video.name
619 } 621 }
620 } 622 }
@@ -628,6 +630,7 @@ export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNoti
628 ? { 630 ? {
629 id: abuse.VideoCommentAbuse.VideoComment.Video.id, 631 id: abuse.VideoCommentAbuse.VideoComment.Video.id,
630 name: abuse.VideoCommentAbuse.VideoComment.Video.name, 632 name: abuse.VideoCommentAbuse.VideoComment.Video.name,
633 shortUUID: uuidToShort(abuse.VideoCommentAbuse.VideoComment.Video.uuid),
631 uuid: abuse.VideoCommentAbuse.VideoComment.Video.uuid 634 uuid: abuse.VideoCommentAbuse.VideoComment.Video.uuid
632 } 635 }
633 : undefined 636 : undefined
diff --git a/server/models/video/formatter/video-format-utils.ts b/server/models/video/formatter/video-format-utils.ts
index ab4cf53a8..8a54de3b0 100644
--- a/server/models/video/formatter/video-format-utils.ts
+++ b/server/models/video/formatter/video-format-utils.ts
@@ -182,8 +182,8 @@ function streamingPlaylistsModelToFormattedJSON (
182 return { 182 return {
183 id: playlist.id, 183 id: playlist.id,
184 type: playlist.type, 184 type: playlist.type,
185 playlistUrl: playlist.playlistUrl, 185 playlistUrl: playlist.getMasterPlaylistUrl(video),
186 segmentsSha256Url: playlist.segmentsSha256Url, 186 segmentsSha256Url: playlist.getSha256SegmentsUrl(video),
187 redundancies, 187 redundancies,
188 files 188 files
189 } 189 }
@@ -331,7 +331,7 @@ function videoModelToActivityPubObject (video: MVideoAP): VideoObject {
331 type: 'Link', 331 type: 'Link',
332 name: 'sha256', 332 name: 'sha256',
333 mediaType: 'application/json' as 'application/json', 333 mediaType: 'application/json' as 'application/json',
334 href: playlist.segmentsSha256Url 334 href: playlist.getSha256SegmentsUrl(video)
335 }) 335 })
336 336
337 addVideoFilesInAPAcc(tag, video, playlist.VideoFiles || []) 337 addVideoFilesInAPAcc(tag, video, playlist.VideoFiles || [])
@@ -339,7 +339,7 @@ function videoModelToActivityPubObject (video: MVideoAP): VideoObject {
339 url.push({ 339 url.push({
340 type: 'Link', 340 type: 'Link',
341 mediaType: 'application/x-mpegURL' as 'application/x-mpegURL', 341 mediaType: 'application/x-mpegURL' as 'application/x-mpegURL',
342 href: playlist.playlistUrl, 342 href: playlist.getMasterPlaylistUrl(video),
343 tag 343 tag
344 }) 344 })
345 } 345 }
diff --git a/server/models/video/sql/shared/video-tables.ts b/server/models/video/sql/shared/video-tables.ts
index abdd22188..742d19099 100644
--- a/server/models/video/sql/shared/video-tables.ts
+++ b/server/models/video/sql/shared/video-tables.ts
@@ -92,12 +92,13 @@ export class VideoTables {
92 } 92 }
93 93
94 getStreamingPlaylistAttributes () { 94 getStreamingPlaylistAttributes () {
95 let playlistKeys = [ 'id', 'playlistUrl', 'type' ] 95 let playlistKeys = [ 'id', 'playlistUrl', 'playlistFilename', 'type' ]
96 96
97 if (this.mode === 'get') { 97 if (this.mode === 'get') {
98 playlistKeys = playlistKeys.concat([ 98 playlistKeys = playlistKeys.concat([
99 'p2pMediaLoaderInfohashes', 99 'p2pMediaLoaderInfohashes',
100 'p2pMediaLoaderPeerVersion', 100 'p2pMediaLoaderPeerVersion',
101 'segmentsSha256Filename',
101 'segmentsSha256Url', 102 'segmentsSha256Url',
102 'videoId', 103 'videoId',
103 'createdAt', 104 'createdAt',
diff --git a/server/models/video/sql/videos-id-list-query-builder.ts b/server/models/video/sql/videos-id-list-query-builder.ts
index 30b251f0f..7625c003d 100644
--- a/server/models/video/sql/videos-id-list-query-builder.ts
+++ b/server/models/video/sql/videos-id-list-query-builder.ts
@@ -1,6 +1,7 @@
1import { Sequelize } from 'sequelize' 1import { Sequelize } from 'sequelize'
2import validator from 'validator' 2import validator from 'validator'
3import { exists } from '@server/helpers/custom-validators/misc' 3import { exists } from '@server/helpers/custom-validators/misc'
4import { WEBSERVER } from '@server/initializers/constants'
4import { buildDirectionAndField, createSafeIn } from '@server/models/utils' 5import { buildDirectionAndField, createSafeIn } from '@server/models/utils'
5import { MUserAccountId, MUserId } from '@server/types/models' 6import { MUserAccountId, MUserId } from '@server/types/models'
6import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models' 7import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models'
@@ -25,6 +26,7 @@ export type BuildVideosListQueryOptions = {
25 26
26 nsfw?: boolean 27 nsfw?: boolean
27 filter?: VideoFilter 28 filter?: VideoFilter
29 host?: string
28 isLive?: boolean 30 isLive?: boolean
29 31
30 categoryOneOf?: number[] 32 categoryOneOf?: number[]
@@ -33,6 +35,8 @@ export type BuildVideosListQueryOptions = {
33 tagsOneOf?: string[] 35 tagsOneOf?: string[]
34 tagsAllOf?: string[] 36 tagsAllOf?: string[]
35 37
38 uuids?: string[]
39
36 withFiles?: boolean 40 withFiles?: boolean
37 41
38 accountId?: number 42 accountId?: number
@@ -131,6 +135,10 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
131 this.whereOnlyLocal() 135 this.whereOnlyLocal()
132 } 136 }
133 137
138 if (options.host) {
139 this.whereHost(options.host)
140 }
141
134 if (options.accountId) { 142 if (options.accountId) {
135 this.whereAccountId(options.accountId) 143 this.whereAccountId(options.accountId)
136 } 144 }
@@ -155,6 +163,10 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
155 this.whereTagsAllOf(options.tagsAllOf) 163 this.whereTagsAllOf(options.tagsAllOf)
156 } 164 }
157 165
166 if (options.uuids) {
167 this.whereUUIDs(options.uuids)
168 }
169
158 if (options.nsfw === true) { 170 if (options.nsfw === true) {
159 this.whereNSFW() 171 this.whereNSFW()
160 } else if (options.nsfw === false) { 172 } else if (options.nsfw === false) {
@@ -291,6 +303,19 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
291 this.and.push('"video"."remote" IS FALSE') 303 this.and.push('"video"."remote" IS FALSE')
292 } 304 }
293 305
306 private whereHost (host: string) {
307 // Local instance
308 if (host === WEBSERVER.HOST) {
309 this.and.push('"accountActor"."serverId" IS NULL')
310 return
311 }
312
313 this.joins.push('INNER JOIN "server" ON "server"."id" = "accountActor"."serverId"')
314
315 this.and.push('"server"."host" = :host')
316 this.replacements.host = host
317 }
318
294 private whereAccountId (accountId: number) { 319 private whereAccountId (accountId: number) {
295 this.and.push('"account"."id" = :accountId') 320 this.and.push('"account"."id" = :accountId')
296 this.replacements.accountId = accountId 321 this.replacements.accountId = accountId
@@ -304,16 +329,16 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
304 private whereFollowerActorId (followerActorId: number, includeLocalVideos: boolean) { 329 private whereFollowerActorId (followerActorId: number, includeLocalVideos: boolean) {
305 let query = 330 let query =
306 '(' + 331 '(' +
307 ' EXISTS (' + 332 ' EXISTS (' + // Videos shared by actors we follow
308 ' SELECT 1 FROM "videoShare" ' + 333 ' SELECT 1 FROM "videoShare" ' +
309 ' INNER JOIN "actorFollow" "actorFollowShare" ON "actorFollowShare"."targetActorId" = "videoShare"."actorId" ' + 334 ' INNER JOIN "actorFollow" "actorFollowShare" ON "actorFollowShare"."targetActorId" = "videoShare"."actorId" ' +
310 ' AND "actorFollowShare"."actorId" = :followerActorId AND "actorFollowShare"."state" = \'accepted\' ' + 335 ' AND "actorFollowShare"."actorId" = :followerActorId AND "actorFollowShare"."state" = \'accepted\' ' +
311 ' WHERE "videoShare"."videoId" = "video"."id"' + 336 ' WHERE "videoShare"."videoId" = "video"."id"' +
312 ' )' + 337 ' )' +
313 ' OR' + 338 ' OR' +
314 ' EXISTS (' + 339 ' EXISTS (' + // Videos published by accounts we follow
315 ' SELECT 1 from "actorFollow" ' + 340 ' SELECT 1 from "actorFollow" ' +
316 ' WHERE "actorFollow"."targetActorId" = "videoChannel"."actorId" AND "actorFollow"."actorId" = :followerActorId ' + 341 ' WHERE "actorFollow"."targetActorId" = "account"."actorId" AND "actorFollow"."actorId" = :followerActorId ' +
317 ' AND "actorFollow"."state" = \'accepted\'' + 342 ' AND "actorFollow"."state" = \'accepted\'' +
318 ' )' 343 ' )'
319 344
@@ -367,6 +392,10 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
367 ) 392 )
368 } 393 }
369 394
395 private whereUUIDs (uuids: string[]) {
396 this.and.push('"video"."uuid" IN (' + createSafeIn(this.sequelize, uuids) + ')')
397 }
398
370 private whereCategoryOneOf (categoryOneOf: number[]) { 399 private whereCategoryOneOf (categoryOneOf: number[]) {
371 this.and.push('"video"."category" IN (:categoryOneOf)') 400 this.and.push('"video"."category" IN (:categoryOneOf)')
372 this.replacements.categoryOneOf = categoryOneOf 401 this.replacements.categoryOneOf = categoryOneOf
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 183e7448c..9f04a57c6 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -1,4 +1,4 @@
1import { FindOptions, Includeable, literal, Op, QueryTypes, ScopeOptions, Transaction } from 'sequelize' 1import { FindOptions, Includeable, literal, Op, QueryTypes, ScopeOptions, Transaction, WhereOptions } from 'sequelize'
2import { 2import {
3 AllowNull, 3 AllowNull,
4 BeforeDestroy, 4 BeforeDestroy,
@@ -17,9 +17,8 @@ import {
17 Table, 17 Table,
18 UpdatedAt 18 UpdatedAt
19} from 'sequelize-typescript' 19} from 'sequelize-typescript'
20import { setAsUpdated } from '@server/helpers/database-utils'
21import { MAccountActor } from '@server/types/models' 20import { MAccountActor } from '@server/types/models'
22import { AttributesOnly } from '@shared/core-utils' 21import { AttributesOnly, pick } from '@shared/core-utils'
23import { ActivityPubActor } from '../../../shared/models/activitypub' 22import { ActivityPubActor } from '../../../shared/models/activitypub'
24import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos' 23import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos'
25import { 24import {
@@ -41,6 +40,7 @@ import { ActorModel, unusedActorAttributesForAPI } from '../actor/actor'
41import { ActorFollowModel } from '../actor/actor-follow' 40import { ActorFollowModel } from '../actor/actor-follow'
42import { ActorImageModel } from '../actor/actor-image' 41import { ActorImageModel } from '../actor/actor-image'
43import { ServerModel } from '../server/server' 42import { ServerModel } from '../server/server'
43import { setAsUpdated } from '../shared'
44import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils' 44import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils'
45import { VideoModel } from './video' 45import { VideoModel } from './video'
46import { VideoPlaylistModel } from './video-playlist' 46import { VideoPlaylistModel } from './video-playlist'
@@ -58,6 +58,8 @@ export enum ScopeNames {
58type AvailableForListOptions = { 58type AvailableForListOptions = {
59 actorId: number 59 actorId: number
60 search?: string 60 search?: string
61 host?: string
62 handles?: string[]
61} 63}
62 64
63type AvailableWithStatsOptions = { 65type AvailableWithStatsOptions = {
@@ -83,7 +85,62 @@ export type SummaryOptions = {
83 // Only list local channels OR channels that are on an instance followed by actorId 85 // Only list local channels OR channels that are on an instance followed by actorId
84 const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId) 86 const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId)
85 87
88 const whereActorAnd: WhereOptions[] = [
89 {
90 [Op.or]: [
91 {
92 serverId: null
93 },
94 {
95 serverId: {
96 [Op.in]: Sequelize.literal(inQueryInstanceFollow)
97 }
98 }
99 ]
100 }
101 ]
102
103 let serverRequired = false
104 let whereServer: WhereOptions
105
106 if (options.host && options.host !== WEBSERVER.HOST) {
107 serverRequired = true
108 whereServer = { host: options.host }
109 }
110
111 if (options.host === WEBSERVER.HOST) {
112 whereActorAnd.push({
113 serverId: null
114 })
115 }
116
117 let rootWhere: WhereOptions
118 if (options.handles) {
119 const or: WhereOptions[] = []
120
121 for (const handle of options.handles || []) {
122 const [ preferredUsername, host ] = handle.split('@')
123
124 if (!host) {
125 or.push({
126 '$Actor.preferredUsername$': preferredUsername,
127 '$Actor.serverId$': null
128 })
129 } else {
130 or.push({
131 '$Actor.preferredUsername$': preferredUsername,
132 '$Actor.Server.host$': host
133 })
134 }
135 }
136
137 rootWhere = {
138 [Op.or]: or
139 }
140 }
141
86 return { 142 return {
143 where: rootWhere,
87 include: [ 144 include: [
88 { 145 {
89 attributes: { 146 attributes: {
@@ -91,19 +148,20 @@ export type SummaryOptions = {
91 }, 148 },
92 model: ActorModel, 149 model: ActorModel,
93 where: { 150 where: {
94 [Op.or]: [ 151 [Op.and]: whereActorAnd
95 {
96 serverId: null
97 },
98 {
99 serverId: {
100 [Op.in]: Sequelize.literal(inQueryInstanceFollow)
101 }
102 }
103 ]
104 }, 152 },
105 include: [ 153 include: [
106 { 154 {
155 model: ServerModel,
156 required: serverRequired,
157 where: whereServer
158 },
159 {
160 model: ActorImageModel,
161 as: 'Avatar',
162 required: false
163 },
164 {
107 model: ActorImageModel, 165 model: ActorImageModel,
108 as: 'Banner', 166 as: 'Banner',
109 required: false 167 required: false
@@ -380,30 +438,6 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
380 } 438 }
381 } 439 }
382 440
383 static listForApi (parameters: {
384 actorId: number
385 start: number
386 count: number
387 sort: string
388 }) {
389 const { actorId } = parameters
390
391 const query = {
392 offset: parameters.start,
393 limit: parameters.count,
394 order: getSort(parameters.sort)
395 }
396
397 return VideoChannelModel
398 .scope({
399 method: [ ScopeNames.FOR_API, { actorId } as AvailableForListOptions ]
400 })
401 .findAndCountAll(query)
402 .then(({ rows, count }) => {
403 return { total: count, data: rows }
404 })
405 }
406
407 static listLocalsForSitemap (sort: string): Promise<MChannelActor[]> { 441 static listLocalsForSitemap (sort: string): Promise<MChannelActor[]> {
408 const query = { 442 const query = {
409 attributes: [ ], 443 attributes: [ ],
@@ -425,26 +459,43 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
425 .findAll(query) 459 .findAll(query)
426 } 460 }
427 461
428 static searchForApi (options: { 462 static listForApi (parameters: Pick<AvailableForListOptions, 'actorId'> & {
429 actorId: number
430 search: string
431 start: number 463 start: number
432 count: number 464 count: number
433 sort: string 465 sort: string
434 }) { 466 }) {
435 const attributesInclude = [] 467 const { actorId } = parameters
436 const escapedSearch = VideoChannelModel.sequelize.escape(options.search)
437 const escapedLikeSearch = VideoChannelModel.sequelize.escape('%' + options.search + '%')
438 attributesInclude.push(createSimilarityAttribute('VideoChannelModel.name', options.search))
439 468
440 const query = { 469 const query = {
441 attributes: { 470 offset: parameters.start,
442 include: attributesInclude 471 limit: parameters.count,
443 }, 472 order: getSort(parameters.sort)
444 offset: options.start, 473 }
445 limit: options.count, 474
446 order: getSort(options.sort), 475 return VideoChannelModel
447 where: { 476 .scope({
477 method: [ ScopeNames.FOR_API, { actorId } as AvailableForListOptions ]
478 })
479 .findAndCountAll(query)
480 .then(({ rows, count }) => {
481 return { total: count, data: rows }
482 })
483 }
484
485 static searchForApi (options: Pick<AvailableForListOptions, 'actorId' | 'search' | 'host' | 'handles'> & {
486 start: number
487 count: number
488 sort: string
489 }) {
490 let attributesInclude: any[] = [ literal('0 as similarity') ]
491 let where: WhereOptions
492
493 if (options.search) {
494 const escapedSearch = VideoChannelModel.sequelize.escape(options.search)
495 const escapedLikeSearch = VideoChannelModel.sequelize.escape('%' + options.search + '%')
496 attributesInclude = [ createSimilarityAttribute('VideoChannelModel.name', options.search) ]
497
498 where = {
448 [Op.or]: [ 499 [Op.or]: [
449 Sequelize.literal( 500 Sequelize.literal(
450 'lower(immutable_unaccent("VideoChannelModel"."name")) % lower(immutable_unaccent(' + escapedSearch + '))' 501 'lower(immutable_unaccent("VideoChannelModel"."name")) % lower(immutable_unaccent(' + escapedSearch + '))'
@@ -456,9 +507,19 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
456 } 507 }
457 } 508 }
458 509
510 const query = {
511 attributes: {
512 include: attributesInclude
513 },
514 offset: options.start,
515 limit: options.count,
516 order: getSort(options.sort),
517 where
518 }
519
459 return VideoChannelModel 520 return VideoChannelModel
460 .scope({ 521 .scope({
461 method: [ ScopeNames.FOR_API, { actorId: options.actorId } as AvailableForListOptions ] 522 method: [ ScopeNames.FOR_API, pick(options, [ 'actorId', 'host', 'handles' ]) as AvailableForListOptions ]
462 }) 523 })
463 .findAndCountAll(query) 524 .findAndCountAll(query)
464 .then(({ rows, count }) => { 525 .then(({ rows, count }) => {
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts
index 22cf63804..09fc5288b 100644
--- a/server/models/video/video-file.ts
+++ b/server/models/video/video-file.ts
@@ -1,7 +1,7 @@
1import { remove } from 'fs-extra' 1import { remove } from 'fs-extra'
2import * as memoizee from 'memoizee' 2import * as memoizee from 'memoizee'
3import { join } from 'path' 3import { join } from 'path'
4import { FindOptions, Op, QueryTypes, Transaction } from 'sequelize' 4import { FindOptions, Op, Transaction } from 'sequelize'
5import { 5import {
6 AllowNull, 6 AllowNull,
7 BelongsTo, 7 BelongsTo,
@@ -44,6 +44,7 @@ import {
44} from '../../initializers/constants' 44} from '../../initializers/constants'
45import { MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '../../types/models/video/video-file' 45import { MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '../../types/models/video/video-file'
46import { VideoRedundancyModel } from '../redundancy/video-redundancy' 46import { VideoRedundancyModel } from '../redundancy/video-redundancy'
47import { doesExist } from '../shared'
47import { parseAggregateResult, throwIfNotValid } from '../utils' 48import { parseAggregateResult, throwIfNotValid } from '../utils'
48import { VideoModel } from './video' 49import { VideoModel } from './video'
49import { VideoStreamingPlaylistModel } from './video-streaming-playlist' 50import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
@@ -250,14 +251,8 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
250 251
251 static doesInfohashExist (infoHash: string) { 252 static doesInfohashExist (infoHash: string) {
252 const query = 'SELECT 1 FROM "videoFile" WHERE "infoHash" = $infoHash LIMIT 1' 253 const query = 'SELECT 1 FROM "videoFile" WHERE "infoHash" = $infoHash LIMIT 1'
253 const options = {
254 type: QueryTypes.SELECT as QueryTypes.SELECT,
255 bind: { infoHash },
256 raw: true
257 }
258 254
259 return VideoModel.sequelize.query(query, options) 255 return doesExist(query, { infoHash })
260 .then(results => results.length === 1)
261 } 256 }
262 257
263 static async doesVideoExistForVideoFile (id: number, videoIdOrUUID: number | string) { 258 static async doesVideoExistForVideoFile (id: number, videoIdOrUUID: number | string) {
@@ -266,6 +261,33 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
266 return !!videoFile 261 return !!videoFile
267 } 262 }
268 263
264 static async doesOwnedTorrentFileExist (filename: string) {
265 const query = 'SELECT 1 FROM "videoFile" ' +
266 'LEFT JOIN "video" "webtorrent" ON "webtorrent"."id" = "videoFile"."videoId" AND "webtorrent"."remote" IS FALSE ' +
267 'LEFT JOIN "videoStreamingPlaylist" ON "videoStreamingPlaylist"."id" = "videoFile"."videoStreamingPlaylistId" ' +
268 'LEFT JOIN "video" "hlsVideo" ON "hlsVideo"."id" = "videoStreamingPlaylist"."videoId" AND "hlsVideo"."remote" IS FALSE ' +
269 'WHERE "torrentFilename" = $filename AND ("hlsVideo"."id" IS NOT NULL OR "webtorrent"."id" IS NOT NULL) LIMIT 1'
270
271 return doesExist(query, { filename })
272 }
273
274 static async doesOwnedWebTorrentVideoFileExist (filename: string) {
275 const query = 'SELECT 1 FROM "videoFile" INNER JOIN "video" ON "video"."id" = "videoFile"."videoId" AND "video"."remote" IS FALSE ' +
276 'WHERE "filename" = $filename LIMIT 1'
277
278 return doesExist(query, { filename })
279 }
280
281 static loadByFilename (filename: string) {
282 const query = {
283 where: {
284 filename
285 }
286 }
287
288 return VideoFileModel.findOne(query)
289 }
290
269 static loadWithVideoOrPlaylistByTorrentFilename (filename: string) { 291 static loadWithVideoOrPlaylistByTorrentFilename (filename: string) {
270 const query = { 292 const query = {
271 where: { 293 where: {
@@ -443,10 +465,9 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
443 } 465 }
444 466
445 getFileDownloadUrl (video: MVideoWithHost) { 467 getFileDownloadUrl (video: MVideoWithHost) {
446 const basePath = this.isHLS() 468 const path = this.isHLS()
447 ? STATIC_DOWNLOAD_PATHS.HLS_VIDEOS 469 ? join(STATIC_DOWNLOAD_PATHS.HLS_VIDEOS, `${video.uuid}-${this.resolution}-fragmented${this.extname}`)
448 : STATIC_DOWNLOAD_PATHS.VIDEOS 470 : join(STATIC_DOWNLOAD_PATHS.VIDEOS, `${video.uuid}-${this.resolution}${this.extname}`)
449 const path = join(basePath, this.filename)
450 471
451 if (video.isOwned()) return WEBSERVER.URL + path 472 if (video.isOwned()) return WEBSERVER.URL + path
452 473
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts
index af81c9906..630684a88 100644
--- a/server/models/video/video-playlist.ts
+++ b/server/models/video/video-playlist.ts
@@ -17,10 +17,9 @@ import {
17 Table, 17 Table,
18 UpdatedAt 18 UpdatedAt
19} from 'sequelize-typescript' 19} from 'sequelize-typescript'
20import { setAsUpdated } from '@server/helpers/database-utils'
21import { buildUUID, uuidToShort } from '@server/helpers/uuid' 20import { buildUUID, uuidToShort } from '@server/helpers/uuid'
22import { MAccountId, MChannelId } from '@server/types/models' 21import { MAccountId, MChannelId } from '@server/types/models'
23import { AttributesOnly } from '@shared/core-utils' 22import { AttributesOnly, buildPlaylistEmbedPath, buildPlaylistWatchPath, pick } from '@shared/core-utils'
24import { ActivityIconObject } from '../../../shared/models/activitypub/objects' 23import { ActivityIconObject } from '../../../shared/models/activitypub/objects'
25import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' 24import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object'
26import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' 25import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
@@ -53,6 +52,7 @@ import {
53} from '../../types/models/video/video-playlist' 52} from '../../types/models/video/video-playlist'
54import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account' 53import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account'
55import { ActorModel } from '../actor/actor' 54import { ActorModel } from '../actor/actor'
55import { setAsUpdated } from '../shared'
56import { 56import {
57 buildServerIdsFollowedBy, 57 buildServerIdsFollowedBy,
58 buildTrigramSearchIndex, 58 buildTrigramSearchIndex,
@@ -82,6 +82,8 @@ type AvailableForListOptions = {
82 videoChannelId?: number 82 videoChannelId?: number
83 listMyPlaylists?: boolean 83 listMyPlaylists?: boolean
84 search?: string 84 search?: string
85 host?: string
86 uuids?: string[]
85 withVideos?: boolean 87 withVideos?: boolean
86} 88}
87 89
@@ -141,9 +143,19 @@ function getVideoLengthSelect () {
141 ] 143 ]
142 }, 144 },
143 [ScopeNames.AVAILABLE_FOR_LIST]: (options: AvailableForListOptions) => { 145 [ScopeNames.AVAILABLE_FOR_LIST]: (options: AvailableForListOptions) => {
146 const whereAnd: WhereOptions[] = []
147
148 const whereServer = options.host && options.host !== WEBSERVER.HOST
149 ? { host: options.host }
150 : undefined
151
144 let whereActor: WhereOptions = {} 152 let whereActor: WhereOptions = {}
145 153
146 const whereAnd: WhereOptions[] = [] 154 if (options.host === WEBSERVER.HOST) {
155 whereActor = {
156 [Op.and]: [ { serverId: null } ]
157 }
158 }
147 159
148 if (options.listMyPlaylists !== true) { 160 if (options.listMyPlaylists !== true) {
149 whereAnd.push({ 161 whereAnd.push({
@@ -168,9 +180,7 @@ function getVideoLengthSelect () {
168 }) 180 })
169 } 181 }
170 182
171 whereActor = { 183 Object.assign(whereActor, { [Op.or]: whereActorOr })
172 [Op.or]: whereActorOr
173 }
174 } 184 }
175 185
176 if (options.accountId) { 186 if (options.accountId) {
@@ -191,18 +201,26 @@ function getVideoLengthSelect () {
191 }) 201 })
192 } 202 }
193 203
204 if (options.uuids) {
205 whereAnd.push({
206 uuid: {
207 [Op.in]: options.uuids
208 }
209 })
210 }
211
194 if (options.withVideos === true) { 212 if (options.withVideos === true) {
195 whereAnd.push( 213 whereAnd.push(
196 literal(`(${getVideoLengthSelect()}) != 0`) 214 literal(`(${getVideoLengthSelect()}) != 0`)
197 ) 215 )
198 } 216 }
199 217
200 const attributesInclude = [] 218 let attributesInclude: any[] = [ literal('0 as similarity') ]
201 219
202 if (options.search) { 220 if (options.search) {
203 const escapedSearch = VideoPlaylistModel.sequelize.escape(options.search) 221 const escapedSearch = VideoPlaylistModel.sequelize.escape(options.search)
204 const escapedLikeSearch = VideoPlaylistModel.sequelize.escape('%' + options.search + '%') 222 const escapedLikeSearch = VideoPlaylistModel.sequelize.escape('%' + options.search + '%')
205 attributesInclude.push(createSimilarityAttribute('VideoPlaylistModel.name', options.search)) 223 attributesInclude = [ createSimilarityAttribute('VideoPlaylistModel.name', options.search) ]
206 224
207 whereAnd.push({ 225 whereAnd.push({
208 [Op.or]: [ 226 [Op.or]: [
@@ -228,7 +246,7 @@ function getVideoLengthSelect () {
228 include: [ 246 include: [
229 { 247 {
230 model: AccountModel.scope({ 248 model: AccountModel.scope({
231 method: [ AccountScopeNames.SUMMARY, { whereActor } as SummaryOptions ] 249 method: [ AccountScopeNames.SUMMARY, { whereActor, whereServer } as SummaryOptions ]
232 }), 250 }),
233 required: true 251 required: true
234 }, 252 },
@@ -339,17 +357,10 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
339 }) 357 })
340 Thumbnail: ThumbnailModel 358 Thumbnail: ThumbnailModel
341 359
342 static listForApi (options: { 360 static listForApi (options: AvailableForListOptions & {
343 followerActorId: number
344 start: number 361 start: number
345 count: number 362 count: number
346 sort: string 363 sort: string
347 type?: VideoPlaylistType
348 accountId?: number
349 videoChannelId?: number
350 listMyPlaylists?: boolean
351 search?: string
352 withVideos?: boolean // false by default
353 }) { 364 }) {
354 const query = { 365 const query = {
355 offset: options.start, 366 offset: options.start,
@@ -362,12 +373,8 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
362 method: [ 373 method: [
363 ScopeNames.AVAILABLE_FOR_LIST, 374 ScopeNames.AVAILABLE_FOR_LIST,
364 { 375 {
365 type: options.type, 376 ...pick(options, [ 'type', 'followerActorId', 'accountId', 'videoChannelId', 'listMyPlaylists', 'search', 'host', 'uuids' ]),
366 followerActorId: options.followerActorId, 377
367 accountId: options.accountId,
368 videoChannelId: options.videoChannelId,
369 listMyPlaylists: options.listMyPlaylists,
370 search: options.search,
371 withVideos: options.withVideos || false 378 withVideos: options.withVideos || false
372 } as AvailableForListOptions 379 } as AvailableForListOptions
373 ] 380 ]
@@ -384,15 +391,14 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
384 }) 391 })
385 } 392 }
386 393
387 static searchForApi (options: { 394 static searchForApi (options: Pick<AvailableForListOptions, 'followerActorId' | 'search'| 'host'| 'uuids'> & {
388 followerActorId: number
389 start: number 395 start: number
390 count: number 396 count: number
391 sort: string 397 sort: string
392 search?: string
393 }) { 398 }) {
394 return VideoPlaylistModel.listForApi({ 399 return VideoPlaylistModel.listForApi({
395 ...options, 400 ...options,
401
396 type: VideoPlaylistType.REGULAR, 402 type: VideoPlaylistType.REGULAR,
397 listMyPlaylists: false, 403 listMyPlaylists: false,
398 withVideos: true 404 withVideos: true
@@ -560,12 +566,12 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
560 return join(STATIC_PATHS.THUMBNAILS, this.Thumbnail.filename) 566 return join(STATIC_PATHS.THUMBNAILS, this.Thumbnail.filename)
561 } 567 }
562 568
563 getWatchUrl () { 569 getWatchStaticPath () {
564 return WEBSERVER.URL + '/w/p/' + this.uuid 570 return buildPlaylistWatchPath({ shortUUID: uuidToShort(this.uuid) })
565 } 571 }
566 572
567 getEmbedStaticPath () { 573 getEmbedStaticPath () {
568 return '/video-playlists/embed/' + this.uuid 574 return buildPlaylistEmbedPath(this)
569 } 575 }
570 576
571 static async getStats () { 577 static async getStats () {
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts
index d627e8c9d..d591a3134 100644
--- a/server/models/video/video-streaming-playlist.ts
+++ b/server/models/video/video-streaming-playlist.ts
@@ -1,19 +1,27 @@
1import * as memoizee from 'memoizee' 1import * as memoizee from 'memoizee'
2import { join } from 'path' 2import { join } from 'path'
3import { Op, QueryTypes } from 'sequelize' 3import { Op } from 'sequelize'
4import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 4import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
5import { VideoFileModel } from '@server/models/video/video-file' 5import { VideoFileModel } from '@server/models/video/video-file'
6import { MStreamingPlaylist } from '@server/types/models' 6import { MStreamingPlaylist, MVideo } from '@server/types/models'
7import { AttributesOnly } from '@shared/core-utils'
7import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' 8import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
8import { sha1 } from '../../helpers/core-utils' 9import { sha1 } from '../../helpers/core-utils'
9import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 10import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
10import { isArrayOf } from '../../helpers/custom-validators/misc' 11import { isArrayOf } from '../../helpers/custom-validators/misc'
11import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' 12import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos'
12import { CONSTRAINTS_FIELDS, MEMOIZE_LENGTH, MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_VERSION, STATIC_PATHS } from '../../initializers/constants' 13import {
14 CONSTRAINTS_FIELDS,
15 MEMOIZE_LENGTH,
16 MEMOIZE_TTL,
17 P2P_MEDIA_LOADER_PEER_VERSION,
18 STATIC_PATHS,
19 WEBSERVER
20} from '../../initializers/constants'
13import { VideoRedundancyModel } from '../redundancy/video-redundancy' 21import { VideoRedundancyModel } from '../redundancy/video-redundancy'
22import { doesExist } from '../shared'
14import { throwIfNotValid } from '../utils' 23import { throwIfNotValid } from '../utils'
15import { VideoModel } from './video' 24import { VideoModel } from './video'
16import { AttributesOnly } from '@shared/core-utils'
17 25
18@Table({ 26@Table({
19 tableName: 'videoStreamingPlaylist', 27 tableName: 'videoStreamingPlaylist',
@@ -43,7 +51,11 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
43 type: VideoStreamingPlaylistType 51 type: VideoStreamingPlaylistType
44 52
45 @AllowNull(false) 53 @AllowNull(false)
46 @Is('PlaylistUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'playlist url')) 54 @Column
55 playlistFilename: string
56
57 @AllowNull(true)
58 @Is('PlaylistUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'playlist url', true))
47 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max)) 59 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max))
48 playlistUrl: string 60 playlistUrl: string
49 61
@@ -57,7 +69,11 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
57 p2pMediaLoaderPeerVersion: number 69 p2pMediaLoaderPeerVersion: number
58 70
59 @AllowNull(false) 71 @AllowNull(false)
60 @Is('VideoStreamingSegmentsSha256Url', value => throwIfNotValid(value, isActivityPubUrlValid, 'segments sha256 url')) 72 @Column
73 segmentsSha256Filename: string
74
75 @AllowNull(true)
76 @Is('VideoStreamingSegmentsSha256Url', value => throwIfNotValid(value, isActivityPubUrlValid, 'segments sha256 url', true))
61 @Column 77 @Column
62 segmentsSha256Url: string 78 segmentsSha256Url: string
63 79
@@ -98,14 +114,8 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
98 114
99 static doesInfohashExist (infoHash: string) { 115 static doesInfohashExist (infoHash: string) {
100 const query = 'SELECT 1 FROM "videoStreamingPlaylist" WHERE $infoHash = ANY("p2pMediaLoaderInfohashes") LIMIT 1' 116 const query = 'SELECT 1 FROM "videoStreamingPlaylist" WHERE $infoHash = ANY("p2pMediaLoaderInfohashes") LIMIT 1'
101 const options = {
102 type: QueryTypes.SELECT as QueryTypes.SELECT,
103 bind: { infoHash },
104 raw: true
105 }
106 117
107 return VideoModel.sequelize.query<object>(query, options) 118 return doesExist(query, { infoHash })
108 .then(results => results.length === 1)
109 } 119 }
110 120
111 static buildP2PMediaLoaderInfoHashes (playlistUrl: string, files: unknown[]) { 121 static buildP2PMediaLoaderInfoHashes (playlistUrl: string, files: unknown[]) {
@@ -125,7 +135,13 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
125 p2pMediaLoaderPeerVersion: { 135 p2pMediaLoaderPeerVersion: {
126 [Op.ne]: P2P_MEDIA_LOADER_PEER_VERSION 136 [Op.ne]: P2P_MEDIA_LOADER_PEER_VERSION
127 } 137 }
128 } 138 },
139 include: [
140 {
141 model: VideoModel.unscoped(),
142 required: true
143 }
144 ]
129 } 145 }
130 146
131 return VideoStreamingPlaylistModel.findAll(query) 147 return VideoStreamingPlaylistModel.findAll(query)
@@ -144,7 +160,7 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
144 return VideoStreamingPlaylistModel.findByPk(id, options) 160 return VideoStreamingPlaylistModel.findByPk(id, options)
145 } 161 }
146 162
147 static loadHLSPlaylistByVideo (videoId: number) { 163 static loadHLSPlaylistByVideo (videoId: number): Promise<MStreamingPlaylist> {
148 const options = { 164 const options = {
149 where: { 165 where: {
150 type: VideoStreamingPlaylistType.HLS, 166 type: VideoStreamingPlaylistType.HLS,
@@ -155,30 +171,29 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
155 return VideoStreamingPlaylistModel.findOne(options) 171 return VideoStreamingPlaylistModel.findOne(options)
156 } 172 }
157 173
158 static getHlsPlaylistFilename (resolution: number) { 174 static async loadOrGenerate (video: MVideo) {
159 return resolution + '.m3u8' 175 let playlist = await VideoStreamingPlaylistModel.loadHLSPlaylistByVideo(video.id)
160 } 176 if (!playlist) playlist = new VideoStreamingPlaylistModel()
161 177
162 static getMasterHlsPlaylistFilename () { 178 return Object.assign(playlist, { videoId: video.id, Video: video })
163 return 'master.m3u8'
164 } 179 }
165 180
166 static getHlsSha256SegmentsFilename () { 181 assignP2PMediaLoaderInfoHashes (video: MVideo, files: unknown[]) {
167 return 'segments-sha256.json' 182 const masterPlaylistUrl = this.getMasterPlaylistUrl(video)
168 }
169 183
170 static getHlsMasterPlaylistStaticPath (videoUUID: string) { 184 this.p2pMediaLoaderInfohashes = VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(masterPlaylistUrl, files)
171 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename())
172 } 185 }
173 186
174 static getHlsPlaylistStaticPath (videoUUID: string, resolution: number) { 187 getMasterPlaylistUrl (video: MVideo) {
175 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getHlsPlaylistFilename(resolution)) 188 if (video.isOwned()) return WEBSERVER.URL + this.getMasterPlaylistStaticPath(video.uuid)
189
190 return this.playlistUrl
176 } 191 }
177 192
178 static getHlsSha256SegmentsStaticPath (videoUUID: string, isLive: boolean) { 193 getSha256SegmentsUrl (video: MVideo) {
179 if (isLive) return join('/live', 'segments-sha256', videoUUID) 194 if (video.isOwned()) return WEBSERVER.URL + this.getSha256SegmentsStaticPath(video.uuid, video.isLive)
180 195
181 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getHlsSha256SegmentsFilename()) 196 return this.segmentsSha256Url
182 } 197 }
183 198
184 getStringType () { 199 getStringType () {
@@ -195,4 +210,14 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
195 return this.type === other.type && 210 return this.type === other.type &&
196 this.videoId === other.videoId 211 this.videoId === other.videoId
197 } 212 }
213
214 private getMasterPlaylistStaticPath (videoUUID: string) {
215 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, this.playlistFilename)
216 }
217
218 private getSha256SegmentsStaticPath (videoUUID: string, isLive: boolean) {
219 if (isLive) return join('/live', 'segments-sha256', videoUUID)
220
221 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, this.segmentsSha256Filename)
222 }
198} 223}
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 1e5648a36..56a5b0e18 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -24,14 +24,14 @@ import {
24 Table, 24 Table,
25 UpdatedAt 25 UpdatedAt
26} from 'sequelize-typescript' 26} from 'sequelize-typescript'
27import { setAsUpdated } from '@server/helpers/database-utils'
28import { buildNSFWFilter } from '@server/helpers/express-utils' 27import { buildNSFWFilter } from '@server/helpers/express-utils'
28import { uuidToShort } from '@server/helpers/uuid'
29import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video' 29import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
30import { LiveManager } from '@server/lib/live/live-manager' 30import { LiveManager } from '@server/lib/live/live-manager'
31import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths' 31import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths'
32import { getServerActor } from '@server/models/application/application' 32import { getServerActor } from '@server/models/application/application'
33import { ModelCache } from '@server/models/model-cache' 33import { ModelCache } from '@server/models/model-cache'
34import { AttributesOnly } from '@shared/core-utils' 34import { AttributesOnly, buildVideoEmbedPath, buildVideoWatchPath, pick } from '@shared/core-utils'
35import { VideoFile } from '@shared/models/videos/video-file.model' 35import { VideoFile } from '@shared/models/videos/video-file.model'
36import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared' 36import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared'
37import { VideoObject } from '../../../shared/models/activitypub/objects' 37import { VideoObject } from '../../../shared/models/activitypub/objects'
@@ -91,6 +91,7 @@ import { VideoRedundancyModel } from '../redundancy/video-redundancy'
91import { ServerModel } from '../server/server' 91import { ServerModel } from '../server/server'
92import { TrackerModel } from '../server/tracker' 92import { TrackerModel } from '../server/tracker'
93import { VideoTrackerModel } from '../server/video-tracker' 93import { VideoTrackerModel } from '../server/video-tracker'
94import { setAsUpdated } from '../shared'
94import { UserModel } from '../user/user' 95import { UserModel } from '../user/user'
95import { UserVideoHistoryModel } from '../user/user-video-history' 96import { UserVideoHistoryModel } from '../user/user-video-history'
96import { buildTrigramSearchIndex, buildWhereIdOrUUID, getVideoSort, isOutdated, throwIfNotValid } from '../utils' 97import { buildTrigramSearchIndex, buildWhereIdOrUUID, getVideoSort, isOutdated, throwIfNotValid } from '../utils'
@@ -762,8 +763,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
762 763
763 // Remove physical files and torrents 764 // Remove physical files and torrents
764 instance.VideoFiles.forEach(file => { 765 instance.VideoFiles.forEach(file => {
765 tasks.push(instance.removeFile(file)) 766 tasks.push(instance.removeFileAndTorrent(file))
766 tasks.push(file.removeTorrent())
767 }) 767 })
768 768
769 // Remove playlists file 769 // Remove playlists file
@@ -1070,7 +1070,8 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1070 const trendingDays = options.sort.endsWith('trending') 1070 const trendingDays = options.sort.endsWith('trending')
1071 ? CONFIG.TRENDING.VIDEOS.INTERVAL_DAYS 1071 ? CONFIG.TRENDING.VIDEOS.INTERVAL_DAYS
1072 : undefined 1072 : undefined
1073 let trendingAlgorithm 1073
1074 let trendingAlgorithm: string
1074 if (options.sort.endsWith('hot')) trendingAlgorithm = 'hot' 1075 if (options.sort.endsWith('hot')) trendingAlgorithm = 'hot'
1075 if (options.sort.endsWith('best')) trendingAlgorithm = 'best' 1076 if (options.sort.endsWith('best')) trendingAlgorithm = 'best'
1076 1077
@@ -1082,40 +1083,44 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1082 : serverActor.id 1083 : serverActor.id
1083 1084
1084 const queryOptions = { 1085 const queryOptions = {
1085 start: options.start, 1086 ...pick(options, [
1086 count: options.count, 1087 'start',
1087 sort: options.sort, 1088 'count',
1089 'sort',
1090 'nsfw',
1091 'isLive',
1092 'categoryOneOf',
1093 'licenceOneOf',
1094 'languageOneOf',
1095 'tagsOneOf',
1096 'tagsAllOf',
1097 'filter',
1098 'withFiles',
1099 'accountId',
1100 'videoChannelId',
1101 'videoPlaylistId',
1102 'includeLocalVideos',
1103 'user',
1104 'historyOfUser',
1105 'search'
1106 ]),
1107
1088 followerActorId, 1108 followerActorId,
1089 serverAccountId: serverActor.Account.id, 1109 serverAccountId: serverActor.Account.id,
1090 nsfw: options.nsfw,
1091 isLive: options.isLive,
1092 categoryOneOf: options.categoryOneOf,
1093 licenceOneOf: options.licenceOneOf,
1094 languageOneOf: options.languageOneOf,
1095 tagsOneOf: options.tagsOneOf,
1096 tagsAllOf: options.tagsAllOf,
1097 filter: options.filter,
1098 withFiles: options.withFiles,
1099 accountId: options.accountId,
1100 videoChannelId: options.videoChannelId,
1101 videoPlaylistId: options.videoPlaylistId,
1102 includeLocalVideos: options.includeLocalVideos,
1103 user: options.user,
1104 historyOfUser: options.historyOfUser,
1105 trendingDays, 1110 trendingDays,
1106 trendingAlgorithm, 1111 trendingAlgorithm
1107 search: options.search
1108 } 1112 }
1109 1113
1110 return VideoModel.getAvailableForApi(queryOptions, options.countVideos) 1114 return VideoModel.getAvailableForApi(queryOptions, options.countVideos)
1111 } 1115 }
1112 1116
1113 static async searchAndPopulateAccountAndServer (options: { 1117 static async searchAndPopulateAccountAndServer (options: {
1118 start: number
1119 count: number
1120 sort: string
1114 includeLocalVideos: boolean 1121 includeLocalVideos: boolean
1115 search?: string 1122 search?: string
1116 start?: number 1123 host?: string
1117 count?: number
1118 sort?: string
1119 startDate?: string // ISO 8601 1124 startDate?: string // ISO 8601
1120 endDate?: string // ISO 8601 1125 endDate?: string // ISO 8601
1121 originallyPublishedStartDate?: string 1126 originallyPublishedStartDate?: string
@@ -1131,41 +1136,38 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1131 durationMax?: number // seconds 1136 durationMax?: number // seconds
1132 user?: MUserAccountId 1137 user?: MUserAccountId
1133 filter?: VideoFilter 1138 filter?: VideoFilter
1139 uuids?: string[]
1134 }) { 1140 }) {
1135 const serverActor = await getServerActor() 1141 const serverActor = await getServerActor()
1136 1142
1137 const queryOptions = { 1143 const queryOptions = {
1138 followerActorId: serverActor.id, 1144 ...pick(options, [
1139 serverAccountId: serverActor.Account.id, 1145 'includeLocalVideos',
1140 1146 'nsfw',
1141 includeLocalVideos: options.includeLocalVideos, 1147 'isLive',
1142 nsfw: options.nsfw, 1148 'categoryOneOf',
1143 isLive: options.isLive, 1149 'licenceOneOf',
1144 1150 'languageOneOf',
1145 categoryOneOf: options.categoryOneOf, 1151 'tagsOneOf',
1146 licenceOneOf: options.licenceOneOf, 1152 'tagsAllOf',
1147 languageOneOf: options.languageOneOf, 1153 'user',
1154 'filter',
1155 'host',
1156 'start',
1157 'count',
1158 'sort',
1159 'startDate',
1160 'endDate',
1161 'originallyPublishedStartDate',
1162 'originallyPublishedEndDate',
1163 'durationMin',
1164 'durationMax',
1165 'uuids',
1166 'search'
1167 ]),
1148 1168
1149 tagsOneOf: options.tagsOneOf, 1169 followerActorId: serverActor.id,
1150 tagsAllOf: options.tagsAllOf, 1170 serverAccountId: serverActor.Account.id
1151
1152 user: options.user,
1153 filter: options.filter,
1154
1155 start: options.start,
1156 count: options.count,
1157 sort: options.sort,
1158
1159 startDate: options.startDate,
1160 endDate: options.endDate,
1161
1162 originallyPublishedStartDate: options.originallyPublishedStartDate,
1163 originallyPublishedEndDate: options.originallyPublishedEndDate,
1164
1165 durationMin: options.durationMin,
1166 durationMax: options.durationMax,
1167
1168 search: options.search
1169 } 1171 }
1170 1172
1171 return VideoModel.getAvailableForApi(queryOptions) 1173 return VideoModel.getAvailableForApi(queryOptions)
@@ -1579,11 +1581,11 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1579 } 1581 }
1580 1582
1581 getWatchStaticPath () { 1583 getWatchStaticPath () {
1582 return '/w/' + this.uuid 1584 return buildVideoWatchPath({ shortUUID: uuidToShort(this.uuid) })
1583 } 1585 }
1584 1586
1585 getEmbedStaticPath () { 1587 getEmbedStaticPath () {
1586 return '/videos/embed/' + this.uuid 1588 return buildVideoEmbedPath(this)
1587 } 1589 }
1588 1590
1589 getMiniatureStaticPath () { 1591 getMiniatureStaticPath () {
@@ -1670,10 +1672,13 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1670 .concat(toAdd) 1672 .concat(toAdd)
1671 } 1673 }
1672 1674
1673 removeFile (videoFile: MVideoFile, isRedundancy = false) { 1675 removeFileAndTorrent (videoFile: MVideoFile, isRedundancy = false) {
1674 const filePath = getVideoFilePath(this, videoFile, isRedundancy) 1676 const filePath = getVideoFilePath(this, videoFile, isRedundancy)
1675 return remove(filePath) 1677
1676 .catch(err => logger.warn('Cannot delete file %s.', filePath, { err })) 1678 const promises: Promise<any>[] = [ remove(filePath) ]
1679 if (!isRedundancy) promises.push(videoFile.removeTorrent())
1680
1681 return Promise.all(promises)
1677 } 1682 }
1678 1683
1679 async removeStreamingPlaylistFiles (streamingPlaylist: MStreamingPlaylist, isRedundancy = false) { 1684 async removeStreamingPlaylistFiles (streamingPlaylist: MStreamingPlaylist, isRedundancy = false) {
diff --git a/server/tests/api/activitypub/cleaner.ts b/server/tests/api/activitypub/cleaner.ts
index 75ef56ce3..51cf6e599 100644
--- a/server/tests/api/activitypub/cleaner.ts
+++ b/server/tests/api/activitypub/cleaner.ts
@@ -4,24 +4,18 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 closeAllSequelize, 7 createMultipleServers,
8 deleteAll,
9 doubleFollow, 8 doubleFollow,
10 getCount, 9 PeerTubeServer,
11 selectQuery, 10 setAccessTokensToServers,
12 setVideoField, 11 wait,
13 updateQuery, 12 waitJobs
14 wait 13} from '@shared/extra-utils'
15} from '../../../../shared/extra-utils'
16import { flushAndRunMultipleServers, ServerInfo, setAccessTokensToServers } from '../../../../shared/extra-utils/index'
17import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
18import { addVideoCommentThread, getVideoCommentThreads } from '../../../../shared/extra-utils/videos/video-comments'
19import { getVideo, rateVideo, uploadVideoAndGetId } from '../../../../shared/extra-utils/videos/videos'
20 14
21const expect = chai.expect 15const expect = chai.expect
22 16
23describe('Test AP cleaner', function () { 17describe('Test AP cleaner', function () {
24 let servers: ServerInfo[] = [] 18 let servers: PeerTubeServer[] = []
25 let videoUUID1: string 19 let videoUUID1: string
26 let videoUUID2: string 20 let videoUUID2: string
27 let videoUUID3: string 21 let videoUUID3: string
@@ -36,7 +30,7 @@ describe('Test AP cleaner', function () {
36 videos: { cleanup_remote_interactions: true } 30 videos: { cleanup_remote_interactions: true }
37 } 31 }
38 } 32 }
39 servers = await flushAndRunMultipleServers(3, config) 33 servers = await createMultipleServers(3, config)
40 34
41 // Get the access tokens 35 // Get the access tokens
42 await setAccessTokensToServers(servers) 36 await setAccessTokensToServers(servers)
@@ -52,9 +46,9 @@ describe('Test AP cleaner', function () {
52 // Create 1 comment per video 46 // Create 1 comment per video
53 // Update 1 remote URL and 1 local URL on 47 // Update 1 remote URL and 1 local URL on
54 48
55 videoUUID1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'server 1' })).uuid 49 videoUUID1 = (await servers[0].videos.quickUpload({ name: 'server 1' })).uuid
56 videoUUID2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'server 2' })).uuid 50 videoUUID2 = (await servers[1].videos.quickUpload({ name: 'server 2' })).uuid
57 videoUUID3 = (await uploadVideoAndGetId({ server: servers[2], videoName: 'server 3' })).uuid 51 videoUUID3 = (await servers[2].videos.quickUpload({ name: 'server 3' })).uuid
58 52
59 videoUUIDs = [ videoUUID1, videoUUID2, videoUUID3 ] 53 videoUUIDs = [ videoUUID1, videoUUID2, videoUUID3 ]
60 54
@@ -62,8 +56,8 @@ describe('Test AP cleaner', function () {
62 56
63 for (const server of servers) { 57 for (const server of servers) {
64 for (const uuid of videoUUIDs) { 58 for (const uuid of videoUUIDs) {
65 await rateVideo(server.url, server.accessToken, uuid, 'like') 59 await server.videos.rate({ id: uuid, rating: 'like' })
66 await addVideoCommentThread(server.url, server.accessToken, uuid, 'comment') 60 await server.comments.createThread({ videoId: uuid, text: 'comment' })
67 } 61 }
68 } 62 }
69 63
@@ -73,9 +67,10 @@ describe('Test AP cleaner', function () {
73 it('Should have the correct likes', async function () { 67 it('Should have the correct likes', async function () {
74 for (const server of servers) { 68 for (const server of servers) {
75 for (const uuid of videoUUIDs) { 69 for (const uuid of videoUUIDs) {
76 const res = await getVideo(server.url, uuid) 70 const video = await server.videos.get({ id: uuid })
77 expect(res.body.likes).to.equal(3) 71
78 expect(res.body.dislikes).to.equal(0) 72 expect(video.likes).to.equal(3)
73 expect(video.dislikes).to.equal(0)
79 } 74 }
80 } 75 }
81 }) 76 })
@@ -83,9 +78,9 @@ describe('Test AP cleaner', function () {
83 it('Should destroy server 3 internal likes and correctly clean them', async function () { 78 it('Should destroy server 3 internal likes and correctly clean them', async function () {
84 this.timeout(20000) 79 this.timeout(20000)
85 80
86 await deleteAll(servers[2].internalServerNumber, 'accountVideoRate') 81 await servers[2].sql.deleteAll('accountVideoRate')
87 for (const uuid of videoUUIDs) { 82 for (const uuid of videoUUIDs) {
88 await setVideoField(servers[2].internalServerNumber, uuid, 'likes', '0') 83 await servers[2].sql.setVideoField(uuid, 'likes', '0')
89 } 84 }
90 85
91 await wait(5000) 86 await wait(5000)
@@ -93,16 +88,16 @@ describe('Test AP cleaner', function () {
93 88
94 // Updated rates of my video 89 // Updated rates of my video
95 { 90 {
96 const res = await getVideo(servers[0].url, videoUUID1) 91 const video = await servers[0].videos.get({ id: videoUUID1 })
97 expect(res.body.likes).to.equal(2) 92 expect(video.likes).to.equal(2)
98 expect(res.body.dislikes).to.equal(0) 93 expect(video.dislikes).to.equal(0)
99 } 94 }
100 95
101 // Did not update rates of a remote video 96 // Did not update rates of a remote video
102 { 97 {
103 const res = await getVideo(servers[0].url, videoUUID2) 98 const video = await servers[0].videos.get({ id: videoUUID2 })
104 expect(res.body.likes).to.equal(3) 99 expect(video.likes).to.equal(3)
105 expect(res.body.dislikes).to.equal(0) 100 expect(video.dislikes).to.equal(0)
106 } 101 }
107 }) 102 })
108 103
@@ -111,7 +106,7 @@ describe('Test AP cleaner', function () {
111 106
112 for (const server of servers) { 107 for (const server of servers) {
113 for (const uuid of videoUUIDs) { 108 for (const uuid of videoUUIDs) {
114 await rateVideo(server.url, server.accessToken, uuid, 'dislike') 109 await server.videos.rate({ id: uuid, rating: 'dislike' })
115 } 110 }
116 } 111 }
117 112
@@ -119,9 +114,9 @@ describe('Test AP cleaner', function () {
119 114
120 for (const server of servers) { 115 for (const server of servers) {
121 for (const uuid of videoUUIDs) { 116 for (const uuid of videoUUIDs) {
122 const res = await getVideo(server.url, uuid) 117 const video = await server.videos.get({ id: uuid })
123 expect(res.body.likes).to.equal(0) 118 expect(video.likes).to.equal(0)
124 expect(res.body.dislikes).to.equal(3) 119 expect(video.dislikes).to.equal(3)
125 } 120 }
126 } 121 }
127 }) 122 })
@@ -129,10 +124,10 @@ describe('Test AP cleaner', function () {
129 it('Should destroy server 3 internal dislikes and correctly clean them', async function () { 124 it('Should destroy server 3 internal dislikes and correctly clean them', async function () {
130 this.timeout(20000) 125 this.timeout(20000)
131 126
132 await deleteAll(servers[2].internalServerNumber, 'accountVideoRate') 127 await servers[2].sql.deleteAll('accountVideoRate')
133 128
134 for (const uuid of videoUUIDs) { 129 for (const uuid of videoUUIDs) {
135 await setVideoField(servers[2].internalServerNumber, uuid, 'dislikes', '0') 130 await servers[2].sql.setVideoField(uuid, 'dislikes', '0')
136 } 131 }
137 132
138 await wait(5000) 133 await wait(5000)
@@ -140,31 +135,31 @@ describe('Test AP cleaner', function () {
140 135
141 // Updated rates of my video 136 // Updated rates of my video
142 { 137 {
143 const res = await getVideo(servers[0].url, videoUUID1) 138 const video = await servers[0].videos.get({ id: videoUUID1 })
144 expect(res.body.likes).to.equal(0) 139 expect(video.likes).to.equal(0)
145 expect(res.body.dislikes).to.equal(2) 140 expect(video.dislikes).to.equal(2)
146 } 141 }
147 142
148 // Did not update rates of a remote video 143 // Did not update rates of a remote video
149 { 144 {
150 const res = await getVideo(servers[0].url, videoUUID2) 145 const video = await servers[0].videos.get({ id: videoUUID2 })
151 expect(res.body.likes).to.equal(0) 146 expect(video.likes).to.equal(0)
152 expect(res.body.dislikes).to.equal(3) 147 expect(video.dislikes).to.equal(3)
153 } 148 }
154 }) 149 })
155 150
156 it('Should destroy server 3 internal shares and correctly clean them', async function () { 151 it('Should destroy server 3 internal shares and correctly clean them', async function () {
157 this.timeout(20000) 152 this.timeout(20000)
158 153
159 const preCount = await getCount(servers[0].internalServerNumber, 'videoShare') 154 const preCount = await servers[0].sql.getCount('videoShare')
160 expect(preCount).to.equal(6) 155 expect(preCount).to.equal(6)
161 156
162 await deleteAll(servers[2].internalServerNumber, 'videoShare') 157 await servers[2].sql.deleteAll('videoShare')
163 await wait(5000) 158 await wait(5000)
164 await waitJobs(servers) 159 await waitJobs(servers)
165 160
166 // Still 6 because we don't have remote shares on local videos 161 // Still 6 because we don't have remote shares on local videos
167 const postCount = await getCount(servers[0].internalServerNumber, 'videoShare') 162 const postCount = await servers[0].sql.getCount('videoShare')
168 expect(postCount).to.equal(6) 163 expect(postCount).to.equal(6)
169 }) 164 })
170 165
@@ -172,18 +167,18 @@ describe('Test AP cleaner', function () {
172 this.timeout(20000) 167 this.timeout(20000)
173 168
174 { 169 {
175 const res = await getVideoCommentThreads(servers[0].url, videoUUID1, 0, 5) 170 const { total } = await servers[0].comments.listThreads({ videoId: videoUUID1 })
176 expect(res.body.total).to.equal(3) 171 expect(total).to.equal(3)
177 } 172 }
178 173
179 await deleteAll(servers[2].internalServerNumber, 'videoComment') 174 await servers[2].sql.deleteAll('videoComment')
180 175
181 await wait(5000) 176 await wait(5000)
182 await waitJobs(servers) 177 await waitJobs(servers)
183 178
184 { 179 {
185 const res = await getVideoCommentThreads(servers[0].url, videoUUID1, 0, 5) 180 const { total } = await servers[0].comments.listThreads({ videoId: videoUUID1 })
186 expect(res.body.total).to.equal(2) 181 expect(total).to.equal(2)
187 } 182 }
188 }) 183 })
189 184
@@ -193,7 +188,7 @@ describe('Test AP cleaner', function () {
193 async function check (like: string, ofServerUrl: string, urlSuffix: string, remote: 'true' | 'false') { 188 async function check (like: string, ofServerUrl: string, urlSuffix: string, remote: 'true' | 'false') {
194 const query = `SELECT "videoId", "accountVideoRate".url FROM "accountVideoRate" ` + 189 const query = `SELECT "videoId", "accountVideoRate".url FROM "accountVideoRate" ` +
195 `INNER JOIN video ON "accountVideoRate"."videoId" = video.id AND remote IS ${remote} WHERE "accountVideoRate"."url" LIKE '${like}'` 190 `INNER JOIN video ON "accountVideoRate"."videoId" = video.id AND remote IS ${remote} WHERE "accountVideoRate"."url" LIKE '${like}'`
196 const res = await selectQuery(servers[0].internalServerNumber, query) 191 const res = await servers[0].sql.selectQuery(query)
197 192
198 for (const rate of res) { 193 for (const rate of res) {
199 const matcher = new RegExp(`^${ofServerUrl}/accounts/root/dislikes/\\d+${urlSuffix}$`) 194 const matcher = new RegExp(`^${ofServerUrl}/accounts/root/dislikes/\\d+${urlSuffix}$`)
@@ -222,7 +217,7 @@ describe('Test AP cleaner', function () {
222 217
223 { 218 {
224 const query = `UPDATE "accountVideoRate" SET url = url || 'stan'` 219 const query = `UPDATE "accountVideoRate" SET url = url || 'stan'`
225 await updateQuery(servers[1].internalServerNumber, query) 220 await servers[1].sql.updateQuery(query)
226 221
227 await wait(5000) 222 await wait(5000)
228 await waitJobs(servers) 223 await waitJobs(servers)
@@ -239,7 +234,7 @@ describe('Test AP cleaner', function () {
239 const query = `SELECT "videoId", "videoComment".url, uuid as "videoUUID" FROM "videoComment" ` + 234 const query = `SELECT "videoId", "videoComment".url, uuid as "videoUUID" FROM "videoComment" ` +
240 `INNER JOIN video ON "videoComment"."videoId" = video.id AND remote IS ${remote} WHERE "videoComment"."url" LIKE '${like}'` 235 `INNER JOIN video ON "videoComment"."videoId" = video.id AND remote IS ${remote} WHERE "videoComment"."url" LIKE '${like}'`
241 236
242 const res = await selectQuery(servers[0].internalServerNumber, query) 237 const res = await servers[0].sql.selectQuery(query)
243 238
244 for (const comment of res) { 239 for (const comment of res) {
245 const matcher = new RegExp(`${ofServerUrl}/videos/watch/${comment.videoUUID}/comments/\\d+${urlSuffix}`) 240 const matcher = new RegExp(`${ofServerUrl}/videos/watch/${comment.videoUUID}/comments/\\d+${urlSuffix}`)
@@ -265,7 +260,7 @@ describe('Test AP cleaner', function () {
265 260
266 { 261 {
267 const query = `UPDATE "videoComment" SET url = url || 'kyle'` 262 const query = `UPDATE "videoComment" SET url = url || 'kyle'`
268 await updateQuery(servers[1].internalServerNumber, query) 263 await servers[1].sql.updateQuery(query)
269 264
270 await wait(5000) 265 await wait(5000)
271 await waitJobs(servers) 266 await waitJobs(servers)
@@ -277,7 +272,5 @@ describe('Test AP cleaner', function () {
277 272
278 after(async function () { 273 after(async function () {
279 await cleanupTests(servers) 274 await cleanupTests(servers)
280
281 await closeAllSequelize(servers)
282 }) 275 })
283}) 276})
diff --git a/server/tests/api/activitypub/client.ts b/server/tests/api/activitypub/client.ts
index be94e219c..c3e4b7f74 100644
--- a/server/tests/api/activitypub/client.ts
+++ b/server/tests/api/activitypub/client.ts
@@ -2,24 +2,21 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoPlaylistPrivacy } from '@shared/models'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { 5import {
8 cleanupTests, 6 cleanupTests,
9 createVideoPlaylist, 7 createMultipleServers,
10 doubleFollow, 8 doubleFollow,
11 flushAndRunMultipleServers,
12 makeActivityPubGetRequest, 9 makeActivityPubGetRequest,
13 ServerInfo, 10 PeerTubeServer,
14 setAccessTokensToServers, 11 setAccessTokensToServers,
15 setDefaultVideoChannel, 12 setDefaultVideoChannel
16 uploadVideoAndGetId 13} from '@shared/extra-utils'
17} from '../../../../shared/extra-utils' 14import { HttpStatusCode, VideoPlaylistPrivacy } from '@shared/models'
18 15
19const expect = chai.expect 16const expect = chai.expect
20 17
21describe('Test activitypub', function () { 18describe('Test activitypub', function () {
22 let servers: ServerInfo[] = [] 19 let servers: PeerTubeServer[] = []
23 let video: { id: number, uuid: string, shortUUID: string } 20 let video: { id: number, uuid: string, shortUUID: string }
24 let playlist: { id: number, uuid: string, shortUUID: string } 21 let playlist: { id: number, uuid: string, shortUUID: string }
25 22
@@ -64,19 +61,18 @@ describe('Test activitypub', function () {
64 before(async function () { 61 before(async function () {
65 this.timeout(30000) 62 this.timeout(30000)
66 63
67 servers = await flushAndRunMultipleServers(2) 64 servers = await createMultipleServers(2)
68 65
69 await setAccessTokensToServers(servers) 66 await setAccessTokensToServers(servers)
70 await setDefaultVideoChannel(servers) 67 await setDefaultVideoChannel(servers)
71 68
72 { 69 {
73 video = await uploadVideoAndGetId({ server: servers[0], videoName: 'video' }) 70 video = await servers[0].videos.quickUpload({ name: 'video' })
74 } 71 }
75 72
76 { 73 {
77 const playlistAttrs = { displayName: 'playlist', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[0].videoChannel.id } 74 const attributes = { displayName: 'playlist', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[0].store.channel.id }
78 const resCreate = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs }) 75 playlist = await servers[0].playlists.create({ attributes })
79 playlist = resCreate.body.videoPlaylist
80 } 76 }
81 77
82 await doubleFollow(servers[0], servers[1]) 78 await doubleFollow(servers[0], servers[1])
diff --git a/server/tests/api/activitypub/fetch.ts b/server/tests/api/activitypub/fetch.ts
index 35fd94eed..422a75d6e 100644
--- a/server/tests/api/activitypub/fetch.ts
+++ b/server/tests/api/activitypub/fetch.ts
@@ -1,61 +1,44 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4
5import {
6 cleanupTests,
7 closeAllSequelize,
8 createUser,
9 doubleFollow,
10 flushAndRunMultipleServers,
11 getVideosListSort,
12 ServerInfo,
13 setAccessTokensToServers,
14 setActorField,
15 setVideoField,
16 uploadVideo,
17 userLogin,
18 waitJobs
19} from '../../../../shared/extra-utils'
20import * as chai from 'chai' 4import * as chai from 'chai'
21import { Video } from '../../../../shared/models/videos' 5import { cleanupTests, createMultipleServers, doubleFollow, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
22 6
23const expect = chai.expect 7const expect = chai.expect
24 8
25describe('Test ActivityPub fetcher', function () { 9describe('Test ActivityPub fetcher', function () {
26 let servers: ServerInfo[] 10 let servers: PeerTubeServer[]
27 11
28 // --------------------------------------------------------------- 12 // ---------------------------------------------------------------
29 13
30 before(async function () { 14 before(async function () {
31 this.timeout(60000) 15 this.timeout(60000)
32 16
33 servers = await flushAndRunMultipleServers(3) 17 servers = await createMultipleServers(3)
34 18
35 // Get the access tokens 19 // Get the access tokens
36 await setAccessTokensToServers(servers) 20 await setAccessTokensToServers(servers)
37 21
38 const user = { username: 'user1', password: 'password' } 22 const user = { username: 'user1', password: 'password' }
39 for (const server of servers) { 23 for (const server of servers) {
40 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 24 await server.users.create({ username: user.username, password: user.password })
41 } 25 }
42 26
43 const userAccessToken = await userLogin(servers[0], user) 27 const userAccessToken = await servers[0].login.getAccessToken(user)
44 28
45 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video root' }) 29 await servers[0].videos.upload({ attributes: { name: 'video root' } })
46 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'bad video root' }) 30 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'bad video root' } })
47 const badVideoUUID = res.body.video.uuid 31 await servers[0].videos.upload({ token: userAccessToken, attributes: { name: 'video user' } })
48 await uploadVideo(servers[0].url, userAccessToken, { name: 'video user' })
49 32
50 { 33 {
51 const to = 'http://localhost:' + servers[0].port + '/accounts/user1' 34 const to = 'http://localhost:' + servers[0].port + '/accounts/user1'
52 const value = 'http://localhost:' + servers[1].port + '/accounts/user1' 35 const value = 'http://localhost:' + servers[1].port + '/accounts/user1'
53 await setActorField(servers[0].internalServerNumber, to, 'url', value) 36 await servers[0].sql.setActorField(to, 'url', value)
54 } 37 }
55 38
56 { 39 {
57 const value = 'http://localhost:' + servers[2].port + '/videos/watch/' + badVideoUUID 40 const value = 'http://localhost:' + servers[2].port + '/videos/watch/' + uuid
58 await setVideoField(servers[0].internalServerNumber, badVideoUUID, 'url', value) 41 await servers[0].sql.setVideoField(uuid, 'url', value)
59 } 42 }
60 }) 43 })
61 44
@@ -66,20 +49,18 @@ describe('Test ActivityPub fetcher', function () {
66 await waitJobs(servers) 49 await waitJobs(servers)
67 50
68 { 51 {
69 const res = await getVideosListSort(servers[0].url, 'createdAt') 52 const { total, data } = await servers[0].videos.list({ sort: 'createdAt' })
70 expect(res.body.total).to.equal(3)
71 53
72 const data: Video[] = res.body.data 54 expect(total).to.equal(3)
73 expect(data[0].name).to.equal('video root') 55 expect(data[0].name).to.equal('video root')
74 expect(data[1].name).to.equal('bad video root') 56 expect(data[1].name).to.equal('bad video root')
75 expect(data[2].name).to.equal('video user') 57 expect(data[2].name).to.equal('video user')
76 } 58 }
77 59
78 { 60 {
79 const res = await getVideosListSort(servers[1].url, 'createdAt') 61 const { total, data } = await servers[1].videos.list({ sort: 'createdAt' })
80 expect(res.body.total).to.equal(1)
81 62
82 const data: Video[] = res.body.data 63 expect(total).to.equal(1)
83 expect(data[0].name).to.equal('video root') 64 expect(data[0].name).to.equal('video root')
84 } 65 }
85 }) 66 })
@@ -88,7 +69,5 @@ describe('Test ActivityPub fetcher', function () {
88 this.timeout(20000) 69 this.timeout(20000)
89 70
90 await cleanupTests(servers) 71 await cleanupTests(servers)
91
92 await closeAllSequelize(servers)
93 }) 72 })
94}) 73})
diff --git a/server/tests/api/activitypub/helpers.ts b/server/tests/api/activitypub/helpers.ts
index 66d7631b7..57b1cab23 100644
--- a/server/tests/api/activitypub/helpers.ts
+++ b/server/tests/api/activitypub/helpers.ts
@@ -2,11 +2,10 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { buildRequestStub } from '../../../../shared/extra-utils/miscs/stubs'
6import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../../../helpers/peertube-crypto'
7import { cloneDeep } from 'lodash' 5import { cloneDeep } from 'lodash'
6import { buildAbsoluteFixturePath, buildRequestStub } from '@shared/extra-utils'
8import { buildSignedActivity } from '../../../helpers/activitypub' 7import { buildSignedActivity } from '../../../helpers/activitypub'
9import { buildAbsoluteFixturePath } from '@shared/extra-utils' 8import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../../../helpers/peertube-crypto'
10 9
11describe('Test activity pub helpers', function () { 10describe('Test activity pub helpers', function () {
12 describe('When checking the Linked Signature', function () { 11 describe('When checking the Linked Signature', function () {
diff --git a/server/tests/api/activitypub/refresher.ts b/server/tests/api/activitypub/refresher.ts
index c717f1a30..81fee0044 100644
--- a/server/tests/api/activitypub/refresher.ts
+++ b/server/tests/api/activitypub/refresher.ts
@@ -2,32 +2,20 @@
2 2
3import 'mocha' 3import 'mocha'
4import { 4import {
5 cleanupTests, closeAllSequelize, 5 cleanupTests,
6 createVideoPlaylist, 6 createMultipleServers,
7 doubleFollow, 7 doubleFollow,
8 flushAndRunMultipleServers,
9 generateUserAccessToken,
10 getVideo,
11 getVideoPlaylist,
12 killallServers, 8 killallServers,
13 reRunServer, 9 PeerTubeServer,
14 ServerInfo,
15 setAccessTokensToServers, 10 setAccessTokensToServers,
16 setActorField,
17 setDefaultVideoChannel, 11 setDefaultVideoChannel,
18 setPlaylistField,
19 setVideoField,
20 uploadVideo,
21 uploadVideoAndGetId,
22 wait, 12 wait,
23 waitJobs 13 waitJobs
24} from '../../../../shared/extra-utils' 14} from '@shared/extra-utils'
25import { getAccount } from '../../../../shared/extra-utils/users/accounts' 15import { HttpStatusCode, VideoPlaylistPrivacy } from '@shared/models'
26import { VideoPlaylistPrivacy } from '../../../../shared/models/videos'
27import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
28 16
29describe('Test AP refresher', function () { 17describe('Test AP refresher', function () {
30 let servers: ServerInfo[] = [] 18 let servers: PeerTubeServer[] = []
31 let videoUUID1: string 19 let videoUUID1: string
32 let videoUUID2: string 20 let videoUUID2: string
33 let videoUUID3: string 21 let videoUUID3: string
@@ -37,36 +25,36 @@ describe('Test AP refresher', function () {
37 before(async function () { 25 before(async function () {
38 this.timeout(60000) 26 this.timeout(60000)
39 27
40 servers = await flushAndRunMultipleServers(2, { transcoding: { enabled: false } }) 28 servers = await createMultipleServers(2, { transcoding: { enabled: false } })
41 29
42 // Get the access tokens 30 // Get the access tokens
43 await setAccessTokensToServers(servers) 31 await setAccessTokensToServers(servers)
44 await setDefaultVideoChannel(servers) 32 await setDefaultVideoChannel(servers)
45 33
46 { 34 {
47 videoUUID1 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video1' })).uuid 35 videoUUID1 = (await servers[1].videos.quickUpload({ name: 'video1' })).uuid
48 videoUUID2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video2' })).uuid 36 videoUUID2 = (await servers[1].videos.quickUpload({ name: 'video2' })).uuid
49 videoUUID3 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video3' })).uuid 37 videoUUID3 = (await servers[1].videos.quickUpload({ name: 'video3' })).uuid
50 } 38 }
51 39
52 { 40 {
53 const a1 = await generateUserAccessToken(servers[1], 'user1') 41 const token1 = await servers[1].users.generateUserAndToken('user1')
54 await uploadVideo(servers[1].url, a1, { name: 'video4' }) 42 await servers[1].videos.upload({ token: token1, attributes: { name: 'video4' } })
55 43
56 const a2 = await generateUserAccessToken(servers[1], 'user2') 44 const token2 = await servers[1].users.generateUserAndToken('user2')
57 await uploadVideo(servers[1].url, a2, { name: 'video5' }) 45 await servers[1].videos.upload({ token: token2, attributes: { name: 'video5' } })
58 } 46 }
59 47
60 { 48 {
61 const playlistAttrs = { displayName: 'playlist1', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id } 49 const attributes = { displayName: 'playlist1', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].store.channel.id }
62 const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs }) 50 const created = await servers[1].playlists.create({ attributes })
63 playlistUUID1 = res.body.videoPlaylist.uuid 51 playlistUUID1 = created.uuid
64 } 52 }
65 53
66 { 54 {
67 const playlistAttrs = { displayName: 'playlist2', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id } 55 const attributes = { displayName: 'playlist2', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].store.channel.id }
68 const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs }) 56 const created = await servers[1].playlists.create({ attributes })
69 playlistUUID2 = res.body.videoPlaylist.uuid 57 playlistUUID2 = created.uuid
70 } 58 }
71 59
72 await doubleFollow(servers[0], servers[1]) 60 await doubleFollow(servers[0], servers[1])
@@ -80,34 +68,34 @@ describe('Test AP refresher', function () {
80 await wait(10000) 68 await wait(10000)
81 69
82 // Change UUID so the remote server returns a 404 70 // Change UUID so the remote server returns a 404
83 await setVideoField(servers[1].internalServerNumber, videoUUID1, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174f') 71 await servers[1].sql.setVideoField(videoUUID1, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174f')
84 72
85 await getVideo(servers[0].url, videoUUID1) 73 await servers[0].videos.get({ id: videoUUID1 })
86 await getVideo(servers[0].url, videoUUID2) 74 await servers[0].videos.get({ id: videoUUID2 })
87 75
88 await waitJobs(servers) 76 await waitJobs(servers)
89 77
90 await getVideo(servers[0].url, videoUUID1, HttpStatusCode.NOT_FOUND_404) 78 await servers[0].videos.get({ id: videoUUID1, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
91 await getVideo(servers[0].url, videoUUID2, HttpStatusCode.OK_200) 79 await servers[0].videos.get({ id: videoUUID2 })
92 }) 80 })
93 81
94 it('Should not update a remote video if the remote instance is down', async function () { 82 it('Should not update a remote video if the remote instance is down', async function () {
95 this.timeout(70000) 83 this.timeout(70000)
96 84
97 killallServers([ servers[1] ]) 85 await killallServers([ servers[1] ])
98 86
99 await setVideoField(servers[1].internalServerNumber, videoUUID3, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174e') 87 await servers[1].sql.setVideoField(videoUUID3, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174e')
100 88
101 // Video will need a refresh 89 // Video will need a refresh
102 await wait(10000) 90 await wait(10000)
103 91
104 await getVideo(servers[0].url, videoUUID3) 92 await servers[0].videos.get({ id: videoUUID3 })
105 // The refresh should fail 93 // The refresh should fail
106 await waitJobs([ servers[0] ]) 94 await waitJobs([ servers[0] ])
107 95
108 await reRunServer(servers[1]) 96 await servers[1].run()
109 97
110 await getVideo(servers[0].url, videoUUID3, HttpStatusCode.OK_200) 98 await servers[0].videos.get({ id: videoUUID3 })
111 }) 99 })
112 }) 100 })
113 101
@@ -116,19 +104,21 @@ describe('Test AP refresher', function () {
116 it('Should remove a deleted actor', async function () { 104 it('Should remove a deleted actor', async function () {
117 this.timeout(60000) 105 this.timeout(60000)
118 106
107 const command = servers[0].accounts
108
119 await wait(10000) 109 await wait(10000)
120 110
121 // Change actor name so the remote server returns a 404 111 // Change actor name so the remote server returns a 404
122 const to = 'http://localhost:' + servers[1].port + '/accounts/user2' 112 const to = 'http://localhost:' + servers[1].port + '/accounts/user2'
123 await setActorField(servers[1].internalServerNumber, to, 'preferredUsername', 'toto') 113 await servers[1].sql.setActorField(to, 'preferredUsername', 'toto')
124 114
125 await getAccount(servers[0].url, 'user1@localhost:' + servers[1].port) 115 await command.get({ accountName: 'user1@localhost:' + servers[1].port })
126 await getAccount(servers[0].url, 'user2@localhost:' + servers[1].port) 116 await command.get({ accountName: 'user2@localhost:' + servers[1].port })
127 117
128 await waitJobs(servers) 118 await waitJobs(servers)
129 119
130 await getAccount(servers[0].url, 'user1@localhost:' + servers[1].port, HttpStatusCode.OK_200) 120 await command.get({ accountName: 'user1@localhost:' + servers[1].port, expectedStatus: HttpStatusCode.OK_200 })
131 await getAccount(servers[0].url, 'user2@localhost:' + servers[1].port, HttpStatusCode.NOT_FOUND_404) 121 await command.get({ accountName: 'user2@localhost:' + servers[1].port, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
132 }) 122 })
133 }) 123 })
134 124
@@ -140,15 +130,15 @@ describe('Test AP refresher', function () {
140 await wait(10000) 130 await wait(10000)
141 131
142 // Change UUID so the remote server returns a 404 132 // Change UUID so the remote server returns a 404
143 await setPlaylistField(servers[1].internalServerNumber, playlistUUID2, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b178e') 133 await servers[1].sql.setPlaylistField(playlistUUID2, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b178e')
144 134
145 await getVideoPlaylist(servers[0].url, playlistUUID1) 135 await servers[0].playlists.get({ playlistId: playlistUUID1 })
146 await getVideoPlaylist(servers[0].url, playlistUUID2) 136 await servers[0].playlists.get({ playlistId: playlistUUID2 })
147 137
148 await waitJobs(servers) 138 await waitJobs(servers)
149 139
150 await getVideoPlaylist(servers[0].url, playlistUUID1, HttpStatusCode.OK_200) 140 await servers[0].playlists.get({ playlistId: playlistUUID1, expectedStatus: HttpStatusCode.OK_200 })
151 await getVideoPlaylist(servers[0].url, playlistUUID2, HttpStatusCode.NOT_FOUND_404) 141 await servers[0].playlists.get({ playlistId: playlistUUID2, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
152 }) 142 })
153 }) 143 })
154 144
@@ -156,7 +146,5 @@ describe('Test AP refresher', function () {
156 this.timeout(10000) 146 this.timeout(10000)
157 147
158 await cleanupTests(servers) 148 await cleanupTests(servers)
159
160 await closeAllSequelize(servers)
161 }) 149 })
162}) 150})
diff --git a/server/tests/api/activitypub/security.ts b/server/tests/api/activitypub/security.ts
index 61db272f6..94d946563 100644
--- a/server/tests/api/activitypub/security.ts
+++ b/server/tests/api/activitypub/security.ts
@@ -2,45 +2,35 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { activityPubContextify, buildSignedActivity } from '@server/helpers/activitypub'
5import { buildDigest } from '@server/helpers/peertube-crypto' 6import { buildDigest } from '@server/helpers/peertube-crypto'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 7import { HTTP_SIGNATURE } from '@server/initializers/constants'
7import { 8import { buildGlobalHeaders } from '@server/lib/job-queue/handlers/utils/activitypub-http-utils'
8 buildAbsoluteFixturePath, 9import { buildAbsoluteFixturePath, cleanupTests, createMultipleServers, killallServers, PeerTubeServer, wait } from '@shared/extra-utils'
9 cleanupTests, 10import { makeFollowRequest, makePOSTAPRequest } from '@shared/extra-utils/requests/activitypub'
10 closeAllSequelize, 11import { HttpStatusCode } from '@shared/models'
11 flushAndRunMultipleServers,
12 killallServers,
13 reRunServer,
14 ServerInfo,
15 setActorField,
16 wait
17} from '../../../../shared/extra-utils'
18import { makeFollowRequest, makePOSTAPRequest } from '../../../../shared/extra-utils/requests/activitypub'
19import { activityPubContextify, buildSignedActivity } from '../../../helpers/activitypub'
20import { HTTP_SIGNATURE } from '../../../initializers/constants'
21import { buildGlobalHeaders } from '../../../lib/job-queue/handlers/utils/activitypub-http-utils'
22 12
23const expect = chai.expect 13const expect = chai.expect
24 14
25function setKeysOfServer (onServer: ServerInfo, ofServer: ServerInfo, publicKey: string, privateKey: string) { 15function setKeysOfServer (onServer: PeerTubeServer, ofServer: PeerTubeServer, publicKey: string, privateKey: string) {
26 const url = 'http://localhost:' + ofServer.port + '/accounts/peertube' 16 const url = 'http://localhost:' + ofServer.port + '/accounts/peertube'
27 17
28 return Promise.all([ 18 return Promise.all([
29 setActorField(onServer.internalServerNumber, url, 'publicKey', publicKey), 19 onServer.sql.setActorField(url, 'publicKey', publicKey),
30 setActorField(onServer.internalServerNumber, url, 'privateKey', privateKey) 20 onServer.sql.setActorField(url, 'privateKey', privateKey)
31 ]) 21 ])
32} 22}
33 23
34function setUpdatedAtOfServer (onServer: ServerInfo, ofServer: ServerInfo, updatedAt: string) { 24function setUpdatedAtOfServer (onServer: PeerTubeServer, ofServer: PeerTubeServer, updatedAt: string) {
35 const url = 'http://localhost:' + ofServer.port + '/accounts/peertube' 25 const url = 'http://localhost:' + ofServer.port + '/accounts/peertube'
36 26
37 return Promise.all([ 27 return Promise.all([
38 setActorField(onServer.internalServerNumber, url, 'createdAt', updatedAt), 28 onServer.sql.setActorField(url, 'createdAt', updatedAt),
39 setActorField(onServer.internalServerNumber, url, 'updatedAt', updatedAt) 29 onServer.sql.setActorField(url, 'updatedAt', updatedAt)
40 ]) 30 ])
41} 31}
42 32
43function getAnnounceWithoutContext (server: ServerInfo) { 33function getAnnounceWithoutContext (server: PeerTubeServer) {
44 const json = require(buildAbsoluteFixturePath('./ap-json/peertube/announce-without-context.json')) 34 const json = require(buildAbsoluteFixturePath('./ap-json/peertube/announce-without-context.json'))
45 const result: typeof json = {} 35 const result: typeof json = {}
46 36
@@ -56,7 +46,7 @@ function getAnnounceWithoutContext (server: ServerInfo) {
56} 46}
57 47
58describe('Test ActivityPub security', function () { 48describe('Test ActivityPub security', function () {
59 let servers: ServerInfo[] 49 let servers: PeerTubeServer[]
60 let url: string 50 let url: string
61 51
62 const keys = require(buildAbsoluteFixturePath('./ap-json/peertube/keys.json')) 52 const keys = require(buildAbsoluteFixturePath('./ap-json/peertube/keys.json'))
@@ -74,7 +64,7 @@ describe('Test ActivityPub security', function () {
74 before(async function () { 64 before(async function () {
75 this.timeout(60000) 65 this.timeout(60000)
76 66
77 servers = await flushAndRunMultipleServers(3) 67 servers = await createMultipleServers(3)
78 68
79 url = servers[0].url + '/inbox' 69 url = servers[0].url + '/inbox'
80 70
@@ -173,8 +163,8 @@ describe('Test ActivityPub security', function () {
173 await setUpdatedAtOfServer(servers[0], servers[1], '2015-07-17 22:00:00+00') 163 await setUpdatedAtOfServer(servers[0], servers[1], '2015-07-17 22:00:00+00')
174 164
175 // Invalid peertube actor cache 165 // Invalid peertube actor cache
176 killallServers([ servers[1] ]) 166 await killallServers([ servers[1] ])
177 await reRunServer(servers[1]) 167 await servers[1].run()
178 168
179 const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) 169 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
180 const headers = buildGlobalHeaders(body) 170 const headers = buildGlobalHeaders(body)
@@ -294,7 +284,5 @@ describe('Test ActivityPub security', function () {
294 this.timeout(10000) 284 this.timeout(10000)
295 285
296 await cleanupTests(servers) 286 await cleanupTests(servers)
297
298 await closeAllSequelize(servers)
299 }) 287 })
300}) 288})
diff --git a/server/tests/api/check-params/abuses.ts b/server/tests/api/check-params/abuses.ts
index 2054776cc..fb9a5fd8b 100644
--- a/server/tests/api/check-params/abuses.ts
+++ b/server/tests/api/check-params/abuses.ts
@@ -1,66 +1,49 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { AbuseCreate, AbuseState } from '@shared/models'
5import { 4import {
6 addAbuseMessage, 5 AbusesCommand,
6 checkBadCountPagination,
7 checkBadSortPagination,
8 checkBadStartPagination,
7 cleanupTests, 9 cleanupTests,
8 createUser, 10 createSingleServer,
9 deleteAbuse,
10 deleteAbuseMessage,
11 doubleFollow, 11 doubleFollow,
12 flushAndRunServer,
13 generateUserAccessToken,
14 getAdminAbusesList,
15 getVideoIdFromUUID,
16 listAbuseMessages,
17 makeGetRequest, 12 makeGetRequest,
18 makePostBodyRequest, 13 makePostBodyRequest,
19 reportAbuse, 14 PeerTubeServer,
20 ServerInfo,
21 setAccessTokensToServers, 15 setAccessTokensToServers,
22 updateAbuse,
23 uploadVideo,
24 userLogin,
25 waitJobs 16 waitJobs
26} from '../../../../shared/extra-utils' 17} from '@shared/extra-utils'
27import { 18import { AbuseCreate, AbuseState, HttpStatusCode } from '@shared/models'
28 checkBadCountPagination,
29 checkBadSortPagination,
30 checkBadStartPagination
31} from '../../../../shared/extra-utils/requests/check-api-params'
32import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
33 19
34describe('Test abuses API validators', function () { 20describe('Test abuses API validators', function () {
35 const basePath = '/api/v1/abuses/' 21 const basePath = '/api/v1/abuses/'
36 22
37 let server: ServerInfo 23 let server: PeerTubeServer
38 24
39 let userAccessToken = '' 25 let userToken = ''
40 let userAccessToken2 = '' 26 let userToken2 = ''
41 let abuseId: number 27 let abuseId: number
42 let messageId: number 28 let messageId: number
43 29
30 let command: AbusesCommand
31
44 // --------------------------------------------------------------- 32 // ---------------------------------------------------------------
45 33
46 before(async function () { 34 before(async function () {
47 this.timeout(30000) 35 this.timeout(30000)
48 36
49 server = await flushAndRunServer(1) 37 server = await createSingleServer(1)
50 38
51 await setAccessTokensToServers([ server ]) 39 await setAccessTokensToServers([ server ])
52 40
53 const username = 'user1' 41 userToken = await server.users.generateUserAndToken('user_1')
54 const password = 'my super password' 42 userToken2 = await server.users.generateUserAndToken('user_2')
55 await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password })
56 userAccessToken = await userLogin(server, { username, password })
57 43
58 { 44 server.store.videoCreated = await server.videos.upload()
59 userAccessToken2 = await generateUserAccessToken(server, 'user_2')
60 }
61 45
62 const res = await uploadVideo(server.url, server.accessToken, {}) 46 command = server.abuses
63 server.video = res.body.video
64 }) 47 })
65 48
66 describe('When listing abuses for admins', function () { 49 describe('When listing abuses for admins', function () {
@@ -82,7 +65,7 @@ describe('Test abuses API validators', function () {
82 await makeGetRequest({ 65 await makeGetRequest({
83 url: server.url, 66 url: server.url,
84 path, 67 path,
85 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 68 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
86 }) 69 })
87 }) 70 })
88 71
@@ -90,8 +73,8 @@ describe('Test abuses API validators', function () {
90 await makeGetRequest({ 73 await makeGetRequest({
91 url: server.url, 74 url: server.url,
92 path, 75 path,
93 token: userAccessToken, 76 token: userToken,
94 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 77 expectedStatus: HttpStatusCode.FORBIDDEN_403
95 }) 78 })
96 }) 79 })
97 80
@@ -126,7 +109,7 @@ describe('Test abuses API validators', function () {
126 videoIs: 'deleted' 109 videoIs: 'deleted'
127 } 110 }
128 111
129 await makeGetRequest({ url: server.url, path, token: server.accessToken, query, statusCodeExpected: HttpStatusCode.OK_200 }) 112 await makeGetRequest({ url: server.url, path, token: server.accessToken, query, expectedStatus: HttpStatusCode.OK_200 })
130 }) 113 })
131 }) 114 })
132 115
@@ -134,32 +117,32 @@ describe('Test abuses API validators', function () {
134 const path = '/api/v1/users/me/abuses' 117 const path = '/api/v1/users/me/abuses'
135 118
136 it('Should fail with a bad start pagination', async function () { 119 it('Should fail with a bad start pagination', async function () {
137 await checkBadStartPagination(server.url, path, userAccessToken) 120 await checkBadStartPagination(server.url, path, userToken)
138 }) 121 })
139 122
140 it('Should fail with a bad count pagination', async function () { 123 it('Should fail with a bad count pagination', async function () {
141 await checkBadCountPagination(server.url, path, userAccessToken) 124 await checkBadCountPagination(server.url, path, userToken)
142 }) 125 })
143 126
144 it('Should fail with an incorrect sort', async function () { 127 it('Should fail with an incorrect sort', async function () {
145 await checkBadSortPagination(server.url, path, userAccessToken) 128 await checkBadSortPagination(server.url, path, userToken)
146 }) 129 })
147 130
148 it('Should fail with a non authenticated user', async function () { 131 it('Should fail with a non authenticated user', async function () {
149 await makeGetRequest({ 132 await makeGetRequest({
150 url: server.url, 133 url: server.url,
151 path, 134 path,
152 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 135 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
153 }) 136 })
154 }) 137 })
155 138
156 it('Should fail with a bad id filter', async function () { 139 it('Should fail with a bad id filter', async function () {
157 await makeGetRequest({ url: server.url, path, token: userAccessToken, query: { id: 'toto' } }) 140 await makeGetRequest({ url: server.url, path, token: userToken, query: { id: 'toto' } })
158 }) 141 })
159 142
160 it('Should fail with a bad state filter', async function () { 143 it('Should fail with a bad state filter', async function () {
161 await makeGetRequest({ url: server.url, path, token: userAccessToken, query: { state: 'toto' } }) 144 await makeGetRequest({ url: server.url, path, token: userToken, query: { state: 'toto' } })
162 await makeGetRequest({ url: server.url, path, token: userAccessToken, query: { state: 0 } }) 145 await makeGetRequest({ url: server.url, path, token: userToken, query: { state: 0 } })
163 }) 146 })
164 147
165 it('Should succeed with the correct params', async function () { 148 it('Should succeed with the correct params', async function () {
@@ -168,7 +151,7 @@ describe('Test abuses API validators', function () {
168 state: 2 151 state: 2
169 } 152 }
170 153
171 await makeGetRequest({ url: server.url, path, token: userAccessToken, query, statusCodeExpected: HttpStatusCode.OK_200 }) 154 await makeGetRequest({ url: server.url, path, token: userToken, query, expectedStatus: HttpStatusCode.OK_200 })
172 }) 155 })
173 }) 156 })
174 157
@@ -177,12 +160,12 @@ describe('Test abuses API validators', function () {
177 160
178 it('Should fail with nothing', async function () { 161 it('Should fail with nothing', async function () {
179 const fields = {} 162 const fields = {}
180 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 163 await makePostBodyRequest({ url: server.url, path, token: userToken, fields })
181 }) 164 })
182 165
183 it('Should fail with a wrong video', async function () { 166 it('Should fail with a wrong video', async function () {
184 const fields = { video: { id: 'blabla' }, reason: 'my super reason' } 167 const fields = { video: { id: 'blabla' }, reason: 'my super reason' }
185 await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields }) 168 await makePostBodyRequest({ url: server.url, path: path, token: userToken, fields })
186 }) 169 })
187 170
188 it('Should fail with an unknown video', async function () { 171 it('Should fail with an unknown video', async function () {
@@ -190,15 +173,15 @@ describe('Test abuses API validators', function () {
190 await makePostBodyRequest({ 173 await makePostBodyRequest({
191 url: server.url, 174 url: server.url,
192 path, 175 path,
193 token: userAccessToken, 176 token: userToken,
194 fields, 177 fields,
195 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 178 expectedStatus: HttpStatusCode.NOT_FOUND_404
196 }) 179 })
197 }) 180 })
198 181
199 it('Should fail with a wrong comment', async function () { 182 it('Should fail with a wrong comment', async function () {
200 const fields = { comment: { id: 'blabla' }, reason: 'my super reason' } 183 const fields = { comment: { id: 'blabla' }, reason: 'my super reason' }
201 await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields }) 184 await makePostBodyRequest({ url: server.url, path: path, token: userToken, fields })
202 }) 185 })
203 186
204 it('Should fail with an unknown comment', async function () { 187 it('Should fail with an unknown comment', async function () {
@@ -206,15 +189,15 @@ describe('Test abuses API validators', function () {
206 await makePostBodyRequest({ 189 await makePostBodyRequest({
207 url: server.url, 190 url: server.url,
208 path, 191 path,
209 token: userAccessToken, 192 token: userToken,
210 fields, 193 fields,
211 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 194 expectedStatus: HttpStatusCode.NOT_FOUND_404
212 }) 195 })
213 }) 196 })
214 197
215 it('Should fail with a wrong account', async function () { 198 it('Should fail with a wrong account', async function () {
216 const fields = { account: { id: 'blabla' }, reason: 'my super reason' } 199 const fields = { account: { id: 'blabla' }, reason: 'my super reason' }
217 await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields }) 200 await makePostBodyRequest({ url: server.url, path: path, token: userToken, fields })
218 }) 201 })
219 202
220 it('Should fail with an unknown account', async function () { 203 it('Should fail with an unknown account', async function () {
@@ -222,9 +205,9 @@ describe('Test abuses API validators', function () {
222 await makePostBodyRequest({ 205 await makePostBodyRequest({
223 url: server.url, 206 url: server.url,
224 path, 207 path,
225 token: userAccessToken, 208 token: userToken,
226 fields, 209 fields,
227 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 210 expectedStatus: HttpStatusCode.NOT_FOUND_404
228 }) 211 })
229 }) 212 })
230 213
@@ -233,65 +216,65 @@ describe('Test abuses API validators', function () {
233 await makePostBodyRequest({ 216 await makePostBodyRequest({
234 url: server.url, 217 url: server.url,
235 path, 218 path,
236 token: userAccessToken, 219 token: userToken,
237 fields, 220 fields,
238 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 221 expectedStatus: HttpStatusCode.BAD_REQUEST_400
239 }) 222 })
240 }) 223 })
241 224
242 it('Should fail with a non authenticated user', async function () { 225 it('Should fail with a non authenticated user', async function () {
243 const fields = { video: { id: server.video.id }, reason: 'my super reason' } 226 const fields = { video: { id: server.store.videoCreated.id }, reason: 'my super reason' }
244 227
245 await makePostBodyRequest({ url: server.url, path, token: 'hello', fields, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 228 await makePostBodyRequest({ url: server.url, path, token: 'hello', fields, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
246 }) 229 })
247 230
248 it('Should fail with a reason too short', async function () { 231 it('Should fail with a reason too short', async function () {
249 const fields = { video: { id: server.video.id }, reason: 'h' } 232 const fields = { video: { id: server.store.videoCreated.id }, reason: 'h' }
250 233
251 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 234 await makePostBodyRequest({ url: server.url, path, token: userToken, fields })
252 }) 235 })
253 236
254 it('Should fail with a too big reason', async function () { 237 it('Should fail with a too big reason', async function () {
255 const fields = { video: { id: server.video.id }, reason: 'super'.repeat(605) } 238 const fields = { video: { id: server.store.videoCreated.id }, reason: 'super'.repeat(605) }
256 239
257 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 240 await makePostBodyRequest({ url: server.url, path, token: userToken, fields })
258 }) 241 })
259 242
260 it('Should succeed with the correct parameters (basic)', async function () { 243 it('Should succeed with the correct parameters (basic)', async function () {
261 const fields: AbuseCreate = { video: { id: server.video.shortUUID }, reason: 'my super reason' } 244 const fields: AbuseCreate = { video: { id: server.store.videoCreated.shortUUID }, reason: 'my super reason' }
262 245
263 const res = await makePostBodyRequest({ 246 const res = await makePostBodyRequest({
264 url: server.url, 247 url: server.url,
265 path, 248 path,
266 token: userAccessToken, 249 token: userToken,
267 fields, 250 fields,
268 statusCodeExpected: HttpStatusCode.OK_200 251 expectedStatus: HttpStatusCode.OK_200
269 }) 252 })
270 abuseId = res.body.abuse.id 253 abuseId = res.body.abuse.id
271 }) 254 })
272 255
273 it('Should fail with a wrong predefined reason', async function () { 256 it('Should fail with a wrong predefined reason', async function () {
274 const fields = { video: { id: server.video.id }, reason: 'my super reason', predefinedReasons: [ 'wrongPredefinedReason' ] } 257 const fields = { video: server.store.videoCreated, reason: 'my super reason', predefinedReasons: [ 'wrongPredefinedReason' ] }
275 258
276 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 259 await makePostBodyRequest({ url: server.url, path, token: userToken, fields })
277 }) 260 })
278 261
279 it('Should fail with negative timestamps', async function () { 262 it('Should fail with negative timestamps', async function () {
280 const fields = { video: { id: server.video.id, startAt: -1 }, reason: 'my super reason' } 263 const fields = { video: { id: server.store.videoCreated.id, startAt: -1 }, reason: 'my super reason' }
281 264
282 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 265 await makePostBodyRequest({ url: server.url, path, token: userToken, fields })
283 }) 266 })
284 267
285 it('Should fail mith misordered startAt/endAt', async function () { 268 it('Should fail mith misordered startAt/endAt', async function () {
286 const fields = { video: { id: server.video.id, startAt: 5, endAt: 1 }, reason: 'my super reason' } 269 const fields = { video: { id: server.store.videoCreated.id, startAt: 5, endAt: 1 }, reason: 'my super reason' }
287 270
288 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 271 await makePostBodyRequest({ url: server.url, path, token: userToken, fields })
289 }) 272 })
290 273
291 it('Should succeed with the corret parameters (advanced)', async function () { 274 it('Should succeed with the corret parameters (advanced)', async function () {
292 const fields: AbuseCreate = { 275 const fields: AbuseCreate = {
293 video: { 276 video: {
294 id: server.video.id, 277 id: server.store.videoCreated.id,
295 startAt: 1, 278 startAt: 1,
296 endAt: 5 279 endAt: 5
297 }, 280 },
@@ -299,37 +282,37 @@ describe('Test abuses API validators', function () {
299 predefinedReasons: [ 'serverRules' ] 282 predefinedReasons: [ 'serverRules' ]
300 } 283 }
301 284
302 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields, statusCodeExpected: HttpStatusCode.OK_200 }) 285 await makePostBodyRequest({ url: server.url, path, token: userToken, fields, expectedStatus: HttpStatusCode.OK_200 })
303 }) 286 })
304 }) 287 })
305 288
306 describe('When updating an abuse', function () { 289 describe('When updating an abuse', function () {
307 290
308 it('Should fail with a non authenticated user', async function () { 291 it('Should fail with a non authenticated user', async function () {
309 await updateAbuse(server.url, 'blabla', abuseId, {}, HttpStatusCode.UNAUTHORIZED_401) 292 await command.update({ token: 'blabla', abuseId, body: {}, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
310 }) 293 })
311 294
312 it('Should fail with a non admin user', async function () { 295 it('Should fail with a non admin user', async function () {
313 await updateAbuse(server.url, userAccessToken, abuseId, {}, HttpStatusCode.FORBIDDEN_403) 296 await command.update({ token: userToken, abuseId, body: {}, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
314 }) 297 })
315 298
316 it('Should fail with a bad abuse id', async function () { 299 it('Should fail with a bad abuse id', async function () {
317 await updateAbuse(server.url, server.accessToken, 45, {}, HttpStatusCode.NOT_FOUND_404) 300 await command.update({ abuseId: 45, body: {}, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
318 }) 301 })
319 302
320 it('Should fail with a bad state', async function () { 303 it('Should fail with a bad state', async function () {
321 const body = { state: 5 } 304 const body = { state: 5 }
322 await updateAbuse(server.url, server.accessToken, abuseId, body, HttpStatusCode.BAD_REQUEST_400) 305 await command.update({ abuseId, body, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
323 }) 306 })
324 307
325 it('Should fail with a bad moderation comment', async function () { 308 it('Should fail with a bad moderation comment', async function () {
326 const body = { moderationComment: 'b'.repeat(3001) } 309 const body = { moderationComment: 'b'.repeat(3001) }
327 await updateAbuse(server.url, server.accessToken, abuseId, body, HttpStatusCode.BAD_REQUEST_400) 310 await command.update({ abuseId, body, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
328 }) 311 })
329 312
330 it('Should succeed with the correct params', async function () { 313 it('Should succeed with the correct params', async function () {
331 const body = { state: AbuseState.ACCEPTED } 314 const body = { state: AbuseState.ACCEPTED }
332 await updateAbuse(server.url, server.accessToken, abuseId, body) 315 await command.update({ abuseId, body })
333 }) 316 })
334 }) 317 })
335 318
@@ -337,23 +320,23 @@ describe('Test abuses API validators', function () {
337 const message = 'my super message' 320 const message = 'my super message'
338 321
339 it('Should fail with an invalid abuse id', async function () { 322 it('Should fail with an invalid abuse id', async function () {
340 await addAbuseMessage(server.url, userAccessToken2, 888, message, HttpStatusCode.NOT_FOUND_404) 323 await command.addMessage({ token: userToken2, abuseId: 888, message, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
341 }) 324 })
342 325
343 it('Should fail with a non authenticated user', async function () { 326 it('Should fail with a non authenticated user', async function () {
344 await addAbuseMessage(server.url, 'fake_token', abuseId, message, HttpStatusCode.UNAUTHORIZED_401) 327 await command.addMessage({ token: 'fake_token', abuseId, message, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
345 }) 328 })
346 329
347 it('Should fail with an invalid logged in user', async function () { 330 it('Should fail with an invalid logged in user', async function () {
348 await addAbuseMessage(server.url, userAccessToken2, abuseId, message, HttpStatusCode.FORBIDDEN_403) 331 await command.addMessage({ token: userToken2, abuseId, message, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
349 }) 332 })
350 333
351 it('Should fail with an invalid message', async function () { 334 it('Should fail with an invalid message', async function () {
352 await addAbuseMessage(server.url, userAccessToken, abuseId, 'a'.repeat(5000), HttpStatusCode.BAD_REQUEST_400) 335 await command.addMessage({ token: userToken, abuseId, message: 'a'.repeat(5000), expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
353 }) 336 })
354 337
355 it('Should suceed with the correct params', async function () { 338 it('Should suceed with the correct params', async function () {
356 const res = await addAbuseMessage(server.url, userAccessToken, abuseId, message) 339 const res = await command.addMessage({ token: userToken, abuseId, message })
357 messageId = res.body.abuseMessage.id 340 messageId = res.body.abuseMessage.id
358 }) 341 })
359 }) 342 })
@@ -361,96 +344,90 @@ describe('Test abuses API validators', function () {
361 describe('When listing abuse messages', function () { 344 describe('When listing abuse messages', function () {
362 345
363 it('Should fail with an invalid abuse id', async function () { 346 it('Should fail with an invalid abuse id', async function () {
364 await listAbuseMessages(server.url, userAccessToken, 888, HttpStatusCode.NOT_FOUND_404) 347 await command.listMessages({ token: userToken, abuseId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
365 }) 348 })
366 349
367 it('Should fail with a non authenticated user', async function () { 350 it('Should fail with a non authenticated user', async function () {
368 await listAbuseMessages(server.url, 'fake_token', abuseId, HttpStatusCode.UNAUTHORIZED_401) 351 await command.listMessages({ token: 'fake_token', abuseId, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
369 }) 352 })
370 353
371 it('Should fail with an invalid logged in user', async function () { 354 it('Should fail with an invalid logged in user', async function () {
372 await listAbuseMessages(server.url, userAccessToken2, abuseId, HttpStatusCode.FORBIDDEN_403) 355 await command.listMessages({ token: userToken2, abuseId, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
373 }) 356 })
374 357
375 it('Should succeed with the correct params', async function () { 358 it('Should succeed with the correct params', async function () {
376 await listAbuseMessages(server.url, userAccessToken, abuseId) 359 await command.listMessages({ token: userToken, abuseId })
377 }) 360 })
378 }) 361 })
379 362
380 describe('When deleting an abuse message', function () { 363 describe('When deleting an abuse message', function () {
381
382 it('Should fail with an invalid abuse id', async function () { 364 it('Should fail with an invalid abuse id', async function () {
383 await deleteAbuseMessage(server.url, userAccessToken, 888, messageId, HttpStatusCode.NOT_FOUND_404) 365 await command.deleteMessage({ token: userToken, abuseId: 888, messageId, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
384 }) 366 })
385 367
386 it('Should fail with an invalid message id', async function () { 368 it('Should fail with an invalid message id', async function () {
387 await deleteAbuseMessage(server.url, userAccessToken, abuseId, 888, HttpStatusCode.NOT_FOUND_404) 369 await command.deleteMessage({ token: userToken, abuseId, messageId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
388 }) 370 })
389 371
390 it('Should fail with a non authenticated user', async function () { 372 it('Should fail with a non authenticated user', async function () {
391 await deleteAbuseMessage(server.url, 'fake_token', abuseId, messageId, HttpStatusCode.UNAUTHORIZED_401) 373 await command.deleteMessage({ token: 'fake_token', abuseId, messageId, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
392 }) 374 })
393 375
394 it('Should fail with an invalid logged in user', async function () { 376 it('Should fail with an invalid logged in user', async function () {
395 await deleteAbuseMessage(server.url, userAccessToken2, abuseId, messageId, HttpStatusCode.FORBIDDEN_403) 377 await command.deleteMessage({ token: userToken2, abuseId, messageId, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
396 }) 378 })
397 379
398 it('Should succeed with the correct params', async function () { 380 it('Should succeed with the correct params', async function () {
399 await deleteAbuseMessage(server.url, userAccessToken, abuseId, messageId) 381 await command.deleteMessage({ token: userToken, abuseId, messageId })
400 }) 382 })
401 }) 383 })
402 384
403 describe('When deleting a video abuse', function () { 385 describe('When deleting a video abuse', function () {
404 386
405 it('Should fail with a non authenticated user', async function () { 387 it('Should fail with a non authenticated user', async function () {
406 await deleteAbuse(server.url, 'blabla', abuseId, HttpStatusCode.UNAUTHORIZED_401) 388 await command.delete({ token: 'blabla', abuseId, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
407 }) 389 })
408 390
409 it('Should fail with a non admin user', async function () { 391 it('Should fail with a non admin user', async function () {
410 await deleteAbuse(server.url, userAccessToken, abuseId, HttpStatusCode.FORBIDDEN_403) 392 await command.delete({ token: userToken, abuseId, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
411 }) 393 })
412 394
413 it('Should fail with a bad abuse id', async function () { 395 it('Should fail with a bad abuse id', async function () {
414 await deleteAbuse(server.url, server.accessToken, 45, HttpStatusCode.NOT_FOUND_404) 396 await command.delete({ abuseId: 45, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
415 }) 397 })
416 398
417 it('Should succeed with the correct params', async function () { 399 it('Should succeed with the correct params', async function () {
418 await deleteAbuse(server.url, server.accessToken, abuseId) 400 await command.delete({ abuseId })
419 }) 401 })
420 }) 402 })
421 403
422 describe('When trying to manage messages of a remote abuse', function () { 404 describe('When trying to manage messages of a remote abuse', function () {
423 let remoteAbuseId: number 405 let remoteAbuseId: number
424 let anotherServer: ServerInfo 406 let anotherServer: PeerTubeServer
425 407
426 before(async function () { 408 before(async function () {
427 this.timeout(50000) 409 this.timeout(50000)
428 410
429 anotherServer = await flushAndRunServer(2) 411 anotherServer = await createSingleServer(2)
430 await setAccessTokensToServers([ anotherServer ]) 412 await setAccessTokensToServers([ anotherServer ])
431 413
432 await doubleFollow(anotherServer, server) 414 await doubleFollow(anotherServer, server)
433 415
434 const server2VideoId = await getVideoIdFromUUID(anotherServer.url, server.video.uuid) 416 const server2VideoId = await anotherServer.videos.getId({ uuid: server.store.videoCreated.uuid })
435 await reportAbuse({ 417 await anotherServer.abuses.report({ reason: 'remote server', videoId: server2VideoId })
436 url: anotherServer.url,
437 token: anotherServer.accessToken,
438 reason: 'remote server',
439 videoId: server2VideoId
440 })
441 418
442 await waitJobs([ server, anotherServer ]) 419 await waitJobs([ server, anotherServer ])
443 420
444 const res = await getAdminAbusesList({ url: server.url, token: server.accessToken, sort: '-createdAt' }) 421 const body = await command.getAdminList({ sort: '-createdAt' })
445 remoteAbuseId = res.body.data[0].id 422 remoteAbuseId = body.data[0].id
446 }) 423 })
447 424
448 it('Should fail when listing abuse messages of a remote abuse', async function () { 425 it('Should fail when listing abuse messages of a remote abuse', async function () {
449 await listAbuseMessages(server.url, server.accessToken, remoteAbuseId, HttpStatusCode.BAD_REQUEST_400) 426 await command.listMessages({ abuseId: remoteAbuseId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
450 }) 427 })
451 428
452 it('Should fail when creating abuse message of a remote abuse', async function () { 429 it('Should fail when creating abuse message of a remote abuse', async function () {
453 await addAbuseMessage(server.url, server.accessToken, remoteAbuseId, 'message', HttpStatusCode.BAD_REQUEST_400) 430 await command.addMessage({ abuseId: remoteAbuseId, message: 'message', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
454 }) 431 })
455 432
456 after(async function () { 433 after(async function () {
diff --git a/server/tests/api/check-params/accounts.ts b/server/tests/api/check-params/accounts.ts
index d1712cff6..141d869b7 100644
--- a/server/tests/api/check-params/accounts.ts
+++ b/server/tests/api/check-params/accounts.ts
@@ -1,26 +1,26 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4
5import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../../shared/extra-utils'
6import { 4import {
7 checkBadCountPagination, 5 checkBadCountPagination,
8 checkBadSortPagination, 6 checkBadSortPagination,
9 checkBadStartPagination 7 checkBadStartPagination,
10} from '../../../../shared/extra-utils/requests/check-api-params' 8 cleanupTests,
11import { getAccount } from '../../../../shared/extra-utils/users/accounts' 9 createSingleServer,
12import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 10 PeerTubeServer
11} from '@shared/extra-utils'
12import { HttpStatusCode } from '@shared/models'
13 13
14describe('Test accounts API validators', function () { 14describe('Test accounts API validators', function () {
15 const path = '/api/v1/accounts/' 15 const path = '/api/v1/accounts/'
16 let server: ServerInfo 16 let server: PeerTubeServer
17 17
18 // --------------------------------------------------------------- 18 // ---------------------------------------------------------------
19 19
20 before(async function () { 20 before(async function () {
21 this.timeout(30000) 21 this.timeout(30000)
22 22
23 server = await flushAndRunServer(1) 23 server = await createSingleServer(1)
24 }) 24 })
25 25
26 describe('When listing accounts', function () { 26 describe('When listing accounts', function () {
@@ -38,8 +38,9 @@ describe('Test accounts API validators', function () {
38 }) 38 })
39 39
40 describe('When getting an account', function () { 40 describe('When getting an account', function () {
41
41 it('Should return 404 with a non existing name', async function () { 42 it('Should return 404 with a non existing name', async function () {
42 await getAccount(server.url, 'arfaze', HttpStatusCode.NOT_FOUND_404) 43 await server.accounts.get({ accountName: 'arfaze', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
43 }) 44 })
44 }) 45 })
45 46
diff --git a/server/tests/api/check-params/blocklist.ts b/server/tests/api/check-params/blocklist.ts
index 5ed8810ce..7d5fae5cf 100644
--- a/server/tests/api/check-params/blocklist.ts
+++ b/server/tests/api/check-params/blocklist.ts
@@ -1,43 +1,38 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4
5import { 4import {
5 checkBadCountPagination,
6 checkBadSortPagination,
7 checkBadStartPagination,
6 cleanupTests, 8 cleanupTests,
7 createUser, 9 createMultipleServers,
8 doubleFollow, 10 doubleFollow,
9 flushAndRunMultipleServers,
10 makeDeleteRequest, 11 makeDeleteRequest,
11 makeGetRequest, 12 makeGetRequest,
12 makePostBodyRequest, 13 makePostBodyRequest,
13 ServerInfo, 14 PeerTubeServer,
14 setAccessTokensToServers, 15 setAccessTokensToServers
15 userLogin 16} from '@shared/extra-utils'
16} from '../../../../shared/extra-utils' 17import { HttpStatusCode } from '@shared/models'
17import {
18 checkBadCountPagination,
19 checkBadSortPagination,
20 checkBadStartPagination
21} from '../../../../shared/extra-utils/requests/check-api-params'
22import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
23 18
24describe('Test blocklist API validators', function () { 19describe('Test blocklist API validators', function () {
25 let servers: ServerInfo[] 20 let servers: PeerTubeServer[]
26 let server: ServerInfo 21 let server: PeerTubeServer
27 let userAccessToken: string 22 let userAccessToken: string
28 23
29 before(async function () { 24 before(async function () {
30 this.timeout(60000) 25 this.timeout(60000)
31 26
32 servers = await flushAndRunMultipleServers(2) 27 servers = await createMultipleServers(2)
33 await setAccessTokensToServers(servers) 28 await setAccessTokensToServers(servers)
34 29
35 server = servers[0] 30 server = servers[0]
36 31
37 const user = { username: 'user1', password: 'password' } 32 const user = { username: 'user1', password: 'password' }
38 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 33 await server.users.create({ username: user.username, password: user.password })
39 34
40 userAccessToken = await userLogin(server, user) 35 userAccessToken = await server.login.getAccessToken(user)
41 36
42 await doubleFollow(servers[0], servers[1]) 37 await doubleFollow(servers[0], servers[1])
43 }) 38 })
@@ -54,7 +49,7 @@ describe('Test blocklist API validators', function () {
54 await makeGetRequest({ 49 await makeGetRequest({
55 url: server.url, 50 url: server.url,
56 path, 51 path,
57 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 52 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
58 }) 53 })
59 }) 54 })
60 55
@@ -77,7 +72,7 @@ describe('Test blocklist API validators', function () {
77 url: server.url, 72 url: server.url,
78 path, 73 path,
79 fields: { accountName: 'user1' }, 74 fields: { accountName: 'user1' },
80 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 75 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
81 }) 76 })
82 }) 77 })
83 78
@@ -87,7 +82,7 @@ describe('Test blocklist API validators', function () {
87 token: server.accessToken, 82 token: server.accessToken,
88 path, 83 path,
89 fields: { accountName: 'user2' }, 84 fields: { accountName: 'user2' },
90 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 85 expectedStatus: HttpStatusCode.NOT_FOUND_404
91 }) 86 })
92 }) 87 })
93 88
@@ -97,7 +92,7 @@ describe('Test blocklist API validators', function () {
97 token: server.accessToken, 92 token: server.accessToken,
98 path, 93 path,
99 fields: { accountName: 'root' }, 94 fields: { accountName: 'root' },
100 statusCodeExpected: HttpStatusCode.CONFLICT_409 95 expectedStatus: HttpStatusCode.CONFLICT_409
101 }) 96 })
102 }) 97 })
103 98
@@ -107,7 +102,7 @@ describe('Test blocklist API validators', function () {
107 token: server.accessToken, 102 token: server.accessToken,
108 path, 103 path,
109 fields: { accountName: 'user1' }, 104 fields: { accountName: 'user1' },
110 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 105 expectedStatus: HttpStatusCode.NO_CONTENT_204
111 }) 106 })
112 }) 107 })
113 }) 108 })
@@ -117,7 +112,7 @@ describe('Test blocklist API validators', function () {
117 await makeDeleteRequest({ 112 await makeDeleteRequest({
118 url: server.url, 113 url: server.url,
119 path: path + '/user1', 114 path: path + '/user1',
120 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 115 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
121 }) 116 })
122 }) 117 })
123 118
@@ -126,7 +121,7 @@ describe('Test blocklist API validators', function () {
126 url: server.url, 121 url: server.url,
127 path: path + '/user2', 122 path: path + '/user2',
128 token: server.accessToken, 123 token: server.accessToken,
129 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 124 expectedStatus: HttpStatusCode.NOT_FOUND_404
130 }) 125 })
131 }) 126 })
132 127
@@ -135,7 +130,7 @@ describe('Test blocklist API validators', function () {
135 url: server.url, 130 url: server.url,
136 path: path + '/user1', 131 path: path + '/user1',
137 token: server.accessToken, 132 token: server.accessToken,
138 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 133 expectedStatus: HttpStatusCode.NO_CONTENT_204
139 }) 134 })
140 }) 135 })
141 }) 136 })
@@ -149,7 +144,7 @@ describe('Test blocklist API validators', function () {
149 await makeGetRequest({ 144 await makeGetRequest({
150 url: server.url, 145 url: server.url,
151 path, 146 path,
152 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 147 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
153 }) 148 })
154 }) 149 })
155 150
@@ -172,7 +167,7 @@ describe('Test blocklist API validators', function () {
172 url: server.url, 167 url: server.url,
173 path, 168 path,
174 fields: { host: 'localhost:9002' }, 169 fields: { host: 'localhost:9002' },
175 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 170 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
176 }) 171 })
177 }) 172 })
178 173
@@ -182,7 +177,7 @@ describe('Test blocklist API validators', function () {
182 token: server.accessToken, 177 token: server.accessToken,
183 path, 178 path,
184 fields: { host: 'localhost:9003' }, 179 fields: { host: 'localhost:9003' },
185 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 180 expectedStatus: HttpStatusCode.NO_CONTENT_204
186 }) 181 })
187 }) 182 })
188 183
@@ -192,7 +187,7 @@ describe('Test blocklist API validators', function () {
192 token: server.accessToken, 187 token: server.accessToken,
193 path, 188 path,
194 fields: { host: 'localhost:' + server.port }, 189 fields: { host: 'localhost:' + server.port },
195 statusCodeExpected: HttpStatusCode.CONFLICT_409 190 expectedStatus: HttpStatusCode.CONFLICT_409
196 }) 191 })
197 }) 192 })
198 193
@@ -202,7 +197,7 @@ describe('Test blocklist API validators', function () {
202 token: server.accessToken, 197 token: server.accessToken,
203 path, 198 path,
204 fields: { host: 'localhost:' + servers[1].port }, 199 fields: { host: 'localhost:' + servers[1].port },
205 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 200 expectedStatus: HttpStatusCode.NO_CONTENT_204
206 }) 201 })
207 }) 202 })
208 }) 203 })
@@ -212,7 +207,7 @@ describe('Test blocklist API validators', function () {
212 await makeDeleteRequest({ 207 await makeDeleteRequest({
213 url: server.url, 208 url: server.url,
214 path: path + '/localhost:' + servers[1].port, 209 path: path + '/localhost:' + servers[1].port,
215 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 210 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
216 }) 211 })
217 }) 212 })
218 213
@@ -221,7 +216,7 @@ describe('Test blocklist API validators', function () {
221 url: server.url, 216 url: server.url,
222 path: path + '/localhost:9004', 217 path: path + '/localhost:9004',
223 token: server.accessToken, 218 token: server.accessToken,
224 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 219 expectedStatus: HttpStatusCode.NOT_FOUND_404
225 }) 220 })
226 }) 221 })
227 222
@@ -230,7 +225,7 @@ describe('Test blocklist API validators', function () {
230 url: server.url, 225 url: server.url,
231 path: path + '/localhost:' + servers[1].port, 226 path: path + '/localhost:' + servers[1].port,
232 token: server.accessToken, 227 token: server.accessToken,
233 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 228 expectedStatus: HttpStatusCode.NO_CONTENT_204
234 }) 229 })
235 }) 230 })
236 }) 231 })
@@ -247,7 +242,7 @@ describe('Test blocklist API validators', function () {
247 await makeGetRequest({ 242 await makeGetRequest({
248 url: server.url, 243 url: server.url,
249 path, 244 path,
250 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 245 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
251 }) 246 })
252 }) 247 })
253 248
@@ -256,7 +251,7 @@ describe('Test blocklist API validators', function () {
256 url: server.url, 251 url: server.url,
257 token: userAccessToken, 252 token: userAccessToken,
258 path, 253 path,
259 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 254 expectedStatus: HttpStatusCode.FORBIDDEN_403
260 }) 255 })
261 }) 256 })
262 257
@@ -279,7 +274,7 @@ describe('Test blocklist API validators', function () {
279 url: server.url, 274 url: server.url,
280 path, 275 path,
281 fields: { accountName: 'user1' }, 276 fields: { accountName: 'user1' },
282 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 277 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
283 }) 278 })
284 }) 279 })
285 280
@@ -289,7 +284,7 @@ describe('Test blocklist API validators', function () {
289 token: userAccessToken, 284 token: userAccessToken,
290 path, 285 path,
291 fields: { accountName: 'user1' }, 286 fields: { accountName: 'user1' },
292 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 287 expectedStatus: HttpStatusCode.FORBIDDEN_403
293 }) 288 })
294 }) 289 })
295 290
@@ -299,7 +294,7 @@ describe('Test blocklist API validators', function () {
299 token: server.accessToken, 294 token: server.accessToken,
300 path, 295 path,
301 fields: { accountName: 'user2' }, 296 fields: { accountName: 'user2' },
302 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 297 expectedStatus: HttpStatusCode.NOT_FOUND_404
303 }) 298 })
304 }) 299 })
305 300
@@ -309,7 +304,7 @@ describe('Test blocklist API validators', function () {
309 token: server.accessToken, 304 token: server.accessToken,
310 path, 305 path,
311 fields: { accountName: 'root' }, 306 fields: { accountName: 'root' },
312 statusCodeExpected: HttpStatusCode.CONFLICT_409 307 expectedStatus: HttpStatusCode.CONFLICT_409
313 }) 308 })
314 }) 309 })
315 310
@@ -319,7 +314,7 @@ describe('Test blocklist API validators', function () {
319 token: server.accessToken, 314 token: server.accessToken,
320 path, 315 path,
321 fields: { accountName: 'user1' }, 316 fields: { accountName: 'user1' },
322 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 317 expectedStatus: HttpStatusCode.NO_CONTENT_204
323 }) 318 })
324 }) 319 })
325 }) 320 })
@@ -329,7 +324,7 @@ describe('Test blocklist API validators', function () {
329 await makeDeleteRequest({ 324 await makeDeleteRequest({
330 url: server.url, 325 url: server.url,
331 path: path + '/user1', 326 path: path + '/user1',
332 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 327 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
333 }) 328 })
334 }) 329 })
335 330
@@ -338,7 +333,7 @@ describe('Test blocklist API validators', function () {
338 url: server.url, 333 url: server.url,
339 path: path + '/user1', 334 path: path + '/user1',
340 token: userAccessToken, 335 token: userAccessToken,
341 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 336 expectedStatus: HttpStatusCode.FORBIDDEN_403
342 }) 337 })
343 }) 338 })
344 339
@@ -347,7 +342,7 @@ describe('Test blocklist API validators', function () {
347 url: server.url, 342 url: server.url,
348 path: path + '/user2', 343 path: path + '/user2',
349 token: server.accessToken, 344 token: server.accessToken,
350 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 345 expectedStatus: HttpStatusCode.NOT_FOUND_404
351 }) 346 })
352 }) 347 })
353 348
@@ -356,7 +351,7 @@ describe('Test blocklist API validators', function () {
356 url: server.url, 351 url: server.url,
357 path: path + '/user1', 352 path: path + '/user1',
358 token: server.accessToken, 353 token: server.accessToken,
359 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 354 expectedStatus: HttpStatusCode.NO_CONTENT_204
360 }) 355 })
361 }) 356 })
362 }) 357 })
@@ -370,7 +365,7 @@ describe('Test blocklist API validators', function () {
370 await makeGetRequest({ 365 await makeGetRequest({
371 url: server.url, 366 url: server.url,
372 path, 367 path,
373 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 368 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
374 }) 369 })
375 }) 370 })
376 371
@@ -379,7 +374,7 @@ describe('Test blocklist API validators', function () {
379 url: server.url, 374 url: server.url,
380 token: userAccessToken, 375 token: userAccessToken,
381 path, 376 path,
382 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 377 expectedStatus: HttpStatusCode.FORBIDDEN_403
383 }) 378 })
384 }) 379 })
385 380
@@ -402,7 +397,7 @@ describe('Test blocklist API validators', function () {
402 url: server.url, 397 url: server.url,
403 path, 398 path,
404 fields: { host: 'localhost:' + servers[1].port }, 399 fields: { host: 'localhost:' + servers[1].port },
405 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 400 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
406 }) 401 })
407 }) 402 })
408 403
@@ -412,7 +407,7 @@ describe('Test blocklist API validators', function () {
412 token: userAccessToken, 407 token: userAccessToken,
413 path, 408 path,
414 fields: { host: 'localhost:' + servers[1].port }, 409 fields: { host: 'localhost:' + servers[1].port },
415 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 410 expectedStatus: HttpStatusCode.FORBIDDEN_403
416 }) 411 })
417 }) 412 })
418 413
@@ -422,7 +417,7 @@ describe('Test blocklist API validators', function () {
422 token: server.accessToken, 417 token: server.accessToken,
423 path, 418 path,
424 fields: { host: 'localhost:9003' }, 419 fields: { host: 'localhost:9003' },
425 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 420 expectedStatus: HttpStatusCode.NO_CONTENT_204
426 }) 421 })
427 }) 422 })
428 423
@@ -432,7 +427,7 @@ describe('Test blocklist API validators', function () {
432 token: server.accessToken, 427 token: server.accessToken,
433 path, 428 path,
434 fields: { host: 'localhost:' + server.port }, 429 fields: { host: 'localhost:' + server.port },
435 statusCodeExpected: HttpStatusCode.CONFLICT_409 430 expectedStatus: HttpStatusCode.CONFLICT_409
436 }) 431 })
437 }) 432 })
438 433
@@ -442,7 +437,7 @@ describe('Test blocklist API validators', function () {
442 token: server.accessToken, 437 token: server.accessToken,
443 path, 438 path,
444 fields: { host: 'localhost:' + servers[1].port }, 439 fields: { host: 'localhost:' + servers[1].port },
445 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 440 expectedStatus: HttpStatusCode.NO_CONTENT_204
446 }) 441 })
447 }) 442 })
448 }) 443 })
@@ -452,7 +447,7 @@ describe('Test blocklist API validators', function () {
452 await makeDeleteRequest({ 447 await makeDeleteRequest({
453 url: server.url, 448 url: server.url,
454 path: path + '/localhost:' + servers[1].port, 449 path: path + '/localhost:' + servers[1].port,
455 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 450 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
456 }) 451 })
457 }) 452 })
458 453
@@ -461,7 +456,7 @@ describe('Test blocklist API validators', function () {
461 url: server.url, 456 url: server.url,
462 path: path + '/localhost:' + servers[1].port, 457 path: path + '/localhost:' + servers[1].port,
463 token: userAccessToken, 458 token: userAccessToken,
464 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 459 expectedStatus: HttpStatusCode.FORBIDDEN_403
465 }) 460 })
466 }) 461 })
467 462
@@ -470,7 +465,7 @@ describe('Test blocklist API validators', function () {
470 url: server.url, 465 url: server.url,
471 path: path + '/localhost:9004', 466 path: path + '/localhost:9004',
472 token: server.accessToken, 467 token: server.accessToken,
473 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 468 expectedStatus: HttpStatusCode.NOT_FOUND_404
474 }) 469 })
475 }) 470 })
476 471
@@ -479,7 +474,7 @@ describe('Test blocklist API validators', function () {
479 url: server.url, 474 url: server.url,
480 path: path + '/localhost:' + servers[1].port, 475 path: path + '/localhost:' + servers[1].port,
481 token: server.accessToken, 476 token: server.accessToken,
482 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 477 expectedStatus: HttpStatusCode.NO_CONTENT_204
483 }) 478 })
484 }) 479 })
485 }) 480 })
diff --git a/server/tests/api/check-params/bulk.ts b/server/tests/api/check-params/bulk.ts
index 07b920ba7..bc9d7784d 100644
--- a/server/tests/api/check-params/bulk.ts
+++ b/server/tests/api/check-params/bulk.ts
@@ -1,19 +1,11 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { 4import { cleanupTests, createSingleServer, makePostBodyRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
5 cleanupTests, 5import { HttpStatusCode } from '@shared/models'
6 createUser,
7 flushAndRunServer,
8 ServerInfo,
9 setAccessTokensToServers,
10 userLogin
11} from '../../../../shared/extra-utils'
12import { makePostBodyRequest } from '../../../../shared/extra-utils/requests/requests'
13import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
14 6
15describe('Test bulk API validators', function () { 7describe('Test bulk API validators', function () {
16 let server: ServerInfo 8 let server: PeerTubeServer
17 let userAccessToken: string 9 let userAccessToken: string
18 10
19 // --------------------------------------------------------------- 11 // ---------------------------------------------------------------
@@ -21,13 +13,13 @@ describe('Test bulk API validators', function () {
21 before(async function () { 13 before(async function () {
22 this.timeout(120000) 14 this.timeout(120000)
23 15
24 server = await flushAndRunServer(1) 16 server = await createSingleServer(1)
25 await setAccessTokensToServers([ server ]) 17 await setAccessTokensToServers([ server ])
26 18
27 const user = { username: 'user1', password: 'password' } 19 const user = { username: 'user1', password: 'password' }
28 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 20 await server.users.create({ username: user.username, password: user.password })
29 21
30 userAccessToken = await userLogin(server, user) 22 userAccessToken = await server.login.getAccessToken(user)
31 }) 23 })
32 24
33 describe('When removing comments of', function () { 25 describe('When removing comments of', function () {
@@ -38,7 +30,7 @@ describe('Test bulk API validators', function () {
38 url: server.url, 30 url: server.url,
39 path, 31 path,
40 fields: { accountName: 'user1', scope: 'my-videos' }, 32 fields: { accountName: 'user1', scope: 'my-videos' },
41 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 33 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
42 }) 34 })
43 }) 35 })
44 36
@@ -48,7 +40,7 @@ describe('Test bulk API validators', function () {
48 token: server.accessToken, 40 token: server.accessToken,
49 path, 41 path,
50 fields: { accountName: 'user2', scope: 'my-videos' }, 42 fields: { accountName: 'user2', scope: 'my-videos' },
51 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 43 expectedStatus: HttpStatusCode.NOT_FOUND_404
52 }) 44 })
53 }) 45 })
54 46
@@ -58,7 +50,7 @@ describe('Test bulk API validators', function () {
58 token: server.accessToken, 50 token: server.accessToken,
59 path, 51 path,
60 fields: { accountName: 'user1', scope: 'my-videoss' }, 52 fields: { accountName: 'user1', scope: 'my-videoss' },
61 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 53 expectedStatus: HttpStatusCode.BAD_REQUEST_400
62 }) 54 })
63 }) 55 })
64 56
@@ -68,7 +60,7 @@ describe('Test bulk API validators', function () {
68 token: userAccessToken, 60 token: userAccessToken,
69 path, 61 path,
70 fields: { accountName: 'user1', scope: 'instance' }, 62 fields: { accountName: 'user1', scope: 'instance' },
71 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 63 expectedStatus: HttpStatusCode.FORBIDDEN_403
72 }) 64 })
73 }) 65 })
74 66
@@ -78,7 +70,7 @@ describe('Test bulk API validators', function () {
78 token: server.accessToken, 70 token: server.accessToken,
79 path, 71 path,
80 fields: { accountName: 'user1', scope: 'instance' }, 72 fields: { accountName: 'user1', scope: 'instance' },
81 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 73 expectedStatus: HttpStatusCode.NO_CONTENT_204
82 }) 74 })
83 }) 75 })
84 }) 76 })
diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts
index 9549070ef..87cb2287e 100644
--- a/server/tests/api/check-params/config.ts
+++ b/server/tests/api/check-params/config.ts
@@ -1,26 +1,21 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import { omit } from 'lodash'
4import 'mocha' 3import 'mocha'
5import { CustomConfig } from '../../../../shared/models/server/custom-config.model' 4import { omit } from 'lodash'
6
7import { 5import {
8 cleanupTests, 6 cleanupTests,
9 createUser, 7 createSingleServer,
10 flushAndRunServer,
11 immutableAssign,
12 makeDeleteRequest, 8 makeDeleteRequest,
13 makeGetRequest, 9 makeGetRequest,
14 makePutBodyRequest, 10 makePutBodyRequest,
15 ServerInfo, 11 PeerTubeServer,
16 setAccessTokensToServers, 12 setAccessTokensToServers
17 userLogin 13} from '@shared/extra-utils'
18} from '../../../../shared/extra-utils' 14import { CustomConfig, HttpStatusCode } from '@shared/models'
19import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
20 15
21describe('Test config API validators', function () { 16describe('Test config API validators', function () {
22 const path = '/api/v1/config/custom' 17 const path = '/api/v1/config/custom'
23 let server: ServerInfo 18 let server: PeerTubeServer
24 let userAccessToken: string 19 let userAccessToken: string
25 const updateParams: CustomConfig = { 20 const updateParams: CustomConfig = {
26 instance: { 21 instance: {
@@ -201,7 +196,7 @@ describe('Test config API validators', function () {
201 before(async function () { 196 before(async function () {
202 this.timeout(30000) 197 this.timeout(30000)
203 198
204 server = await flushAndRunServer(1) 199 server = await createSingleServer(1)
205 200
206 await setAccessTokensToServers([ server ]) 201 await setAccessTokensToServers([ server ])
207 202
@@ -209,8 +204,8 @@ describe('Test config API validators', function () {
209 username: 'user1', 204 username: 'user1',
210 password: 'password' 205 password: 'password'
211 } 206 }
212 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 207 await server.users.create({ username: user.username, password: user.password })
213 userAccessToken = await userLogin(server, user) 208 userAccessToken = await server.login.getAccessToken(user)
214 }) 209 })
215 210
216 describe('When getting the configuration', function () { 211 describe('When getting the configuration', function () {
@@ -218,7 +213,7 @@ describe('Test config API validators', function () {
218 await makeGetRequest({ 213 await makeGetRequest({
219 url: server.url, 214 url: server.url,
220 path, 215 path,
221 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 216 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
222 }) 217 })
223 }) 218 })
224 219
@@ -227,7 +222,7 @@ describe('Test config API validators', function () {
227 url: server.url, 222 url: server.url,
228 path, 223 path,
229 token: userAccessToken, 224 token: userAccessToken,
230 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 225 expectedStatus: HttpStatusCode.FORBIDDEN_403
231 }) 226 })
232 }) 227 })
233 }) 228 })
@@ -238,7 +233,7 @@ describe('Test config API validators', function () {
238 url: server.url, 233 url: server.url,
239 path, 234 path,
240 fields: updateParams, 235 fields: updateParams,
241 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 236 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
242 }) 237 })
243 }) 238 })
244 239
@@ -248,7 +243,7 @@ describe('Test config API validators', function () {
248 path, 243 path,
249 fields: updateParams, 244 fields: updateParams,
250 token: userAccessToken, 245 token: userAccessToken,
251 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 246 expectedStatus: HttpStatusCode.FORBIDDEN_403
252 }) 247 })
253 }) 248 })
254 249
@@ -260,47 +255,53 @@ describe('Test config API validators', function () {
260 path, 255 path,
261 fields: newUpdateParams, 256 fields: newUpdateParams,
262 token: server.accessToken, 257 token: server.accessToken,
263 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 258 expectedStatus: HttpStatusCode.BAD_REQUEST_400
264 }) 259 })
265 }) 260 })
266 261
267 it('Should fail with a bad default NSFW policy', async function () { 262 it('Should fail with a bad default NSFW policy', async function () {
268 const newUpdateParams = immutableAssign(updateParams, { 263 const newUpdateParams = {
264 ...updateParams,
265
269 instance: { 266 instance: {
270 defaultNSFWPolicy: 'hello' 267 defaultNSFWPolicy: 'hello'
271 } 268 }
272 }) 269 }
273 270
274 await makePutBodyRequest({ 271 await makePutBodyRequest({
275 url: server.url, 272 url: server.url,
276 path, 273 path,
277 fields: newUpdateParams, 274 fields: newUpdateParams,
278 token: server.accessToken, 275 token: server.accessToken,
279 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 276 expectedStatus: HttpStatusCode.BAD_REQUEST_400
280 }) 277 })
281 }) 278 })
282 279
283 it('Should fail if email disabled and signup requires email verification', async function () { 280 it('Should fail if email disabled and signup requires email verification', async function () {
284 // opposite scenario - success when enable enabled - covered via tests/api/users/user-verification.ts 281 // opposite scenario - success when enable enabled - covered via tests/api/users/user-verification.ts
285 const newUpdateParams = immutableAssign(updateParams, { 282 const newUpdateParams = {
283 ...updateParams,
284
286 signup: { 285 signup: {
287 enabled: true, 286 enabled: true,
288 limit: 5, 287 limit: 5,
289 requiresEmailVerification: true 288 requiresEmailVerification: true
290 } 289 }
291 }) 290 }
292 291
293 await makePutBodyRequest({ 292 await makePutBodyRequest({
294 url: server.url, 293 url: server.url,
295 path, 294 path,
296 fields: newUpdateParams, 295 fields: newUpdateParams,
297 token: server.accessToken, 296 token: server.accessToken,
298 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 297 expectedStatus: HttpStatusCode.BAD_REQUEST_400
299 }) 298 })
300 }) 299 })
301 300
302 it('Should fail with a disabled webtorrent & hls transcoding', async function () { 301 it('Should fail with a disabled webtorrent & hls transcoding', async function () {
303 const newUpdateParams = immutableAssign(updateParams, { 302 const newUpdateParams = {
303 ...updateParams,
304
304 transcoding: { 305 transcoding: {
305 hls: { 306 hls: {
306 enabled: false 307 enabled: false
@@ -309,14 +310,14 @@ describe('Test config API validators', function () {
309 enabled: false 310 enabled: false
310 } 311 }
311 } 312 }
312 }) 313 }
313 314
314 await makePutBodyRequest({ 315 await makePutBodyRequest({
315 url: server.url, 316 url: server.url,
316 path, 317 path,
317 fields: newUpdateParams, 318 fields: newUpdateParams,
318 token: server.accessToken, 319 token: server.accessToken,
319 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 320 expectedStatus: HttpStatusCode.BAD_REQUEST_400
320 }) 321 })
321 }) 322 })
322 323
@@ -326,7 +327,7 @@ describe('Test config API validators', function () {
326 path, 327 path,
327 fields: updateParams, 328 fields: updateParams,
328 token: server.accessToken, 329 token: server.accessToken,
329 statusCodeExpected: HttpStatusCode.OK_200 330 expectedStatus: HttpStatusCode.OK_200
330 }) 331 })
331 }) 332 })
332 }) 333 })
@@ -336,7 +337,7 @@ describe('Test config API validators', function () {
336 await makeDeleteRequest({ 337 await makeDeleteRequest({
337 url: server.url, 338 url: server.url,
338 path, 339 path,
339 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 340 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
340 }) 341 })
341 }) 342 })
342 343
@@ -345,7 +346,7 @@ describe('Test config API validators', function () {
345 url: server.url, 346 url: server.url,
346 path, 347 path,
347 token: userAccessToken, 348 token: userAccessToken,
348 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 349 expectedStatus: HttpStatusCode.FORBIDDEN_403
349 }) 350 })
350 }) 351 })
351 }) 352 })
diff --git a/server/tests/api/check-params/contact-form.ts b/server/tests/api/check-params/contact-form.ts
index c7f9c1b47..1df9993da 100644
--- a/server/tests/api/check-params/contact-form.ts
+++ b/server/tests/api/check-params/contact-form.ts
@@ -1,14 +1,12 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4 4import { cleanupTests, createSingleServer, killallServers, MockSmtpServer, PeerTubeServer } from '@shared/extra-utils'
5import { cleanupTests, flushAndRunServer, immutableAssign, killallServers, reRunServer, ServerInfo } from '../../../../shared/extra-utils' 5import { ContactFormCommand } from '@shared/extra-utils/server'
6import { sendContactForm } from '../../../../shared/extra-utils/server/contact-form' 6import { HttpStatusCode } from '@shared/models'
7import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
8import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
9 7
10describe('Test contact form API validators', function () { 8describe('Test contact form API validators', function () {
11 let server: ServerInfo 9 let server: PeerTubeServer
12 const emails: object[] = [] 10 const emails: object[] = []
13 const defaultBody = { 11 const defaultBody = {
14 fromName: 'super name', 12 fromName: 'super name',
@@ -17,6 +15,7 @@ describe('Test contact form API validators', function () {
17 body: 'Hello, how are you?' 15 body: 'Hello, how are you?'
18 } 16 }
19 let emailPort: number 17 let emailPort: number
18 let command: ContactFormCommand
20 19
21 // --------------------------------------------------------------- 20 // ---------------------------------------------------------------
22 21
@@ -26,86 +25,51 @@ describe('Test contact form API validators', function () {
26 emailPort = await MockSmtpServer.Instance.collectEmails(emails) 25 emailPort = await MockSmtpServer.Instance.collectEmails(emails)
27 26
28 // Email is disabled 27 // Email is disabled
29 server = await flushAndRunServer(1) 28 server = await createSingleServer(1)
29 command = server.contactForm
30 }) 30 })
31 31
32 it('Should not accept a contact form if emails are disabled', async function () { 32 it('Should not accept a contact form if emails are disabled', async function () {
33 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: HttpStatusCode.CONFLICT_409 })) 33 await command.send({ ...defaultBody, expectedStatus: HttpStatusCode.CONFLICT_409 })
34 }) 34 })
35 35
36 it('Should not accept a contact form if it is disabled in the configuration', async function () { 36 it('Should not accept a contact form if it is disabled in the configuration', async function () {
37 this.timeout(10000) 37 this.timeout(10000)
38 38
39 killallServers([ server ]) 39 await killallServers([ server ])
40 40
41 // Contact form is disabled 41 // Contact form is disabled
42 await reRunServer(server, { smtp: { hostname: 'localhost', port: emailPort }, contact_form: { enabled: false } }) 42 await server.run({ smtp: { hostname: 'localhost', port: emailPort }, contact_form: { enabled: false } })
43 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: HttpStatusCode.CONFLICT_409 })) 43 await command.send({ ...defaultBody, expectedStatus: HttpStatusCode.CONFLICT_409 })
44 }) 44 })
45 45
46 it('Should not accept a contact form if from email is invalid', async function () { 46 it('Should not accept a contact form if from email is invalid', async function () {
47 this.timeout(10000) 47 this.timeout(10000)
48 48
49 killallServers([ server ]) 49 await killallServers([ server ])
50 50
51 // Email & contact form enabled 51 // Email & contact form enabled
52 await reRunServer(server, { smtp: { hostname: 'localhost', port: emailPort } }) 52 await server.run({ smtp: { hostname: 'localhost', port: emailPort } })
53 53
54 await sendContactForm(immutableAssign(defaultBody, { 54 await command.send({ ...defaultBody, fromEmail: 'badEmail', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
55 url: server.url, 55 await command.send({ ...defaultBody, fromEmail: 'badEmail@', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
56 expectedStatus: HttpStatusCode.BAD_REQUEST_400, 56 await command.send({ ...defaultBody, fromEmail: undefined, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
57 fromEmail: 'badEmail'
58 }))
59 await sendContactForm(immutableAssign(defaultBody, {
60 url: server.url,
61 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
62 fromEmail: 'badEmail@'
63 }))
64 await sendContactForm(immutableAssign(defaultBody, {
65 url: server.url,
66 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
67 fromEmail: undefined
68 }))
69 }) 57 })
70 58
71 it('Should not accept a contact form if from name is invalid', async function () { 59 it('Should not accept a contact form if from name is invalid', async function () {
72 await sendContactForm(immutableAssign(defaultBody, { 60 await command.send({ ...defaultBody, fromName: 'name'.repeat(100), expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
73 url: server.url, 61 await command.send({ ...defaultBody, fromName: '', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
74 expectedStatus: HttpStatusCode.BAD_REQUEST_400, 62 await command.send({ ...defaultBody, fromName: undefined, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
75 fromName: 'name'.repeat(100)
76 }))
77 await sendContactForm(immutableAssign(defaultBody, {
78 url: server.url,
79 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
80 fromName: ''
81 }))
82 await sendContactForm(immutableAssign(defaultBody, {
83 url: server.url,
84 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
85 fromName: undefined
86 }))
87 }) 63 })
88 64
89 it('Should not accept a contact form if body is invalid', async function () { 65 it('Should not accept a contact form if body is invalid', async function () {
90 await sendContactForm(immutableAssign(defaultBody, { 66 await command.send({ ...defaultBody, body: 'body'.repeat(5000), expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
91 url: server.url, 67 await command.send({ ...defaultBody, body: 'a', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
92 expectedStatus: HttpStatusCode.BAD_REQUEST_400, 68 await command.send({ ...defaultBody, body: undefined, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
93 body: 'body'.repeat(5000)
94 }))
95 await sendContactForm(immutableAssign(defaultBody, {
96 url: server.url,
97 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
98 body: 'a'
99 }))
100 await sendContactForm(immutableAssign(defaultBody, {
101 url: server.url,
102 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
103 body: undefined
104 }))
105 }) 69 })
106 70
107 it('Should accept a contact form with the correct parameters', async function () { 71 it('Should accept a contact form with the correct parameters', async function () {
108 await sendContactForm(immutableAssign(defaultBody, { url: server.url })) 72 await command.send(defaultBody)
109 }) 73 })
110 74
111 after(async function () { 75 after(async function () {
diff --git a/server/tests/api/check-params/custom-pages.ts b/server/tests/api/check-params/custom-pages.ts
index 74ca3384c..9fbbea315 100644
--- a/server/tests/api/check-params/custom-pages.ts
+++ b/server/tests/api/check-params/custom-pages.ts
@@ -1,21 +1,20 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
5import { 4import {
6 cleanupTests, 5 cleanupTests,
7 createUser, 6 createSingleServer,
8 flushAndRunServer, 7 makeGetRequest,
9 ServerInfo, 8 makePutBodyRequest,
10 setAccessTokensToServers, 9 PeerTubeServer,
11 userLogin 10 setAccessTokensToServers
12} from '../../../../shared/extra-utils' 11} from '@shared/extra-utils'
13import { makeGetRequest, makePutBodyRequest } from '../../../../shared/extra-utils/requests/requests' 12import { HttpStatusCode } from '@shared/models'
14 13
15describe('Test custom pages validators', function () { 14describe('Test custom pages validators', function () {
16 const path = '/api/v1/custom-pages/homepage/instance' 15 const path = '/api/v1/custom-pages/homepage/instance'
17 16
18 let server: ServerInfo 17 let server: PeerTubeServer
19 let userAccessToken: string 18 let userAccessToken: string
20 19
21 // --------------------------------------------------------------- 20 // ---------------------------------------------------------------
@@ -23,13 +22,13 @@ describe('Test custom pages validators', function () {
23 before(async function () { 22 before(async function () {
24 this.timeout(120000) 23 this.timeout(120000)
25 24
26 server = await flushAndRunServer(1) 25 server = await createSingleServer(1)
27 await setAccessTokensToServers([ server ]) 26 await setAccessTokensToServers([ server ])
28 27
29 const user = { username: 'user1', password: 'password' } 28 const user = { username: 'user1', password: 'password' }
30 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 29 await server.users.create({ username: user.username, password: user.password })
31 30
32 userAccessToken = await userLogin(server, user) 31 userAccessToken = await server.login.getAccessToken(user)
33 }) 32 })
34 33
35 describe('When updating instance homepage', function () { 34 describe('When updating instance homepage', function () {
@@ -39,7 +38,7 @@ describe('Test custom pages validators', function () {
39 url: server.url, 38 url: server.url,
40 path, 39 path,
41 fields: { content: 'super content' }, 40 fields: { content: 'super content' },
42 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 41 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
43 }) 42 })
44 }) 43 })
45 44
@@ -49,7 +48,7 @@ describe('Test custom pages validators', function () {
49 path, 48 path,
50 token: userAccessToken, 49 token: userAccessToken,
51 fields: { content: 'super content' }, 50 fields: { content: 'super content' },
52 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 51 expectedStatus: HttpStatusCode.FORBIDDEN_403
53 }) 52 })
54 }) 53 })
55 54
@@ -59,7 +58,7 @@ describe('Test custom pages validators', function () {
59 path, 58 path,
60 token: server.accessToken, 59 token: server.accessToken,
61 fields: { content: 'super content' }, 60 fields: { content: 'super content' },
62 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 61 expectedStatus: HttpStatusCode.NO_CONTENT_204
63 }) 62 })
64 }) 63 })
65 }) 64 })
@@ -70,7 +69,7 @@ describe('Test custom pages validators', function () {
70 await makeGetRequest({ 69 await makeGetRequest({
71 url: server.url, 70 url: server.url,
72 path, 71 path,
73 statusCodeExpected: HttpStatusCode.OK_200 72 expectedStatus: HttpStatusCode.OK_200
74 }) 73 })
75 }) 74 })
76 }) 75 })
diff --git a/server/tests/api/check-params/debug.ts b/server/tests/api/check-params/debug.ts
index 37bf0f99b..a55786359 100644
--- a/server/tests/api/check-params/debug.ts
+++ b/server/tests/api/check-params/debug.ts
@@ -1,21 +1,12 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4 4import { cleanupTests, createSingleServer, makeGetRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
5import { 5import { HttpStatusCode } from '@shared/models'
6 cleanupTests,
7 createUser,
8 flushAndRunServer,
9 ServerInfo,
10 setAccessTokensToServers,
11 userLogin
12} from '../../../../shared/extra-utils'
13import { makeGetRequest } from '../../../../shared/extra-utils/requests/requests'
14import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
15 6
16describe('Test debug API validators', function () { 7describe('Test debug API validators', function () {
17 const path = '/api/v1/server/debug' 8 const path = '/api/v1/server/debug'
18 let server: ServerInfo 9 let server: PeerTubeServer
19 let userAccessToken = '' 10 let userAccessToken = ''
20 11
21 // --------------------------------------------------------------- 12 // ---------------------------------------------------------------
@@ -23,7 +14,7 @@ describe('Test debug API validators', function () {
23 before(async function () { 14 before(async function () {
24 this.timeout(120000) 15 this.timeout(120000)
25 16
26 server = await flushAndRunServer(1) 17 server = await createSingleServer(1)
27 18
28 await setAccessTokensToServers([ server ]) 19 await setAccessTokensToServers([ server ])
29 20
@@ -31,8 +22,8 @@ describe('Test debug API validators', function () {
31 username: 'user1', 22 username: 'user1',
32 password: 'my super password' 23 password: 'my super password'
33 } 24 }
34 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 25 await server.users.create({ username: user.username, password: user.password })
35 userAccessToken = await userLogin(server, user) 26 userAccessToken = await server.login.getAccessToken(user)
36 }) 27 })
37 28
38 describe('When getting debug endpoint', function () { 29 describe('When getting debug endpoint', function () {
@@ -41,7 +32,7 @@ describe('Test debug API validators', function () {
41 await makeGetRequest({ 32 await makeGetRequest({
42 url: server.url, 33 url: server.url,
43 path, 34 path,
44 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 35 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
45 }) 36 })
46 }) 37 })
47 38
@@ -50,7 +41,7 @@ describe('Test debug API validators', function () {
50 url: server.url, 41 url: server.url,
51 path, 42 path,
52 token: userAccessToken, 43 token: userAccessToken,
53 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 44 expectedStatus: HttpStatusCode.FORBIDDEN_403
54 }) 45 })
55 }) 46 })
56 47
@@ -60,7 +51,7 @@ describe('Test debug API validators', function () {
60 path, 51 path,
61 token: server.accessToken, 52 token: server.accessToken,
62 query: { startDate: new Date().toISOString() }, 53 query: { startDate: new Date().toISOString() },
63 statusCodeExpected: HttpStatusCode.OK_200 54 expectedStatus: HttpStatusCode.OK_200
64 }) 55 })
65 }) 56 })
66 }) 57 })
diff --git a/server/tests/api/check-params/follows.ts b/server/tests/api/check-params/follows.ts
index c03dd5c9c..2bc9f6b96 100644
--- a/server/tests/api/check-params/follows.ts
+++ b/server/tests/api/check-params/follows.ts
@@ -1,33 +1,29 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4
5import {
6 cleanupTests,
7 createUser,
8 flushAndRunServer,
9 makeDeleteRequest, makeGetRequest,
10 makePostBodyRequest,
11 ServerInfo,
12 setAccessTokensToServers,
13 userLogin
14} from '../../../../shared/extra-utils'
15import { 4import {
16 checkBadCountPagination, 5 checkBadCountPagination,
17 checkBadSortPagination, 6 checkBadSortPagination,
18 checkBadStartPagination 7 checkBadStartPagination,
19} from '../../../../shared/extra-utils/requests/check-api-params' 8 cleanupTests,
20import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 9 createSingleServer,
10 makeDeleteRequest,
11 makeGetRequest,
12 makePostBodyRequest,
13 PeerTubeServer,
14 setAccessTokensToServers
15} from '@shared/extra-utils'
16import { HttpStatusCode } from '@shared/models'
21 17
22describe('Test server follows API validators', function () { 18describe('Test server follows API validators', function () {
23 let server: ServerInfo 19 let server: PeerTubeServer
24 20
25 // --------------------------------------------------------------- 21 // ---------------------------------------------------------------
26 22
27 before(async function () { 23 before(async function () {
28 this.timeout(30000) 24 this.timeout(30000)
29 25
30 server = await flushAndRunServer(1) 26 server = await createSingleServer(1)
31 27
32 await setAccessTokensToServers([ server ]) 28 await setAccessTokensToServers([ server ])
33 }) 29 })
@@ -36,64 +32,68 @@ describe('Test server follows API validators', function () {
36 let userAccessToken = null 32 let userAccessToken = null
37 33
38 before(async function () { 34 before(async function () {
39 const user = { 35 userAccessToken = await server.users.generateUserAndToken('user1')
40 username: 'user1',
41 password: 'password'
42 }
43
44 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password })
45 userAccessToken = await userLogin(server, user)
46 }) 36 })
47 37
48 describe('When adding follows', function () { 38 describe('When adding follows', function () {
49 const path = '/api/v1/server/following' 39 const path = '/api/v1/server/following'
50 40
51 it('Should fail without hosts', async function () { 41 it('Should fail with nothing', async function () {
52 await makePostBodyRequest({ 42 await makePostBodyRequest({
53 url: server.url, 43 url: server.url,
54 path, 44 path,
55 token: server.accessToken, 45 token: server.accessToken,
56 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 46 expectedStatus: HttpStatusCode.BAD_REQUEST_400
57 }) 47 })
58 }) 48 })
59 49
60 it('Should fail if hosts is not an array', async function () { 50 it('Should fail if hosts is not composed by hosts', async function () {
61 await makePostBodyRequest({ 51 await makePostBodyRequest({
62 url: server.url, 52 url: server.url,
63 path, 53 path,
54 fields: { hosts: [ 'localhost:9002', 'localhost:coucou' ] },
64 token: server.accessToken, 55 token: server.accessToken,
65 fields: { hosts: 'localhost:9002' }, 56 expectedStatus: HttpStatusCode.BAD_REQUEST_400
66 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400
67 }) 57 })
68 }) 58 })
69 59
70 it('Should fail if the array is not composed by hosts', async function () { 60 it('Should fail if hosts is composed with http schemes', async function () {
71 await makePostBodyRequest({ 61 await makePostBodyRequest({
72 url: server.url, 62 url: server.url,
73 path, 63 path,
74 fields: { hosts: [ 'localhost:9002', 'localhost:coucou' ] }, 64 fields: { hosts: [ 'localhost:9002', 'http://localhost:9003' ] },
65 token: server.accessToken,
66 expectedStatus: HttpStatusCode.BAD_REQUEST_400
67 })
68 })
69
70 it('Should fail if hosts are not unique', async function () {
71 await makePostBodyRequest({
72 url: server.url,
73 path,
74 fields: { urls: [ 'localhost:9002', 'localhost:9002' ] },
75 token: server.accessToken, 75 token: server.accessToken,
76 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 76 expectedStatus: HttpStatusCode.BAD_REQUEST_400
77 }) 77 })
78 }) 78 })
79 79
80 it('Should fail if the array is composed with http schemes', async function () { 80 it('Should fail if handles is not composed by handles', async function () {
81 await makePostBodyRequest({ 81 await makePostBodyRequest({
82 url: server.url, 82 url: server.url,
83 path, 83 path,
84 fields: { hosts: [ 'localhost:9002', 'http://localhost:9003' ] }, 84 fields: { handles: [ 'hello@example.com', 'localhost:9001' ] },
85 token: server.accessToken, 85 token: server.accessToken,
86 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 86 expectedStatus: HttpStatusCode.BAD_REQUEST_400
87 }) 87 })
88 }) 88 })
89 89
90 it('Should fail if hosts are not unique', async function () { 90 it('Should fail if handles are not unique', async function () {
91 await makePostBodyRequest({ 91 await makePostBodyRequest({
92 url: server.url, 92 url: server.url,
93 path, 93 path,
94 fields: { urls: [ 'localhost:9002', 'localhost:9002' ] }, 94 fields: { urls: [ 'hello@example.com', 'hello@example.com' ] },
95 token: server.accessToken, 95 token: server.accessToken,
96 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 96 expectedStatus: HttpStatusCode.BAD_REQUEST_400
97 }) 97 })
98 }) 98 })
99 99
@@ -103,7 +103,7 @@ describe('Test server follows API validators', function () {
103 path, 103 path,
104 fields: { hosts: [ 'localhost:9002' ] }, 104 fields: { hosts: [ 'localhost:9002' ] },
105 token: 'fake_token', 105 token: 'fake_token',
106 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 106 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
107 }) 107 })
108 }) 108 })
109 109
@@ -113,7 +113,7 @@ describe('Test server follows API validators', function () {
113 path, 113 path,
114 fields: { hosts: [ 'localhost:9002' ] }, 114 fields: { hosts: [ 'localhost:9002' ] },
115 token: userAccessToken, 115 token: userAccessToken,
116 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 116 expectedStatus: HttpStatusCode.FORBIDDEN_403
117 }) 117 })
118 }) 118 })
119 }) 119 })
@@ -157,7 +157,7 @@ describe('Test server follows API validators', function () {
157 await makeGetRequest({ 157 await makeGetRequest({
158 url: server.url, 158 url: server.url,
159 path, 159 path,
160 statusCodeExpected: HttpStatusCode.OK_200, 160 expectedStatus: HttpStatusCode.OK_200,
161 query: { 161 query: {
162 state: 'accepted', 162 state: 'accepted',
163 actorType: 'Application' 163 actorType: 'Application'
@@ -206,7 +206,7 @@ describe('Test server follows API validators', function () {
206 await makeGetRequest({ 206 await makeGetRequest({
207 url: server.url, 207 url: server.url,
208 path, 208 path,
209 statusCodeExpected: HttpStatusCode.OK_200, 209 expectedStatus: HttpStatusCode.OK_200,
210 query: { 210 query: {
211 state: 'accepted' 211 state: 'accepted'
212 } 212 }
@@ -222,7 +222,7 @@ describe('Test server follows API validators', function () {
222 url: server.url, 222 url: server.url,
223 path: path + '/toto@localhost:9002', 223 path: path + '/toto@localhost:9002',
224 token: 'fake_token', 224 token: 'fake_token',
225 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 225 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
226 }) 226 })
227 }) 227 })
228 228
@@ -231,7 +231,7 @@ describe('Test server follows API validators', function () {
231 url: server.url, 231 url: server.url,
232 path: path + '/toto@localhost:9002', 232 path: path + '/toto@localhost:9002',
233 token: userAccessToken, 233 token: userAccessToken,
234 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 234 expectedStatus: HttpStatusCode.FORBIDDEN_403
235 }) 235 })
236 }) 236 })
237 237
@@ -240,7 +240,7 @@ describe('Test server follows API validators', function () {
240 url: server.url, 240 url: server.url,
241 path: path + '/toto', 241 path: path + '/toto',
242 token: server.accessToken, 242 token: server.accessToken,
243 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 243 expectedStatus: HttpStatusCode.BAD_REQUEST_400
244 }) 244 })
245 }) 245 })
246 246
@@ -249,7 +249,7 @@ describe('Test server follows API validators', function () {
249 url: server.url, 249 url: server.url,
250 path: path + '/toto@localhost:9003', 250 path: path + '/toto@localhost:9003',
251 token: server.accessToken, 251 token: server.accessToken,
252 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 252 expectedStatus: HttpStatusCode.NOT_FOUND_404
253 }) 253 })
254 }) 254 })
255 }) 255 })
@@ -262,7 +262,7 @@ describe('Test server follows API validators', function () {
262 url: server.url, 262 url: server.url,
263 path: path + '/toto@localhost:9002/accept', 263 path: path + '/toto@localhost:9002/accept',
264 token: 'fake_token', 264 token: 'fake_token',
265 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 265 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
266 }) 266 })
267 }) 267 })
268 268
@@ -271,7 +271,7 @@ describe('Test server follows API validators', function () {
271 url: server.url, 271 url: server.url,
272 path: path + '/toto@localhost:9002/accept', 272 path: path + '/toto@localhost:9002/accept',
273 token: userAccessToken, 273 token: userAccessToken,
274 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 274 expectedStatus: HttpStatusCode.FORBIDDEN_403
275 }) 275 })
276 }) 276 })
277 277
@@ -280,7 +280,7 @@ describe('Test server follows API validators', function () {
280 url: server.url, 280 url: server.url,
281 path: path + '/toto/accept', 281 path: path + '/toto/accept',
282 token: server.accessToken, 282 token: server.accessToken,
283 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 283 expectedStatus: HttpStatusCode.BAD_REQUEST_400
284 }) 284 })
285 }) 285 })
286 286
@@ -289,7 +289,7 @@ describe('Test server follows API validators', function () {
289 url: server.url, 289 url: server.url,
290 path: path + '/toto@localhost:9003/accept', 290 path: path + '/toto@localhost:9003/accept',
291 token: server.accessToken, 291 token: server.accessToken,
292 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 292 expectedStatus: HttpStatusCode.NOT_FOUND_404
293 }) 293 })
294 }) 294 })
295 }) 295 })
@@ -302,7 +302,7 @@ describe('Test server follows API validators', function () {
302 url: server.url, 302 url: server.url,
303 path: path + '/toto@localhost:9002/reject', 303 path: path + '/toto@localhost:9002/reject',
304 token: 'fake_token', 304 token: 'fake_token',
305 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 305 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
306 }) 306 })
307 }) 307 })
308 308
@@ -311,7 +311,7 @@ describe('Test server follows API validators', function () {
311 url: server.url, 311 url: server.url,
312 path: path + '/toto@localhost:9002/reject', 312 path: path + '/toto@localhost:9002/reject',
313 token: userAccessToken, 313 token: userAccessToken,
314 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 314 expectedStatus: HttpStatusCode.FORBIDDEN_403
315 }) 315 })
316 }) 316 })
317 317
@@ -320,7 +320,7 @@ describe('Test server follows API validators', function () {
320 url: server.url, 320 url: server.url,
321 path: path + '/toto/reject', 321 path: path + '/toto/reject',
322 token: server.accessToken, 322 token: server.accessToken,
323 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 323 expectedStatus: HttpStatusCode.BAD_REQUEST_400
324 }) 324 })
325 }) 325 })
326 326
@@ -329,7 +329,7 @@ describe('Test server follows API validators', function () {
329 url: server.url, 329 url: server.url,
330 path: path + '/toto@localhost:9003/reject', 330 path: path + '/toto@localhost:9003/reject',
331 token: server.accessToken, 331 token: server.accessToken,
332 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 332 expectedStatus: HttpStatusCode.NOT_FOUND_404
333 }) 333 })
334 }) 334 })
335 }) 335 })
@@ -342,7 +342,7 @@ describe('Test server follows API validators', function () {
342 url: server.url, 342 url: server.url,
343 path: path + '/localhost:9002', 343 path: path + '/localhost:9002',
344 token: 'fake_token', 344 token: 'fake_token',
345 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 345 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
346 }) 346 })
347 }) 347 })
348 348
@@ -351,7 +351,7 @@ describe('Test server follows API validators', function () {
351 url: server.url, 351 url: server.url,
352 path: path + '/localhost:9002', 352 path: path + '/localhost:9002',
353 token: userAccessToken, 353 token: userAccessToken,
354 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 354 expectedStatus: HttpStatusCode.FORBIDDEN_403
355 }) 355 })
356 }) 356 })
357 357
@@ -360,7 +360,7 @@ describe('Test server follows API validators', function () {
360 url: server.url, 360 url: server.url,
361 path: path + '/example.com', 361 path: path + '/example.com',
362 token: server.accessToken, 362 token: server.accessToken,
363 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 363 expectedStatus: HttpStatusCode.NOT_FOUND_404
364 }) 364 })
365 }) 365 })
366 }) 366 })
diff --git a/server/tests/api/check-params/jobs.ts b/server/tests/api/check-params/jobs.ts
index 3c1d2049b..23d95d8e4 100644
--- a/server/tests/api/check-params/jobs.ts
+++ b/server/tests/api/check-params/jobs.ts
@@ -1,26 +1,21 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4
5import {
6 cleanupTests,
7 createUser,
8 flushAndRunServer,
9 ServerInfo,
10 setAccessTokensToServers,
11 userLogin
12} from '../../../../shared/extra-utils'
13import { 4import {
14 checkBadCountPagination, 5 checkBadCountPagination,
15 checkBadSortPagination, 6 checkBadSortPagination,
16 checkBadStartPagination 7 checkBadStartPagination,
17} from '../../../../shared/extra-utils/requests/check-api-params' 8 cleanupTests,
18import { makeGetRequest } from '../../../../shared/extra-utils/requests/requests' 9 createSingleServer,
19import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 10 makeGetRequest,
11 PeerTubeServer,
12 setAccessTokensToServers
13} from '@shared/extra-utils'
14import { HttpStatusCode } from '@shared/models'
20 15
21describe('Test jobs API validators', function () { 16describe('Test jobs API validators', function () {
22 const path = '/api/v1/jobs/failed' 17 const path = '/api/v1/jobs/failed'
23 let server: ServerInfo 18 let server: PeerTubeServer
24 let userAccessToken = '' 19 let userAccessToken = ''
25 20
26 // --------------------------------------------------------------- 21 // ---------------------------------------------------------------
@@ -28,7 +23,7 @@ describe('Test jobs API validators', function () {
28 before(async function () { 23 before(async function () {
29 this.timeout(120000) 24 this.timeout(120000)
30 25
31 server = await flushAndRunServer(1) 26 server = await createSingleServer(1)
32 27
33 await setAccessTokensToServers([ server ]) 28 await setAccessTokensToServers([ server ])
34 29
@@ -36,8 +31,8 @@ describe('Test jobs API validators', function () {
36 username: 'user1', 31 username: 'user1',
37 password: 'my super password' 32 password: 'my super password'
38 } 33 }
39 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 34 await server.users.create({ username: user.username, password: user.password })
40 userAccessToken = await userLogin(server, user) 35 userAccessToken = await server.login.getAccessToken(user)
41 }) 36 })
42 37
43 describe('When listing jobs', function () { 38 describe('When listing jobs', function () {
@@ -77,7 +72,7 @@ describe('Test jobs API validators', function () {
77 await makeGetRequest({ 72 await makeGetRequest({
78 url: server.url, 73 url: server.url,
79 path, 74 path,
80 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 75 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
81 }) 76 })
82 }) 77 })
83 78
@@ -86,7 +81,7 @@ describe('Test jobs API validators', function () {
86 url: server.url, 81 url: server.url,
87 path, 82 path,
88 token: userAccessToken, 83 token: userAccessToken,
89 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 84 expectedStatus: HttpStatusCode.FORBIDDEN_403
90 }) 85 })
91 }) 86 })
92 87
diff --git a/server/tests/api/check-params/live.ts b/server/tests/api/check-params/live.ts
index 933d8abf2..700b4724d 100644
--- a/server/tests/api/check-params/live.ts
+++ b/server/tests/api/check-params/live.ts
@@ -2,69 +2,64 @@
2 2
3import 'mocha' 3import 'mocha'
4import { omit } from 'lodash' 4import { omit } from 'lodash'
5import { LiveVideo, VideoCreateResult, VideoPrivacy } from '@shared/models'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { 5import {
8 buildAbsoluteFixturePath, 6 buildAbsoluteFixturePath,
9 cleanupTests, 7 cleanupTests,
10 createUser, 8 createSingleServer,
11 flushAndRunServer, 9 LiveCommand,
12 getLive,
13 getMyUserInformation,
14 immutableAssign,
15 makePostBodyRequest, 10 makePostBodyRequest,
16 makeUploadRequest, 11 makeUploadRequest,
17 runAndTestFfmpegStreamError, 12 PeerTubeServer,
18 sendRTMPStream, 13 sendRTMPStream,
19 ServerInfo,
20 setAccessTokensToServers, 14 setAccessTokensToServers,
21 stopFfmpeg, 15 stopFfmpeg
22 updateCustomSubConfig, 16} from '@shared/extra-utils'
23 updateLive, 17import { HttpStatusCode, VideoCreateResult, VideoPrivacy } from '@shared/models'
24 uploadVideoAndGetId,
25 userLogin,
26 waitUntilLivePublished
27} from '../../../../shared/extra-utils'
28 18
29describe('Test video lives API validator', function () { 19describe('Test video lives API validator', function () {
30 const path = '/api/v1/videos/live' 20 const path = '/api/v1/videos/live'
31 let server: ServerInfo 21 let server: PeerTubeServer
32 let userAccessToken = '' 22 let userAccessToken = ''
33 let channelId: number 23 let channelId: number
34 let video: VideoCreateResult 24 let video: VideoCreateResult
35 let videoIdNotLive: number 25 let videoIdNotLive: number
26 let command: LiveCommand
36 27
37 // --------------------------------------------------------------- 28 // ---------------------------------------------------------------
38 29
39 before(async function () { 30 before(async function () {
40 this.timeout(30000) 31 this.timeout(30000)
41 32
42 server = await flushAndRunServer(1) 33 server = await createSingleServer(1)
43 34
44 await setAccessTokensToServers([ server ]) 35 await setAccessTokensToServers([ server ])
45 36
46 await updateCustomSubConfig(server.url, server.accessToken, { 37 await server.config.updateCustomSubConfig({
47 live: { 38 newConfig: {
48 enabled: true, 39 live: {
49 maxInstanceLives: 20, 40 enabled: true,
50 maxUserLives: 20, 41 maxInstanceLives: 20,
51 allowReplay: true 42 maxUserLives: 20,
43 allowReplay: true
44 }
52 } 45 }
53 }) 46 })
54 47
55 const username = 'user1' 48 const username = 'user1'
56 const password = 'my super password' 49 const password = 'my super password'
57 await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) 50 await server.users.create({ username: username, password: password })
58 userAccessToken = await userLogin(server, { username, password }) 51 userAccessToken = await server.login.getAccessToken({ username, password })
59 52
60 { 53 {
61 const res = await getMyUserInformation(server.url, server.accessToken) 54 const { videoChannels } = await server.users.getMyInfo()
62 channelId = res.body.videoChannels[0].id 55 channelId = videoChannels[0].id
63 } 56 }
64 57
65 { 58 {
66 videoIdNotLive = (await uploadVideoAndGetId({ server, videoName: 'not live' })).id 59 videoIdNotLive = (await server.videos.quickUpload({ name: 'not live' })).id
67 } 60 }
61
62 command = server.live
68 }) 63 })
69 64
70 describe('When creating a live', function () { 65 describe('When creating a live', function () {
@@ -96,37 +91,37 @@ describe('Test video lives API validator', function () {
96 }) 91 })
97 92
98 it('Should fail with a long name', async function () { 93 it('Should fail with a long name', async function () {
99 const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(65) }) 94 const fields = { ...baseCorrectParams, name: 'super'.repeat(65) }
100 95
101 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 96 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
102 }) 97 })
103 98
104 it('Should fail with a bad category', async function () { 99 it('Should fail with a bad category', async function () {
105 const fields = immutableAssign(baseCorrectParams, { category: 125 }) 100 const fields = { ...baseCorrectParams, category: 125 }
106 101
107 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 102 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
108 }) 103 })
109 104
110 it('Should fail with a bad licence', async function () { 105 it('Should fail with a bad licence', async function () {
111 const fields = immutableAssign(baseCorrectParams, { licence: 125 }) 106 const fields = { ...baseCorrectParams, licence: 125 }
112 107
113 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 108 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
114 }) 109 })
115 110
116 it('Should fail with a bad language', async function () { 111 it('Should fail with a bad language', async function () {
117 const fields = immutableAssign(baseCorrectParams, { language: 'a'.repeat(15) }) 112 const fields = { ...baseCorrectParams, language: 'a'.repeat(15) }
118 113
119 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 114 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
120 }) 115 })
121 116
122 it('Should fail with a long description', async function () { 117 it('Should fail with a long description', async function () {
123 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) }) 118 const fields = { ...baseCorrectParams, description: 'super'.repeat(2500) }
124 119
125 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 120 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
126 }) 121 })
127 122
128 it('Should fail with a long support text', async function () { 123 it('Should fail with a long support text', async function () {
129 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) }) 124 const fields = { ...baseCorrectParams, support: 'super'.repeat(201) }
130 125
131 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 126 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
132 }) 127 })
@@ -138,7 +133,7 @@ describe('Test video lives API validator', function () {
138 }) 133 })
139 134
140 it('Should fail with a bad channel', async function () { 135 it('Should fail with a bad channel', async function () {
141 const fields = immutableAssign(baseCorrectParams, { channelId: 545454 }) 136 const fields = { ...baseCorrectParams, channelId: 545454 }
142 137
143 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 138 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
144 }) 139 })
@@ -148,31 +143,31 @@ describe('Test video lives API validator', function () {
148 username: 'fake', 143 username: 'fake',
149 password: 'fake_password' 144 password: 'fake_password'
150 } 145 }
151 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 146 await server.users.create({ username: user.username, password: user.password })
152 147
153 const accessTokenUser = await userLogin(server, user) 148 const accessTokenUser = await server.login.getAccessToken(user)
154 const res = await getMyUserInformation(server.url, accessTokenUser) 149 const { videoChannels } = await server.users.getMyInfo({ token: accessTokenUser })
155 const customChannelId = res.body.videoChannels[0].id 150 const customChannelId = videoChannels[0].id
156 151
157 const fields = immutableAssign(baseCorrectParams, { channelId: customChannelId }) 152 const fields = { ...baseCorrectParams, channelId: customChannelId }
158 153
159 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 154 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields })
160 }) 155 })
161 156
162 it('Should fail with too many tags', async function () { 157 it('Should fail with too many tags', async function () {
163 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }) 158 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }
164 159
165 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 160 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
166 }) 161 })
167 162
168 it('Should fail with a tag length too low', async function () { 163 it('Should fail with a tag length too low', async function () {
169 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 't' ] }) 164 const fields = { ...baseCorrectParams, tags: [ 'tag1', 't' ] }
170 165
171 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 166 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
172 }) 167 })
173 168
174 it('Should fail with a tag length too big', async function () { 169 it('Should fail with a tag length too big', async function () {
175 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }) 170 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }
176 171
177 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 172 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
178 }) 173 })
@@ -214,7 +209,7 @@ describe('Test video lives API validator', function () {
214 }) 209 })
215 210
216 it('Should fail with save replay and permanent live set to true', async function () { 211 it('Should fail with save replay and permanent live set to true', async function () {
217 const fields = immutableAssign(baseCorrectParams, { saveReplay: true, permanentLive: true }) 212 const fields = { ...baseCorrectParams, saveReplay: true, permanentLive: true }
218 213
219 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 214 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
220 }) 215 })
@@ -227,16 +222,18 @@ describe('Test video lives API validator', function () {
227 path, 222 path,
228 token: server.accessToken, 223 token: server.accessToken,
229 fields: baseCorrectParams, 224 fields: baseCorrectParams,
230 statusCodeExpected: HttpStatusCode.OK_200 225 expectedStatus: HttpStatusCode.OK_200
231 }) 226 })
232 227
233 video = res.body.video 228 video = res.body.video
234 }) 229 })
235 230
236 it('Should forbid if live is disabled', async function () { 231 it('Should forbid if live is disabled', async function () {
237 await updateCustomSubConfig(server.url, server.accessToken, { 232 await server.config.updateCustomSubConfig({
238 live: { 233 newConfig: {
239 enabled: false 234 live: {
235 enabled: false
236 }
240 } 237 }
241 }) 238 })
242 239
@@ -245,17 +242,19 @@ describe('Test video lives API validator', function () {
245 path, 242 path,
246 token: server.accessToken, 243 token: server.accessToken,
247 fields: baseCorrectParams, 244 fields: baseCorrectParams,
248 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 245 expectedStatus: HttpStatusCode.FORBIDDEN_403
249 }) 246 })
250 }) 247 })
251 248
252 it('Should forbid to save replay if not enabled by the admin', async function () { 249 it('Should forbid to save replay if not enabled by the admin', async function () {
253 const fields = immutableAssign(baseCorrectParams, { saveReplay: true }) 250 const fields = { ...baseCorrectParams, saveReplay: true }
254 251
255 await updateCustomSubConfig(server.url, server.accessToken, { 252 await server.config.updateCustomSubConfig({
256 live: { 253 newConfig: {
257 enabled: true, 254 live: {
258 allowReplay: false 255 enabled: true,
256 allowReplay: false
257 }
259 } 258 }
260 }) 259 })
261 260
@@ -264,17 +263,19 @@ describe('Test video lives API validator', function () {
264 path, 263 path,
265 token: server.accessToken, 264 token: server.accessToken,
266 fields, 265 fields,
267 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 266 expectedStatus: HttpStatusCode.FORBIDDEN_403
268 }) 267 })
269 }) 268 })
270 269
271 it('Should allow to save replay if enabled by the admin', async function () { 270 it('Should allow to save replay if enabled by the admin', async function () {
272 const fields = immutableAssign(baseCorrectParams, { saveReplay: true }) 271 const fields = { ...baseCorrectParams, saveReplay: true }
273 272
274 await updateCustomSubConfig(server.url, server.accessToken, { 273 await server.config.updateCustomSubConfig({
275 live: { 274 newConfig: {
276 enabled: true, 275 live: {
277 allowReplay: true 276 enabled: true,
277 allowReplay: true
278 }
278 } 279 }
279 }) 280 })
280 281
@@ -283,15 +284,17 @@ describe('Test video lives API validator', function () {
283 path, 284 path,
284 token: server.accessToken, 285 token: server.accessToken,
285 fields, 286 fields,
286 statusCodeExpected: HttpStatusCode.OK_200 287 expectedStatus: HttpStatusCode.OK_200
287 }) 288 })
288 }) 289 })
289 290
290 it('Should not allow live if max instance lives is reached', async function () { 291 it('Should not allow live if max instance lives is reached', async function () {
291 await updateCustomSubConfig(server.url, server.accessToken, { 292 await server.config.updateCustomSubConfig({
292 live: { 293 newConfig: {
293 enabled: true, 294 live: {
294 maxInstanceLives: 1 295 enabled: true,
296 maxInstanceLives: 1
297 }
295 } 298 }
296 }) 299 })
297 300
@@ -300,16 +303,18 @@ describe('Test video lives API validator', function () {
300 path, 303 path,
301 token: server.accessToken, 304 token: server.accessToken,
302 fields: baseCorrectParams, 305 fields: baseCorrectParams,
303 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 306 expectedStatus: HttpStatusCode.FORBIDDEN_403
304 }) 307 })
305 }) 308 })
306 309
307 it('Should not allow live if max user lives is reached', async function () { 310 it('Should not allow live if max user lives is reached', async function () {
308 await updateCustomSubConfig(server.url, server.accessToken, { 311 await server.config.updateCustomSubConfig({
309 live: { 312 newConfig: {
310 enabled: true, 313 live: {
311 maxInstanceLives: 20, 314 enabled: true,
312 maxUserLives: 1 315 maxInstanceLives: 20,
316 maxUserLives: 1
317 }
313 } 318 }
314 }) 319 })
315 320
@@ -318,7 +323,7 @@ describe('Test video lives API validator', function () {
318 path, 323 path,
319 token: server.accessToken, 324 token: server.accessToken,
320 fields: baseCorrectParams, 325 fields: baseCorrectParams,
321 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 326 expectedStatus: HttpStatusCode.FORBIDDEN_403
322 }) 327 })
323 }) 328 })
324 }) 329 })
@@ -326,110 +331,112 @@ describe('Test video lives API validator', function () {
326 describe('When getting live information', function () { 331 describe('When getting live information', function () {
327 332
328 it('Should fail without access token', async function () { 333 it('Should fail without access token', async function () {
329 await getLive(server.url, '', video.id, HttpStatusCode.UNAUTHORIZED_401) 334 await command.get({ token: '', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
330 }) 335 })
331 336
332 it('Should fail with a bad access token', async function () { 337 it('Should fail with a bad access token', async function () {
333 await getLive(server.url, 'toto', video.id, HttpStatusCode.UNAUTHORIZED_401) 338 await command.get({ token: 'toto', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
334 }) 339 })
335 340
336 it('Should fail with access token of another user', async function () { 341 it('Should fail with access token of another user', async function () {
337 await getLive(server.url, userAccessToken, video.id, HttpStatusCode.FORBIDDEN_403) 342 await command.get({ token: userAccessToken, videoId: video.id, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
338 }) 343 })
339 344
340 it('Should fail with a bad video id', async function () { 345 it('Should fail with a bad video id', async function () {
341 await getLive(server.url, server.accessToken, 'toto', HttpStatusCode.BAD_REQUEST_400) 346 await command.get({ videoId: 'toto', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
342 }) 347 })
343 348
344 it('Should fail with an unknown video id', async function () { 349 it('Should fail with an unknown video id', async function () {
345 await getLive(server.url, server.accessToken, 454555, HttpStatusCode.NOT_FOUND_404) 350 await command.get({ videoId: 454555, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
346 }) 351 })
347 352
348 it('Should fail with a non live video', async function () { 353 it('Should fail with a non live video', async function () {
349 await getLive(server.url, server.accessToken, videoIdNotLive, HttpStatusCode.NOT_FOUND_404) 354 await command.get({ videoId: videoIdNotLive, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
350 }) 355 })
351 356
352 it('Should succeed with the correct params', async function () { 357 it('Should succeed with the correct params', async function () {
353 await getLive(server.url, server.accessToken, video.id) 358 await command.get({ videoId: video.id })
354 await getLive(server.url, server.accessToken, video.shortUUID) 359 await command.get({ videoId: video.uuid })
360 await command.get({ videoId: video.shortUUID })
355 }) 361 })
356 }) 362 })
357 363
358 describe('When updating live information', async function () { 364 describe('When updating live information', async function () {
359 365
360 it('Should fail without access token', async function () { 366 it('Should fail without access token', async function () {
361 await updateLive(server.url, '', video.id, {}, HttpStatusCode.UNAUTHORIZED_401) 367 await command.update({ token: '', videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
362 }) 368 })
363 369
364 it('Should fail with a bad access token', async function () { 370 it('Should fail with a bad access token', async function () {
365 await updateLive(server.url, 'toto', video.id, {}, HttpStatusCode.UNAUTHORIZED_401) 371 await command.update({ token: 'toto', videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
366 }) 372 })
367 373
368 it('Should fail with access token of another user', async function () { 374 it('Should fail with access token of another user', async function () {
369 await updateLive(server.url, userAccessToken, video.id, {}, HttpStatusCode.FORBIDDEN_403) 375 await command.update({ token: userAccessToken, videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
370 }) 376 })
371 377
372 it('Should fail with a bad video id', async function () { 378 it('Should fail with a bad video id', async function () {
373 await updateLive(server.url, server.accessToken, 'toto', {}, HttpStatusCode.BAD_REQUEST_400) 379 await command.update({ videoId: 'toto', fields: {}, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
374 }) 380 })
375 381
376 it('Should fail with an unknown video id', async function () { 382 it('Should fail with an unknown video id', async function () {
377 await updateLive(server.url, server.accessToken, 454555, {}, HttpStatusCode.NOT_FOUND_404) 383 await command.update({ videoId: 454555, fields: {}, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
378 }) 384 })
379 385
380 it('Should fail with a non live video', async function () { 386 it('Should fail with a non live video', async function () {
381 await updateLive(server.url, server.accessToken, videoIdNotLive, {}, HttpStatusCode.NOT_FOUND_404) 387 await command.update({ videoId: videoIdNotLive, fields: {}, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
382 }) 388 })
383 389
384 it('Should fail with save replay and permanent live set to true', async function () { 390 it('Should fail with save replay and permanent live set to true', async function () {
385 const fields = { saveReplay: true, permanentLive: true } 391 const fields = { saveReplay: true, permanentLive: true }
386 392
387 await updateLive(server.url, server.accessToken, video.id, fields, HttpStatusCode.BAD_REQUEST_400) 393 await command.update({ videoId: video.id, fields, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
388 }) 394 })
389 395
390 it('Should succeed with the correct params', async function () { 396 it('Should succeed with the correct params', async function () {
391 await updateLive(server.url, server.accessToken, video.id, { saveReplay: false }) 397 await command.update({ videoId: video.id, fields: { saveReplay: false } })
392 await updateLive(server.url, server.accessToken, video.shortUUID, { saveReplay: false }) 398 await command.update({ videoId: video.uuid, fields: { saveReplay: false } })
399 await command.update({ videoId: video.shortUUID, fields: { saveReplay: false } })
393 }) 400 })
394 401
395 it('Should fail to update replay status if replay is not allowed on the instance', async function () { 402 it('Should fail to update replay status if replay is not allowed on the instance', async function () {
396 await updateCustomSubConfig(server.url, server.accessToken, { 403 await server.config.updateCustomSubConfig({
397 live: { 404 newConfig: {
398 enabled: true, 405 live: {
399 allowReplay: false 406 enabled: true,
407 allowReplay: false
408 }
400 } 409 }
401 }) 410 })
402 411
403 await updateLive(server.url, server.accessToken, video.id, { saveReplay: true }, HttpStatusCode.FORBIDDEN_403) 412 await command.update({ videoId: video.id, fields: { saveReplay: true }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
404 }) 413 })
405 414
406 it('Should fail to update a live if it has already started', async function () { 415 it('Should fail to update a live if it has already started', async function () {
407 this.timeout(40000) 416 this.timeout(40000)
408 417
409 const resLive = await getLive(server.url, server.accessToken, video.id) 418 const live = await command.get({ videoId: video.id })
410 const live: LiveVideo = resLive.body
411 419
412 const command = sendRTMPStream(live.rtmpUrl, live.streamKey) 420 const ffmpegCommand = sendRTMPStream(live.rtmpUrl, live.streamKey)
413 421
414 await waitUntilLivePublished(server.url, server.accessToken, video.id) 422 await command.waitUntilPublished({ videoId: video.id })
415 await updateLive(server.url, server.accessToken, video.id, {}, HttpStatusCode.BAD_REQUEST_400) 423 await command.update({ videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
416 424
417 await stopFfmpeg(command) 425 await stopFfmpeg(ffmpegCommand)
418 }) 426 })
419 427
420 it('Should fail to stream twice in the save live', async function () { 428 it('Should fail to stream twice in the save live', async function () {
421 this.timeout(40000) 429 this.timeout(40000)
422 430
423 const resLive = await getLive(server.url, server.accessToken, video.id) 431 const live = await command.get({ videoId: video.id })
424 const live: LiveVideo = resLive.body
425 432
426 const command = sendRTMPStream(live.rtmpUrl, live.streamKey) 433 const ffmpegCommand = sendRTMPStream(live.rtmpUrl, live.streamKey)
427 434
428 await waitUntilLivePublished(server.url, server.accessToken, video.id) 435 await command.waitUntilPublished({ videoId: video.id })
429 436
430 await runAndTestFfmpegStreamError(server.url, server.accessToken, video.id, true) 437 await command.runAndTestStreamError({ videoId: video.id, shouldHaveError: true })
431 438
432 await stopFfmpeg(command) 439 await stopFfmpeg(ffmpegCommand)
433 }) 440 })
434 }) 441 })
435 442
diff --git a/server/tests/api/check-params/logs.ts b/server/tests/api/check-params/logs.ts
index dac1e6b98..05372257a 100644
--- a/server/tests/api/check-params/logs.ts
+++ b/server/tests/api/check-params/logs.ts
@@ -1,21 +1,12 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4 4import { cleanupTests, createSingleServer, makeGetRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
5import { 5import { HttpStatusCode } from '@shared/models'
6 cleanupTests,
7 createUser,
8 flushAndRunServer,
9 ServerInfo,
10 setAccessTokensToServers,
11 userLogin
12} from '../../../../shared/extra-utils'
13import { makeGetRequest } from '../../../../shared/extra-utils/requests/requests'
14import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
15 6
16describe('Test logs API validators', function () { 7describe('Test logs API validators', function () {
17 const path = '/api/v1/server/logs' 8 const path = '/api/v1/server/logs'
18 let server: ServerInfo 9 let server: PeerTubeServer
19 let userAccessToken = '' 10 let userAccessToken = ''
20 11
21 // --------------------------------------------------------------- 12 // ---------------------------------------------------------------
@@ -23,7 +14,7 @@ describe('Test logs API validators', function () {
23 before(async function () { 14 before(async function () {
24 this.timeout(120000) 15 this.timeout(120000)
25 16
26 server = await flushAndRunServer(1) 17 server = await createSingleServer(1)
27 18
28 await setAccessTokensToServers([ server ]) 19 await setAccessTokensToServers([ server ])
29 20
@@ -31,8 +22,8 @@ describe('Test logs API validators', function () {
31 username: 'user1', 22 username: 'user1',
32 password: 'my super password' 23 password: 'my super password'
33 } 24 }
34 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 25 await server.users.create({ username: user.username, password: user.password })
35 userAccessToken = await userLogin(server, user) 26 userAccessToken = await server.login.getAccessToken(user)
36 }) 27 })
37 28
38 describe('When getting logs', function () { 29 describe('When getting logs', function () {
@@ -41,7 +32,7 @@ describe('Test logs API validators', function () {
41 await makeGetRequest({ 32 await makeGetRequest({
42 url: server.url, 33 url: server.url,
43 path, 34 path,
44 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 35 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
45 }) 36 })
46 }) 37 })
47 38
@@ -50,7 +41,7 @@ describe('Test logs API validators', function () {
50 url: server.url, 41 url: server.url,
51 path, 42 path,
52 token: userAccessToken, 43 token: userAccessToken,
53 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 44 expectedStatus: HttpStatusCode.FORBIDDEN_403
54 }) 45 })
55 }) 46 })
56 47
@@ -59,7 +50,7 @@ describe('Test logs API validators', function () {
59 url: server.url, 50 url: server.url,
60 path, 51 path,
61 token: server.accessToken, 52 token: server.accessToken,
62 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 53 expectedStatus: HttpStatusCode.BAD_REQUEST_400
63 }) 54 })
64 }) 55 })
65 56
@@ -69,7 +60,7 @@ describe('Test logs API validators', function () {
69 path, 60 path,
70 token: server.accessToken, 61 token: server.accessToken,
71 query: { startDate: 'toto' }, 62 query: { startDate: 'toto' },
72 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 63 expectedStatus: HttpStatusCode.BAD_REQUEST_400
73 }) 64 })
74 }) 65 })
75 66
@@ -79,7 +70,7 @@ describe('Test logs API validators', function () {
79 path, 70 path,
80 token: server.accessToken, 71 token: server.accessToken,
81 query: { startDate: new Date().toISOString(), endDate: 'toto' }, 72 query: { startDate: new Date().toISOString(), endDate: 'toto' },
82 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 73 expectedStatus: HttpStatusCode.BAD_REQUEST_400
83 }) 74 })
84 }) 75 })
85 76
@@ -89,7 +80,7 @@ describe('Test logs API validators', function () {
89 path, 80 path,
90 token: server.accessToken, 81 token: server.accessToken,
91 query: { startDate: new Date().toISOString(), level: 'toto' }, 82 query: { startDate: new Date().toISOString(), level: 'toto' },
92 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 83 expectedStatus: HttpStatusCode.BAD_REQUEST_400
93 }) 84 })
94 }) 85 })
95 86
@@ -99,7 +90,7 @@ describe('Test logs API validators', function () {
99 path, 90 path,
100 token: server.accessToken, 91 token: server.accessToken,
101 query: { startDate: new Date().toISOString() }, 92 query: { startDate: new Date().toISOString() },
102 statusCodeExpected: HttpStatusCode.OK_200 93 expectedStatus: HttpStatusCode.OK_200
103 }) 94 })
104 }) 95 })
105 }) 96 })
diff --git a/server/tests/api/check-params/plugins.ts b/server/tests/api/check-params/plugins.ts
index a833fe6ff..33f84ecbc 100644
--- a/server/tests/api/check-params/plugins.ts
+++ b/server/tests/api/check-params/plugins.ts
@@ -1,27 +1,22 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { HttpStatusCode } from '@shared/core-utils'
5import { 4import {
6 checkBadCountPagination, 5 checkBadCountPagination,
7 checkBadSortPagination, 6 checkBadSortPagination,
8 checkBadStartPagination, 7 checkBadStartPagination,
9 cleanupTests, 8 cleanupTests,
10 createUser, 9 createSingleServer,
11 flushAndRunServer,
12 immutableAssign,
13 installPlugin,
14 makeGetRequest, 10 makeGetRequest,
15 makePostBodyRequest, 11 makePostBodyRequest,
16 makePutBodyRequest, 12 makePutBodyRequest,
17 ServerInfo, 13 PeerTubeServer,
18 setAccessTokensToServers, 14 setAccessTokensToServers
19 userLogin
20} from '@shared/extra-utils' 15} from '@shared/extra-utils'
21import { PeerTubePlugin, PluginType } from '@shared/models' 16import { HttpStatusCode, PeerTubePlugin, PluginType } from '@shared/models'
22 17
23describe('Test server plugins API validators', function () { 18describe('Test server plugins API validators', function () {
24 let server: ServerInfo 19 let server: PeerTubeServer
25 let userAccessToken = null 20 let userAccessToken = null
26 21
27 const npmPlugin = 'peertube-plugin-hello-world' 22 const npmPlugin = 'peertube-plugin-hello-world'
@@ -37,7 +32,7 @@ describe('Test server plugins API validators', function () {
37 before(async function () { 32 before(async function () {
38 this.timeout(30000) 33 this.timeout(30000)
39 34
40 server = await flushAndRunServer(1) 35 server = await createSingleServer(1)
41 36
42 await setAccessTokensToServers([ server ]) 37 await setAccessTokensToServers([ server ])
43 38
@@ -46,17 +41,17 @@ describe('Test server plugins API validators', function () {
46 password: 'password' 41 password: 'password'
47 } 42 }
48 43
49 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 44 await server.users.create({ username: user.username, password: user.password })
50 userAccessToken = await userLogin(server, user) 45 userAccessToken = await server.login.getAccessToken(user)
51 46
52 { 47 {
53 const res = await installPlugin({ url: server.url, accessToken: server.accessToken, npmName: npmPlugin }) 48 const res = await server.plugins.install({ npmName: npmPlugin })
54 const plugin = res.body as PeerTubePlugin 49 const plugin = res.body as PeerTubePlugin
55 npmVersion = plugin.version 50 npmVersion = plugin.version
56 } 51 }
57 52
58 { 53 {
59 const res = await installPlugin({ url: server.url, accessToken: server.accessToken, npmName: themePlugin }) 54 const res = await server.plugins.install({ npmName: themePlugin })
60 const plugin = res.body as PeerTubePlugin 55 const plugin = res.body as PeerTubePlugin
61 themeVersion = plugin.version 56 themeVersion = plugin.version
62 } 57 }
@@ -74,7 +69,7 @@ describe('Test server plugins API validators', function () {
74 ] 69 ]
75 70
76 for (const p of paths) { 71 for (const p of paths) {
77 await makeGetRequest({ url: server.url, path: p, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) 72 await makeGetRequest({ url: server.url, path: p, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
78 } 73 }
79 }) 74 })
80 75
@@ -82,7 +77,7 @@ describe('Test server plugins API validators', function () {
82 await makeGetRequest({ 77 await makeGetRequest({
83 url: server.url, 78 url: server.url,
84 path: '/themes/' + pluginName + '/' + npmVersion + '/static/images/chocobo.png', 79 path: '/themes/' + pluginName + '/' + npmVersion + '/static/images/chocobo.png',
85 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 80 expectedStatus: HttpStatusCode.NOT_FOUND_404
86 }) 81 })
87 }) 82 })
88 83
@@ -97,7 +92,7 @@ describe('Test server plugins API validators', function () {
97 ] 92 ]
98 93
99 for (const p of paths) { 94 for (const p of paths) {
100 await makeGetRequest({ url: server.url, path: p, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 95 await makeGetRequest({ url: server.url, path: p, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
101 } 96 }
102 }) 97 })
103 98
@@ -111,14 +106,14 @@ describe('Test server plugins API validators', function () {
111 ] 106 ]
112 107
113 for (const p of paths) { 108 for (const p of paths) {
114 await makeGetRequest({ url: server.url, path: p, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 109 await makeGetRequest({ url: server.url, path: p, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
115 } 110 }
116 }) 111 })
117 112
118 it('Should fail with an unknown auth name', async function () { 113 it('Should fail with an unknown auth name', async function () {
119 const path = '/plugins/' + pluginName + '/' + npmVersion + '/auth/bad-auth' 114 const path = '/plugins/' + pluginName + '/' + npmVersion + '/auth/bad-auth'
120 115
121 await makeGetRequest({ url: server.url, path, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) 116 await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
122 }) 117 })
123 118
124 it('Should fail with an unknown static file', async function () { 119 it('Should fail with an unknown static file', async function () {
@@ -130,7 +125,7 @@ describe('Test server plugins API validators', function () {
130 ] 125 ]
131 126
132 for (const p of paths) { 127 for (const p of paths) {
133 await makeGetRequest({ url: server.url, path: p, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) 128 await makeGetRequest({ url: server.url, path: p, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
134 } 129 }
135 }) 130 })
136 131
@@ -138,7 +133,7 @@ describe('Test server plugins API validators', function () {
138 await makeGetRequest({ 133 await makeGetRequest({
139 url: server.url, 134 url: server.url,
140 path: '/themes/' + themeName + '/' + themeVersion + '/css/assets/fake.css', 135 path: '/themes/' + themeName + '/' + themeVersion + '/css/assets/fake.css',
141 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 136 expectedStatus: HttpStatusCode.NOT_FOUND_404
142 }) 137 })
143 }) 138 })
144 139
@@ -152,11 +147,11 @@ describe('Test server plugins API validators', function () {
152 ] 147 ]
153 148
154 for (const p of paths) { 149 for (const p of paths) {
155 await makeGetRequest({ url: server.url, path: p, statusCodeExpected: HttpStatusCode.OK_200 }) 150 await makeGetRequest({ url: server.url, path: p, expectedStatus: HttpStatusCode.OK_200 })
156 } 151 }
157 152
158 const authPath = '/plugins/' + pluginName + '/' + npmVersion + '/auth/fake-auth' 153 const authPath = '/plugins/' + pluginName + '/' + npmVersion + '/auth/fake-auth'
159 await makeGetRequest({ url: server.url, path: authPath, statusCodeExpected: HttpStatusCode.FOUND_302 }) 154 await makeGetRequest({ url: server.url, path: authPath, expectedStatus: HttpStatusCode.FOUND_302 })
160 }) 155 })
161 }) 156 })
162 157
@@ -174,7 +169,7 @@ describe('Test server plugins API validators', function () {
174 path, 169 path,
175 token: 'fake_token', 170 token: 'fake_token',
176 query: baseQuery, 171 query: baseQuery,
177 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 172 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
178 }) 173 })
179 }) 174 })
180 175
@@ -184,7 +179,7 @@ describe('Test server plugins API validators', function () {
184 path, 179 path,
185 token: userAccessToken, 180 token: userAccessToken,
186 query: baseQuery, 181 query: baseQuery,
187 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 182 expectedStatus: HttpStatusCode.FORBIDDEN_403
188 }) 183 })
189 }) 184 })
190 185
@@ -201,7 +196,7 @@ describe('Test server plugins API validators', function () {
201 }) 196 })
202 197
203 it('Should fail with an invalid plugin type', async function () { 198 it('Should fail with an invalid plugin type', async function () {
204 const query = immutableAssign(baseQuery, { pluginType: 5 }) 199 const query = { ...baseQuery, pluginType: 5 }
205 200
206 await makeGetRequest({ 201 await makeGetRequest({
207 url: server.url, 202 url: server.url,
@@ -212,7 +207,7 @@ describe('Test server plugins API validators', function () {
212 }) 207 })
213 208
214 it('Should fail with an invalid current peertube engine', async function () { 209 it('Should fail with an invalid current peertube engine', async function () {
215 const query = immutableAssign(baseQuery, { currentPeerTubeEngine: '1.0' }) 210 const query = { ...baseQuery, currentPeerTubeEngine: '1.0' }
216 211
217 await makeGetRequest({ 212 await makeGetRequest({
218 url: server.url, 213 url: server.url,
@@ -228,7 +223,7 @@ describe('Test server plugins API validators', function () {
228 path, 223 path,
229 token: server.accessToken, 224 token: server.accessToken,
230 query: baseQuery, 225 query: baseQuery,
231 statusCodeExpected: HttpStatusCode.OK_200 226 expectedStatus: HttpStatusCode.OK_200
232 }) 227 })
233 }) 228 })
234 }) 229 })
@@ -245,7 +240,7 @@ describe('Test server plugins API validators', function () {
245 path, 240 path,
246 token: 'fake_token', 241 token: 'fake_token',
247 query: baseQuery, 242 query: baseQuery,
248 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 243 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
249 }) 244 })
250 }) 245 })
251 246
@@ -255,7 +250,7 @@ describe('Test server plugins API validators', function () {
255 path, 250 path,
256 token: userAccessToken, 251 token: userAccessToken,
257 query: baseQuery, 252 query: baseQuery,
258 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 253 expectedStatus: HttpStatusCode.FORBIDDEN_403
259 }) 254 })
260 }) 255 })
261 256
@@ -272,7 +267,7 @@ describe('Test server plugins API validators', function () {
272 }) 267 })
273 268
274 it('Should fail with an invalid plugin type', async function () { 269 it('Should fail with an invalid plugin type', async function () {
275 const query = immutableAssign(baseQuery, { pluginType: 5 }) 270 const query = { ...baseQuery, pluginType: 5 }
276 271
277 await makeGetRequest({ 272 await makeGetRequest({
278 url: server.url, 273 url: server.url,
@@ -288,7 +283,7 @@ describe('Test server plugins API validators', function () {
288 path, 283 path,
289 token: server.accessToken, 284 token: server.accessToken,
290 query: baseQuery, 285 query: baseQuery,
291 statusCodeExpected: HttpStatusCode.OK_200 286 expectedStatus: HttpStatusCode.OK_200
292 }) 287 })
293 }) 288 })
294 }) 289 })
@@ -302,7 +297,7 @@ describe('Test server plugins API validators', function () {
302 url: server.url, 297 url: server.url,
303 path: path + suffix, 298 path: path + suffix,
304 token: 'fake_token', 299 token: 'fake_token',
305 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 300 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
306 }) 301 })
307 } 302 }
308 }) 303 })
@@ -313,7 +308,7 @@ describe('Test server plugins API validators', function () {
313 url: server.url, 308 url: server.url,
314 path: path + suffix, 309 path: path + suffix,
315 token: userAccessToken, 310 token: userAccessToken,
316 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 311 expectedStatus: HttpStatusCode.FORBIDDEN_403
317 }) 312 })
318 } 313 }
319 }) 314 })
@@ -324,7 +319,7 @@ describe('Test server plugins API validators', function () {
324 url: server.url, 319 url: server.url,
325 path: path + suffix, 320 path: path + suffix,
326 token: server.accessToken, 321 token: server.accessToken,
327 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 322 expectedStatus: HttpStatusCode.BAD_REQUEST_400
328 }) 323 })
329 } 324 }
330 325
@@ -333,7 +328,7 @@ describe('Test server plugins API validators', function () {
333 url: server.url, 328 url: server.url,
334 path: path + suffix, 329 path: path + suffix,
335 token: server.accessToken, 330 token: server.accessToken,
336 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 331 expectedStatus: HttpStatusCode.BAD_REQUEST_400
337 }) 332 })
338 } 333 }
339 }) 334 })
@@ -344,7 +339,7 @@ describe('Test server plugins API validators', function () {
344 url: server.url, 339 url: server.url,
345 path: path + suffix, 340 path: path + suffix,
346 token: server.accessToken, 341 token: server.accessToken,
347 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 342 expectedStatus: HttpStatusCode.NOT_FOUND_404
348 }) 343 })
349 } 344 }
350 }) 345 })
@@ -355,7 +350,7 @@ describe('Test server plugins API validators', function () {
355 url: server.url, 350 url: server.url,
356 path: path + suffix, 351 path: path + suffix,
357 token: server.accessToken, 352 token: server.accessToken,
358 statusCodeExpected: HttpStatusCode.OK_200 353 expectedStatus: HttpStatusCode.OK_200
359 }) 354 })
360 } 355 }
361 }) 356 })
@@ -371,7 +366,7 @@ describe('Test server plugins API validators', function () {
371 path: path + npmPlugin + '/settings', 366 path: path + npmPlugin + '/settings',
372 fields: { settings }, 367 fields: { settings },
373 token: 'fake_token', 368 token: 'fake_token',
374 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 369 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
375 }) 370 })
376 }) 371 })
377 372
@@ -381,7 +376,7 @@ describe('Test server plugins API validators', function () {
381 path: path + npmPlugin + '/settings', 376 path: path + npmPlugin + '/settings',
382 fields: { settings }, 377 fields: { settings },
383 token: userAccessToken, 378 token: userAccessToken,
384 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 379 expectedStatus: HttpStatusCode.FORBIDDEN_403
385 }) 380 })
386 }) 381 })
387 382
@@ -391,7 +386,7 @@ describe('Test server plugins API validators', function () {
391 path: path + 'toto/settings', 386 path: path + 'toto/settings',
392 fields: { settings }, 387 fields: { settings },
393 token: server.accessToken, 388 token: server.accessToken,
394 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 389 expectedStatus: HttpStatusCode.BAD_REQUEST_400
395 }) 390 })
396 391
397 await makePutBodyRequest({ 392 await makePutBodyRequest({
@@ -399,7 +394,7 @@ describe('Test server plugins API validators', function () {
399 path: path + 'peertube-plugin-TOTO/settings', 394 path: path + 'peertube-plugin-TOTO/settings',
400 fields: { settings }, 395 fields: { settings },
401 token: server.accessToken, 396 token: server.accessToken,
402 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 397 expectedStatus: HttpStatusCode.BAD_REQUEST_400
403 }) 398 })
404 }) 399 })
405 400
@@ -409,7 +404,7 @@ describe('Test server plugins API validators', function () {
409 path: path + 'peertube-plugin-toto/settings', 404 path: path + 'peertube-plugin-toto/settings',
410 fields: { settings }, 405 fields: { settings },
411 token: server.accessToken, 406 token: server.accessToken,
412 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 407 expectedStatus: HttpStatusCode.NOT_FOUND_404
413 }) 408 })
414 }) 409 })
415 410
@@ -419,7 +414,7 @@ describe('Test server plugins API validators', function () {
419 path: path + npmPlugin + '/settings', 414 path: path + npmPlugin + '/settings',
420 fields: { settings }, 415 fields: { settings },
421 token: server.accessToken, 416 token: server.accessToken,
422 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 417 expectedStatus: HttpStatusCode.NO_CONTENT_204
423 }) 418 })
424 }) 419 })
425 }) 420 })
@@ -434,7 +429,7 @@ describe('Test server plugins API validators', function () {
434 path: path + suffix, 429 path: path + suffix,
435 fields: { npmName: npmPlugin }, 430 fields: { npmName: npmPlugin },
436 token: 'fake_token', 431 token: 'fake_token',
437 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 432 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
438 }) 433 })
439 } 434 }
440 }) 435 })
@@ -446,7 +441,7 @@ describe('Test server plugins API validators', function () {
446 path: path + suffix, 441 path: path + suffix,
447 fields: { npmName: npmPlugin }, 442 fields: { npmName: npmPlugin },
448 token: userAccessToken, 443 token: userAccessToken,
449 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 444 expectedStatus: HttpStatusCode.FORBIDDEN_403
450 }) 445 })
451 } 446 }
452 }) 447 })
@@ -458,7 +453,7 @@ describe('Test server plugins API validators', function () {
458 path: path + suffix, 453 path: path + suffix,
459 fields: { npmName: 'toto' }, 454 fields: { npmName: 'toto' },
460 token: server.accessToken, 455 token: server.accessToken,
461 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 456 expectedStatus: HttpStatusCode.BAD_REQUEST_400
462 }) 457 })
463 } 458 }
464 459
@@ -468,7 +463,7 @@ describe('Test server plugins API validators', function () {
468 path: path + suffix, 463 path: path + suffix,
469 fields: { npmName: 'peertube-plugin-TOTO' }, 464 fields: { npmName: 'peertube-plugin-TOTO' },
470 token: server.accessToken, 465 token: server.accessToken,
471 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 466 expectedStatus: HttpStatusCode.BAD_REQUEST_400
472 }) 467 })
473 } 468 }
474 }) 469 })
@@ -488,7 +483,7 @@ describe('Test server plugins API validators', function () {
488 path: path + obj.suffix, 483 path: path + obj.suffix,
489 fields: { npmName: npmPlugin }, 484 fields: { npmName: npmPlugin },
490 token: server.accessToken, 485 token: server.accessToken,
491 statusCodeExpected: obj.status 486 expectedStatus: obj.status
492 }) 487 })
493 } 488 }
494 }) 489 })
diff --git a/server/tests/api/check-params/redundancy.ts b/server/tests/api/check-params/redundancy.ts
index dac6938de..d9f905549 100644
--- a/server/tests/api/check-params/redundancy.ts
+++ b/server/tests/api/check-params/redundancy.ts
@@ -1,30 +1,25 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { VideoCreateResult } from '@shared/models'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6import { 4import {
7 checkBadCountPagination, 5 checkBadCountPagination,
8 checkBadSortPagination, 6 checkBadSortPagination,
9 checkBadStartPagination, 7 checkBadStartPagination,
10 cleanupTests, 8 cleanupTests,
11 createUser, 9 createMultipleServers,
12 doubleFollow, 10 doubleFollow,
13 flushAndRunMultipleServers,
14 getVideo,
15 makeDeleteRequest, 11 makeDeleteRequest,
16 makeGetRequest, 12 makeGetRequest,
17 makePostBodyRequest, 13 makePostBodyRequest,
18 makePutBodyRequest, 14 makePutBodyRequest,
19 ServerInfo, 15 PeerTubeServer,
20 setAccessTokensToServers, 16 setAccessTokensToServers,
21 uploadVideoAndGetId,
22 userLogin,
23 waitJobs 17 waitJobs
24} from '../../../../shared/extra-utils' 18} from '@shared/extra-utils'
19import { HttpStatusCode, VideoCreateResult } from '@shared/models'
25 20
26describe('Test server redundancy API validators', function () { 21describe('Test server redundancy API validators', function () {
27 let servers: ServerInfo[] 22 let servers: PeerTubeServer[]
28 let userAccessToken = null 23 let userAccessToken = null
29 let videoIdLocal: number 24 let videoIdLocal: number
30 let videoRemote: VideoCreateResult 25 let videoRemote: VideoCreateResult
@@ -34,7 +29,7 @@ describe('Test server redundancy API validators', function () {
34 before(async function () { 29 before(async function () {
35 this.timeout(80000) 30 this.timeout(80000)
36 31
37 servers = await flushAndRunMultipleServers(2) 32 servers = await createMultipleServers(2)
38 33
39 await setAccessTokensToServers(servers) 34 await setAccessTokensToServers(servers)
40 await doubleFollow(servers[0], servers[1]) 35 await doubleFollow(servers[0], servers[1])
@@ -44,17 +39,16 @@ describe('Test server redundancy API validators', function () {
44 password: 'password' 39 password: 'password'
45 } 40 }
46 41
47 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 42 await servers[0].users.create({ username: user.username, password: user.password })
48 userAccessToken = await userLogin(servers[0], user) 43 userAccessToken = await servers[0].login.getAccessToken(user)
49 44
50 videoIdLocal = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video' })).id 45 videoIdLocal = (await servers[0].videos.quickUpload({ name: 'video' })).id
51 46
52 const remoteUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video' })).uuid 47 const remoteUUID = (await servers[1].videos.quickUpload({ name: 'video' })).uuid
53 48
54 await waitJobs(servers) 49 await waitJobs(servers)
55 50
56 const resVideo = await getVideo(servers[0].url, remoteUUID) 51 videoRemote = await servers[0].videos.get({ id: remoteUUID })
57 videoRemote = resVideo.body
58 }) 52 })
59 53
60 describe('When listing redundancies', function () { 54 describe('When listing redundancies', function () {
@@ -69,11 +63,11 @@ describe('Test server redundancy API validators', function () {
69 }) 63 })
70 64
71 it('Should fail with an invalid token', async function () { 65 it('Should fail with an invalid token', async function () {
72 await makeGetRequest({ url, path, token: 'fake_token', statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 66 await makeGetRequest({ url, path, token: 'fake_token', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
73 }) 67 })
74 68
75 it('Should fail if the user is not an administrator', async function () { 69 it('Should fail if the user is not an administrator', async function () {
76 await makeGetRequest({ url, path, token: userAccessToken, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 70 await makeGetRequest({ url, path, token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
77 }) 71 })
78 72
79 it('Should fail with a bad start pagination', async function () { 73 it('Should fail with a bad start pagination', async function () {
@@ -97,7 +91,7 @@ describe('Test server redundancy API validators', function () {
97 }) 91 })
98 92
99 it('Should succeed with the correct params', async function () { 93 it('Should succeed with the correct params', async function () {
100 await makeGetRequest({ url, path, token, query: { target: 'my-videos' }, statusCodeExpected: HttpStatusCode.OK_200 }) 94 await makeGetRequest({ url, path, token, query: { target: 'my-videos' }, expectedStatus: HttpStatusCode.OK_200 })
101 }) 95 })
102 }) 96 })
103 97
@@ -113,11 +107,11 @@ describe('Test server redundancy API validators', function () {
113 }) 107 })
114 108
115 it('Should fail with an invalid token', async function () { 109 it('Should fail with an invalid token', async function () {
116 await makePostBodyRequest({ url, path, token: 'fake_token', statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 110 await makePostBodyRequest({ url, path, token: 'fake_token', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
117 }) 111 })
118 112
119 it('Should fail if the user is not an administrator', async function () { 113 it('Should fail if the user is not an administrator', async function () {
120 await makePostBodyRequest({ url, path, token: userAccessToken, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 114 await makePostBodyRequest({ url, path, token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
121 }) 115 })
122 116
123 it('Should fail without a video id', async function () { 117 it('Should fail without a video id', async function () {
@@ -129,7 +123,7 @@ describe('Test server redundancy API validators', function () {
129 }) 123 })
130 124
131 it('Should fail with a not found video id', async function () { 125 it('Should fail with a not found video id', async function () {
132 await makePostBodyRequest({ url, path, token, fields: { videoId: 6565 }, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) 126 await makePostBodyRequest({ url, path, token, fields: { videoId: 6565 }, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
133 }) 127 })
134 128
135 it('Should fail with a local a video id', async function () { 129 it('Should fail with a local a video id', async function () {
@@ -142,7 +136,7 @@ describe('Test server redundancy API validators', function () {
142 path, 136 path,
143 token, 137 token,
144 fields: { videoId: videoRemote.shortUUID }, 138 fields: { videoId: videoRemote.shortUUID },
145 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 139 expectedStatus: HttpStatusCode.NO_CONTENT_204
146 }) 140 })
147 }) 141 })
148 142
@@ -156,7 +150,7 @@ describe('Test server redundancy API validators', function () {
156 path, 150 path,
157 token, 151 token,
158 fields: { videoId: videoRemote.uuid }, 152 fields: { videoId: videoRemote.uuid },
159 statusCodeExpected: HttpStatusCode.CONFLICT_409 153 expectedStatus: HttpStatusCode.CONFLICT_409
160 }) 154 })
161 }) 155 })
162 }) 156 })
@@ -173,11 +167,11 @@ describe('Test server redundancy API validators', function () {
173 }) 167 })
174 168
175 it('Should fail with an invalid token', async function () { 169 it('Should fail with an invalid token', async function () {
176 await makeDeleteRequest({ url, path: path + '1', token: 'fake_token', statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 170 await makeDeleteRequest({ url, path: path + '1', token: 'fake_token', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
177 }) 171 })
178 172
179 it('Should fail if the user is not an administrator', async function () { 173 it('Should fail if the user is not an administrator', async function () {
180 await makeDeleteRequest({ url, path: path + '1', token: userAccessToken, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 174 await makeDeleteRequest({ url, path: path + '1', token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
181 }) 175 })
182 176
183 it('Should fail with an incorrect video id', async function () { 177 it('Should fail with an incorrect video id', async function () {
@@ -185,7 +179,7 @@ describe('Test server redundancy API validators', function () {
185 }) 179 })
186 180
187 it('Should fail with a not found video redundancy', async function () { 181 it('Should fail with a not found video redundancy', async function () {
188 await makeDeleteRequest({ url, path: path + '454545', token, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) 182 await makeDeleteRequest({ url, path: path + '454545', token, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
189 }) 183 })
190 }) 184 })
191 185
@@ -198,7 +192,7 @@ describe('Test server redundancy API validators', function () {
198 path: path + '/localhost:' + servers[1].port, 192 path: path + '/localhost:' + servers[1].port,
199 fields: { redundancyAllowed: true }, 193 fields: { redundancyAllowed: true },
200 token: 'fake_token', 194 token: 'fake_token',
201 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 195 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
202 }) 196 })
203 }) 197 })
204 198
@@ -208,7 +202,7 @@ describe('Test server redundancy API validators', function () {
208 path: path + '/localhost:' + servers[1].port, 202 path: path + '/localhost:' + servers[1].port,
209 fields: { redundancyAllowed: true }, 203 fields: { redundancyAllowed: true },
210 token: userAccessToken, 204 token: userAccessToken,
211 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 205 expectedStatus: HttpStatusCode.FORBIDDEN_403
212 }) 206 })
213 }) 207 })
214 208
@@ -218,7 +212,7 @@ describe('Test server redundancy API validators', function () {
218 path: path + '/example.com', 212 path: path + '/example.com',
219 fields: { redundancyAllowed: true }, 213 fields: { redundancyAllowed: true },
220 token: servers[0].accessToken, 214 token: servers[0].accessToken,
221 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 215 expectedStatus: HttpStatusCode.NOT_FOUND_404
222 }) 216 })
223 }) 217 })
224 218
@@ -228,7 +222,7 @@ describe('Test server redundancy API validators', function () {
228 path: path + '/localhost:' + servers[1].port, 222 path: path + '/localhost:' + servers[1].port,
229 fields: { blabla: true }, 223 fields: { blabla: true },
230 token: servers[0].accessToken, 224 token: servers[0].accessToken,
231 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 225 expectedStatus: HttpStatusCode.BAD_REQUEST_400
232 }) 226 })
233 }) 227 })
234 228
@@ -238,7 +232,7 @@ describe('Test server redundancy API validators', function () {
238 path: path + '/localhost:' + servers[1].port, 232 path: path + '/localhost:' + servers[1].port,
239 fields: { redundancyAllowed: true }, 233 fields: { redundancyAllowed: true },
240 token: servers[0].accessToken, 234 token: servers[0].accessToken,
241 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 235 expectedStatus: HttpStatusCode.NO_CONTENT_204
242 }) 236 })
243 }) 237 })
244 }) 238 })
diff --git a/server/tests/api/check-params/search.ts b/server/tests/api/check-params/search.ts
index 20ad46cff..cc15d2593 100644
--- a/server/tests/api/check-params/search.ts
+++ b/server/tests/api/check-params/search.ts
@@ -2,41 +2,39 @@
2 2
3import 'mocha' 3import 'mocha'
4import { 4import {
5 checkBadCountPagination,
6 checkBadSortPagination,
7 checkBadStartPagination,
5 cleanupTests, 8 cleanupTests,
6 flushAndRunServer, 9 createSingleServer,
7 immutableAssign,
8 makeGetRequest, 10 makeGetRequest,
9 ServerInfo, 11 PeerTubeServer,
10 updateCustomSubConfig,
11 setAccessTokensToServers 12 setAccessTokensToServers
12} from '../../../../shared/extra-utils' 13} from '@shared/extra-utils'
13import { 14import { HttpStatusCode } from '@shared/models'
14 checkBadCountPagination, 15
15 checkBadSortPagination, 16function updateSearchIndex (server: PeerTubeServer, enabled: boolean, disableLocalSearch = false) {
16 checkBadStartPagination 17 return server.config.updateCustomSubConfig({
17} from '../../../../shared/extra-utils/requests/check-api-params' 18 newConfig: {
18import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 19 search: {
19 20 searchIndex: {
20function updateSearchIndex (server: ServerInfo, enabled: boolean, disableLocalSearch = false) { 21 enabled,
21 return updateCustomSubConfig(server.url, server.accessToken, { 22 disableLocalSearch
22 search: { 23 }
23 searchIndex: {
24 enabled,
25 disableLocalSearch
26 } 24 }
27 } 25 }
28 }) 26 })
29} 27}
30 28
31describe('Test videos API validator', function () { 29describe('Test videos API validator', function () {
32 let server: ServerInfo 30 let server: PeerTubeServer
33 31
34 // --------------------------------------------------------------- 32 // ---------------------------------------------------------------
35 33
36 before(async function () { 34 before(async function () {
37 this.timeout(30000) 35 this.timeout(30000)
38 36
39 server = await flushAndRunServer(1) 37 server = await createSingleServer(1)
40 await setAccessTokensToServers([ server ]) 38 await setAccessTokensToServers([ server ])
41 }) 39 })
42 40
@@ -59,84 +57,104 @@ describe('Test videos API validator', function () {
59 await checkBadSortPagination(server.url, path, null, query) 57 await checkBadSortPagination(server.url, path, null, query)
60 }) 58 })
61 59
62 it('Should success with the correct parameters', async function () { 60 it('Should succeed with the correct parameters', async function () {
63 await makeGetRequest({ url: server.url, path, query, statusCodeExpected: HttpStatusCode.OK_200 }) 61 await makeGetRequest({ url: server.url, path, query, expectedStatus: HttpStatusCode.OK_200 })
64 }) 62 })
65 63
66 it('Should fail with an invalid category', async function () { 64 it('Should fail with an invalid category', async function () {
67 const customQuery1 = immutableAssign(query, { categoryOneOf: [ 'aa', 'b' ] }) 65 const customQuery1 = { ...query, categoryOneOf: [ 'aa', 'b' ] }
68 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 66 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
69 67
70 const customQuery2 = immutableAssign(query, { categoryOneOf: 'a' }) 68 const customQuery2 = { ...query, categoryOneOf: 'a' }
71 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 69 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
72 }) 70 })
73 71
74 it('Should succeed with a valid category', async function () { 72 it('Should succeed with a valid category', async function () {
75 const customQuery1 = immutableAssign(query, { categoryOneOf: [ 1, 7 ] }) 73 const customQuery1 = { ...query, categoryOneOf: [ 1, 7 ] }
76 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.OK_200 }) 74 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.OK_200 })
77 75
78 const customQuery2 = immutableAssign(query, { categoryOneOf: 1 }) 76 const customQuery2 = { ...query, categoryOneOf: 1 }
79 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.OK_200 }) 77 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.OK_200 })
80 }) 78 })
81 79
82 it('Should fail with an invalid licence', async function () { 80 it('Should fail with an invalid licence', async function () {
83 const customQuery1 = immutableAssign(query, { licenceOneOf: [ 'aa', 'b' ] }) 81 const customQuery1 = { ...query, licenceOneOf: [ 'aa', 'b' ] }
84 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 82 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
85 83
86 const customQuery2 = immutableAssign(query, { licenceOneOf: 'a' }) 84 const customQuery2 = { ...query, licenceOneOf: 'a' }
87 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 85 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
88 }) 86 })
89 87
90 it('Should succeed with a valid licence', async function () { 88 it('Should succeed with a valid licence', async function () {
91 const customQuery1 = immutableAssign(query, { licenceOneOf: [ 1, 2 ] }) 89 const customQuery1 = { ...query, licenceOneOf: [ 1, 2 ] }
92 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.OK_200 }) 90 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.OK_200 })
93 91
94 const customQuery2 = immutableAssign(query, { licenceOneOf: 1 }) 92 const customQuery2 = { ...query, licenceOneOf: 1 }
95 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.OK_200 }) 93 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.OK_200 })
96 }) 94 })
97 95
98 it('Should succeed with a valid language', async function () { 96 it('Should succeed with a valid language', async function () {
99 const customQuery1 = immutableAssign(query, { languageOneOf: [ 'fr', 'en' ] }) 97 const customQuery1 = { ...query, languageOneOf: [ 'fr', 'en' ] }
100 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.OK_200 }) 98 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.OK_200 })
101 99
102 const customQuery2 = immutableAssign(query, { languageOneOf: 'fr' }) 100 const customQuery2 = { ...query, languageOneOf: 'fr' }
103 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.OK_200 }) 101 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.OK_200 })
104 }) 102 })
105 103
106 it('Should succeed with valid tags', async function () { 104 it('Should succeed with valid tags', async function () {
107 const customQuery1 = immutableAssign(query, { tagsOneOf: [ 'tag1', 'tag2' ] }) 105 const customQuery1 = { ...query, tagsOneOf: [ 'tag1', 'tag2' ] }
108 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.OK_200 }) 106 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.OK_200 })
109 107
110 const customQuery2 = immutableAssign(query, { tagsOneOf: 'tag1' }) 108 const customQuery2 = { ...query, tagsOneOf: 'tag1' }
111 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.OK_200 }) 109 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.OK_200 })
112 110
113 const customQuery3 = immutableAssign(query, { tagsAllOf: [ 'tag1', 'tag2' ] }) 111 const customQuery3 = { ...query, tagsAllOf: [ 'tag1', 'tag2' ] }
114 await makeGetRequest({ url: server.url, path, query: customQuery3, statusCodeExpected: HttpStatusCode.OK_200 }) 112 await makeGetRequest({ url: server.url, path, query: customQuery3, expectedStatus: HttpStatusCode.OK_200 })
115 113
116 const customQuery4 = immutableAssign(query, { tagsAllOf: 'tag1' }) 114 const customQuery4 = { ...query, tagsAllOf: 'tag1' }
117 await makeGetRequest({ url: server.url, path, query: customQuery4, statusCodeExpected: HttpStatusCode.OK_200 }) 115 await makeGetRequest({ url: server.url, path, query: customQuery4, expectedStatus: HttpStatusCode.OK_200 })
118 }) 116 })
119 117
120 it('Should fail with invalid durations', async function () { 118 it('Should fail with invalid durations', async function () {
121 const customQuery1 = immutableAssign(query, { durationMin: 'hello' }) 119 const customQuery1 = { ...query, durationMin: 'hello' }
122 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 120 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
123 121
124 const customQuery2 = immutableAssign(query, { durationMax: 'hello' }) 122 const customQuery2 = { ...query, durationMax: 'hello' }
125 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 123 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
126 }) 124 })
127 125
128 it('Should fail with invalid dates', async function () { 126 it('Should fail with invalid dates', async function () {
129 const customQuery1 = immutableAssign(query, { startDate: 'hello' }) 127 const customQuery1 = { ...query, startDate: 'hello' }
130 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 128 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
129
130 const customQuery2 = { ...query, endDate: 'hello' }
131 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
132
133 const customQuery3 = { ...query, originallyPublishedStartDate: 'hello' }
134 await makeGetRequest({ url: server.url, path, query: customQuery3, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
135
136 const customQuery4 = { ...query, originallyPublishedEndDate: 'hello' }
137 await makeGetRequest({ url: server.url, path, query: customQuery4, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
138 })
139
140 it('Should fail with an invalid host', async function () {
141 const customQuery = { ...query, host: '6565' }
142 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
143 })
131 144
132 const customQuery2 = immutableAssign(query, { endDate: 'hello' }) 145 it('Should succeed with a host', async function () {
133 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 146 const customQuery = { ...query, host: 'example.com' }
147 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
148 })
134 149
135 const customQuery3 = immutableAssign(query, { originallyPublishedStartDate: 'hello' }) 150 it('Should fail with invalid uuids', async function () {
136 await makeGetRequest({ url: server.url, path, query: customQuery3, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 151 const customQuery = { ...query, uuids: [ '6565', 'dfd70b83-639f-4980-94af-304a56ab4b35' ] }
152 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
153 })
137 154
138 const customQuery4 = immutableAssign(query, { originallyPublishedEndDate: 'hello' }) 155 it('Should succeed with valid uuids', async function () {
139 await makeGetRequest({ url: server.url, path, query: customQuery4, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 156 const customQuery = { ...query, uuids: [ 'dfd70b83-639f-4980-94af-304a56ab4b35' ] }
157 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
140 }) 158 })
141 }) 159 })
142 160
@@ -144,7 +162,8 @@ describe('Test videos API validator', function () {
144 const path = '/api/v1/search/video-playlists/' 162 const path = '/api/v1/search/video-playlists/'
145 163
146 const query = { 164 const query = {
147 search: 'coucou' 165 search: 'coucou',
166 host: 'example.com'
148 } 167 }
149 168
150 it('Should fail with a bad start pagination', async function () { 169 it('Should fail with a bad start pagination', async function () {
@@ -159,8 +178,17 @@ describe('Test videos API validator', function () {
159 await checkBadSortPagination(server.url, path, null, query) 178 await checkBadSortPagination(server.url, path, null, query)
160 }) 179 })
161 180
162 it('Should success with the correct parameters', async function () { 181 it('Should fail with an invalid host', async function () {
163 await makeGetRequest({ url: server.url, path, query, statusCodeExpected: HttpStatusCode.OK_200 }) 182 await makeGetRequest({ url: server.url, path, query: { ...query, host: '6565' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
183 })
184
185 it('Should fail with invalid uuids', async function () {
186 const customQuery = { ...query, uuids: [ '6565', 'dfd70b83-639f-4980-94af-304a56ab4b35' ] }
187 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
188 })
189
190 it('Should succeed with the correct parameters', async function () {
191 await makeGetRequest({ url: server.url, path, query, expectedStatus: HttpStatusCode.OK_200 })
164 }) 192 })
165 }) 193 })
166 194
@@ -168,7 +196,8 @@ describe('Test videos API validator', function () {
168 const path = '/api/v1/search/video-channels/' 196 const path = '/api/v1/search/video-channels/'
169 197
170 const query = { 198 const query = {
171 search: 'coucou' 199 search: 'coucou',
200 host: 'example.com'
172 } 201 }
173 202
174 it('Should fail with a bad start pagination', async function () { 203 it('Should fail with a bad start pagination', async function () {
@@ -183,8 +212,16 @@ describe('Test videos API validator', function () {
183 await checkBadSortPagination(server.url, path, null, query) 212 await checkBadSortPagination(server.url, path, null, query)
184 }) 213 })
185 214
186 it('Should success with the correct parameters', async function () { 215 it('Should fail with an invalid host', async function () {
187 await makeGetRequest({ url: server.url, path, query, statusCodeExpected: HttpStatusCode.OK_200 }) 216 await makeGetRequest({ url: server.url, path, query: { ...query, host: '6565' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
217 })
218
219 it('Should fail with invalid handles', async function () {
220 await makeGetRequest({ url: server.url, path, query: { ...query, handles: [ '' ] }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
221 })
222
223 it('Should succeed with the correct parameters', async function () {
224 await makeGetRequest({ url: server.url, path, query, expectedStatus: HttpStatusCode.OK_200 })
188 }) 225 })
189 }) 226 })
190 227
@@ -202,42 +239,42 @@ describe('Test videos API validator', function () {
202 239
203 for (const path of paths) { 240 for (const path of paths) {
204 { 241 {
205 const customQuery = immutableAssign(query, { searchTarget: 'hello' }) 242 const customQuery = { ...query, searchTarget: 'hello' }
206 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 243 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
207 } 244 }
208 245
209 { 246 {
210 const customQuery = immutableAssign(query, { searchTarget: undefined }) 247 const customQuery = { ...query, searchTarget: undefined }
211 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.OK_200 }) 248 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
212 } 249 }
213 250
214 { 251 {
215 const customQuery = immutableAssign(query, { searchTarget: 'local' }) 252 const customQuery = { ...query, searchTarget: 'local' }
216 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.OK_200 }) 253 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
217 } 254 }
218 255
219 { 256 {
220 const customQuery = immutableAssign(query, { searchTarget: 'search-index' }) 257 const customQuery = { ...query, searchTarget: 'search-index' }
221 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 258 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
222 } 259 }
223 260
224 await updateSearchIndex(server, true, true) 261 await updateSearchIndex(server, true, true)
225 262
226 { 263 {
227 const customQuery = immutableAssign(query, { searchTarget: 'local' }) 264 const customQuery = { ...query, searchTarget: 'local' }
228 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 265 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
229 } 266 }
230 267
231 { 268 {
232 const customQuery = immutableAssign(query, { searchTarget: 'search-index' }) 269 const customQuery = { ...query, searchTarget: 'search-index' }
233 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.OK_200 }) 270 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
234 } 271 }
235 272
236 await updateSearchIndex(server, true, false) 273 await updateSearchIndex(server, true, false)
237 274
238 { 275 {
239 const customQuery = immutableAssign(query, { searchTarget: 'local' }) 276 const customQuery = { ...query, searchTarget: 'local' }
240 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.OK_200 }) 277 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
241 } 278 }
242 279
243 await updateSearchIndex(server, false, false) 280 await updateSearchIndex(server, false, false)
diff --git a/server/tests/api/check-params/services.ts b/server/tests/api/check-params/services.ts
index 514e3da70..8d795fabc 100644
--- a/server/tests/api/check-params/services.ts
+++ b/server/tests/api/check-params/services.ts
@@ -1,22 +1,18 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4
5import { 4import {
6 cleanupTests, 5 cleanupTests,
7 flushAndRunServer, 6 createSingleServer,
8 makeGetRequest, 7 makeGetRequest,
9 ServerInfo, 8 PeerTubeServer,
10 setAccessTokensToServers, 9 setAccessTokensToServers,
11 uploadVideo,
12 createVideoPlaylist,
13 setDefaultVideoChannel 10 setDefaultVideoChannel
14} from '../../../../shared/extra-utils' 11} from '@shared/extra-utils'
15import { VideoPlaylistPrivacy } from '@shared/models' 12import { HttpStatusCode, VideoPlaylistPrivacy } from '@shared/models'
16import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
17 13
18describe('Test services API validators', function () { 14describe('Test services API validators', function () {
19 let server: ServerInfo 15 let server: PeerTubeServer
20 let playlistUUID: string 16 let playlistUUID: string
21 17
22 // --------------------------------------------------------------- 18 // ---------------------------------------------------------------
@@ -24,27 +20,22 @@ describe('Test services API validators', function () {
24 before(async function () { 20 before(async function () {
25 this.timeout(60000) 21 this.timeout(60000)
26 22
27 server = await flushAndRunServer(1) 23 server = await createSingleServer(1)
28 await setAccessTokensToServers([ server ]) 24 await setAccessTokensToServers([ server ])
29 await setDefaultVideoChannel([ server ]) 25 await setDefaultVideoChannel([ server ])
30 26
31 { 27 server.store.videoCreated = await server.videos.upload({ attributes: { name: 'my super name' } })
32 const res = await uploadVideo(server.url, server.accessToken, { name: 'my super name' })
33 server.video = res.body.video
34 }
35 28
36 { 29 {
37 const res = await createVideoPlaylist({ 30 const created = await server.playlists.create({
38 url: server.url, 31 attributes: {
39 token: server.accessToken,
40 playlistAttrs: {
41 displayName: 'super playlist', 32 displayName: 'super playlist',
42 privacy: VideoPlaylistPrivacy.PUBLIC, 33 privacy: VideoPlaylistPrivacy.PUBLIC,
43 videoChannelId: server.videoChannel.id 34 videoChannelId: server.store.channel.id
44 } 35 }
45 }) 36 })
46 37
47 playlistUUID = res.body.videoPlaylist.uuid 38 playlistUUID = created.uuid
48 } 39 }
49 }) 40 })
50 41
@@ -56,7 +47,7 @@ describe('Test services API validators', function () {
56 }) 47 })
57 48
58 it('Should fail with an invalid host', async function () { 49 it('Should fail with an invalid host', async function () {
59 const embedUrl = 'http://hello.com/videos/watch/' + server.video.uuid 50 const embedUrl = 'http://hello.com/videos/watch/' + server.store.videoCreated.uuid
60 await checkParamEmbed(server, embedUrl) 51 await checkParamEmbed(server, embedUrl)
61 }) 52 })
62 53
@@ -71,37 +62,37 @@ describe('Test services API validators', function () {
71 }) 62 })
72 63
73 it('Should fail with an invalid path', async function () { 64 it('Should fail with an invalid path', async function () {
74 const embedUrl = `http://localhost:${server.port}/videos/watchs/${server.video.uuid}` 65 const embedUrl = `http://localhost:${server.port}/videos/watchs/${server.store.videoCreated.uuid}`
75 66
76 await checkParamEmbed(server, embedUrl) 67 await checkParamEmbed(server, embedUrl)
77 }) 68 })
78 69
79 it('Should fail with an invalid max height', async function () { 70 it('Should fail with an invalid max height', async function () {
80 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.video.uuid}` 71 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.store.videoCreated.uuid}`
81 72
82 await checkParamEmbed(server, embedUrl, HttpStatusCode.BAD_REQUEST_400, { maxheight: 'hello' }) 73 await checkParamEmbed(server, embedUrl, HttpStatusCode.BAD_REQUEST_400, { maxheight: 'hello' })
83 }) 74 })
84 75
85 it('Should fail with an invalid max width', async function () { 76 it('Should fail with an invalid max width', async function () {
86 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.video.uuid}` 77 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.store.videoCreated.uuid}`
87 78
88 await checkParamEmbed(server, embedUrl, HttpStatusCode.BAD_REQUEST_400, { maxwidth: 'hello' }) 79 await checkParamEmbed(server, embedUrl, HttpStatusCode.BAD_REQUEST_400, { maxwidth: 'hello' })
89 }) 80 })
90 81
91 it('Should fail with an invalid format', async function () { 82 it('Should fail with an invalid format', async function () {
92 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.video.uuid}` 83 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.store.videoCreated.uuid}`
93 84
94 await checkParamEmbed(server, embedUrl, HttpStatusCode.BAD_REQUEST_400, { format: 'blabla' }) 85 await checkParamEmbed(server, embedUrl, HttpStatusCode.BAD_REQUEST_400, { format: 'blabla' })
95 }) 86 })
96 87
97 it('Should fail with a non supported format', async function () { 88 it('Should fail with a non supported format', async function () {
98 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.video.uuid}` 89 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.store.videoCreated.uuid}`
99 90
100 await checkParamEmbed(server, embedUrl, HttpStatusCode.NOT_IMPLEMENTED_501, { format: 'xml' }) 91 await checkParamEmbed(server, embedUrl, HttpStatusCode.NOT_IMPLEMENTED_501, { format: 'xml' })
101 }) 92 })
102 93
103 it('Should succeed with the correct params with a video', async function () { 94 it('Should succeed with the correct params with a video', async function () {
104 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.video.uuid}` 95 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.store.videoCreated.uuid}`
105 const query = { 96 const query = {
106 format: 'json', 97 format: 'json',
107 maxheight: 400, 98 maxheight: 400,
@@ -128,13 +119,13 @@ describe('Test services API validators', function () {
128 }) 119 })
129}) 120})
130 121
131function checkParamEmbed (server: ServerInfo, embedUrl: string, statusCodeExpected = HttpStatusCode.BAD_REQUEST_400, query = {}) { 122function checkParamEmbed (server: PeerTubeServer, embedUrl: string, expectedStatus = HttpStatusCode.BAD_REQUEST_400, query = {}) {
132 const path = '/services/oembed' 123 const path = '/services/oembed'
133 124
134 return makeGetRequest({ 125 return makeGetRequest({
135 url: server.url, 126 url: server.url,
136 path, 127 path,
137 query: Object.assign(query, { url: embedUrl }), 128 query: Object.assign(query, { url: embedUrl }),
138 statusCodeExpected 129 expectedStatus
139 }) 130 })
140} 131}
diff --git a/server/tests/api/check-params/upload-quota.ts b/server/tests/api/check-params/upload-quota.ts
index d0fbec415..322e93d0d 100644
--- a/server/tests/api/check-params/upload-quota.ts
+++ b/server/tests/api/check-params/upload-quota.ts
@@ -2,46 +2,39 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { HttpStatusCode, randomInt } from '@shared/core-utils' 5import { randomInt } from '@shared/core-utils'
6import { getGoodVideoUrl, getMagnetURI, getMyVideoImports, importVideo } from '@shared/extra-utils/videos/video-imports'
7import { MyUser, VideoImport, VideoImportState, VideoPrivacy } from '@shared/models'
8import { 6import {
9 cleanupTests, 7 cleanupTests,
10 flushAndRunServer, 8 createSingleServer,
11 getMyUserInformation, 9 FIXTURE_URLS,
12 immutableAssign, 10 PeerTubeServer,
13 registerUser,
14 ServerInfo,
15 setAccessTokensToServers, 11 setAccessTokensToServers,
16 setDefaultVideoChannel, 12 setDefaultVideoChannel,
17 updateUser, 13 VideosCommand,
18 uploadVideo,
19 userLogin,
20 waitJobs 14 waitJobs
21} from '../../../../shared/extra-utils' 15} from '@shared/extra-utils'
16import { HttpStatusCode, VideoImportState, VideoPrivacy } from '@shared/models'
22 17
23describe('Test upload quota', function () { 18describe('Test upload quota', function () {
24 let server: ServerInfo 19 let server: PeerTubeServer
25 let rootId: number 20 let rootId: number
21 let command: VideosCommand
26 22
27 // --------------------------------------------------------------- 23 // ---------------------------------------------------------------
28 24
29 before(async function () { 25 before(async function () {
30 this.timeout(30000) 26 this.timeout(30000)
31 27
32 server = await flushAndRunServer(1) 28 server = await createSingleServer(1)
33 await setAccessTokensToServers([ server ]) 29 await setAccessTokensToServers([ server ])
34 await setDefaultVideoChannel([ server ]) 30 await setDefaultVideoChannel([ server ])
35 31
36 const res = await getMyUserInformation(server.url, server.accessToken) 32 const user = await server.users.getMyInfo()
37 rootId = (res.body as MyUser).id 33 rootId = user.id
38 34
39 await updateUser({ 35 await server.users.update({ userId: rootId, videoQuota: 42 })
40 url: server.url, 36
41 userId: rootId, 37 command = server.videos
42 accessToken: server.accessToken,
43 videoQuota: 42
44 })
45 }) 38 })
46 39
47 describe('When having a video quota', function () { 40 describe('When having a video quota', function () {
@@ -50,49 +43,48 @@ describe('Test upload quota', function () {
50 this.timeout(30000) 43 this.timeout(30000)
51 44
52 const user = { username: 'registered' + randomInt(1, 1500), password: 'password' } 45 const user = { username: 'registered' + randomInt(1, 1500), password: 'password' }
53 await registerUser(server.url, user.username, user.password) 46 await server.users.register(user)
54 const userAccessToken = await userLogin(server, user) 47 const userToken = await server.login.getAccessToken(user)
55 48
56 const videoAttributes = { fixture: 'video_short2.webm' } 49 const attributes = { fixture: 'video_short2.webm' }
57 for (let i = 0; i < 5; i++) { 50 for (let i = 0; i < 5; i++) {
58 await uploadVideo(server.url, userAccessToken, videoAttributes) 51 await command.upload({ token: userToken, attributes })
59 } 52 }
60 53
61 await uploadVideo(server.url, userAccessToken, videoAttributes, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'legacy') 54 await command.upload({ token: userToken, attributes, expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'legacy' })
62 }) 55 })
63 56
64 it('Should fail with a registered user having too many videos with resumable upload', async function () { 57 it('Should fail with a registered user having too many videos with resumable upload', async function () {
65 this.timeout(30000) 58 this.timeout(30000)
66 59
67 const user = { username: 'registered' + randomInt(1, 1500), password: 'password' } 60 const user = { username: 'registered' + randomInt(1, 1500), password: 'password' }
68 await registerUser(server.url, user.username, user.password) 61 await server.users.register(user)
69 const userAccessToken = await userLogin(server, user) 62 const userToken = await server.login.getAccessToken(user)
70 63
71 const videoAttributes = { fixture: 'video_short2.webm' } 64 const attributes = { fixture: 'video_short2.webm' }
72 for (let i = 0; i < 5; i++) { 65 for (let i = 0; i < 5; i++) {
73 await uploadVideo(server.url, userAccessToken, videoAttributes) 66 await command.upload({ token: userToken, attributes })
74 } 67 }
75 68
76 await uploadVideo(server.url, userAccessToken, videoAttributes, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'resumable') 69 await command.upload({ token: userToken, attributes, expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'resumable' })
77 }) 70 })
78 71
79 it('Should fail to import with HTTP/Torrent/magnet', async function () { 72 it('Should fail to import with HTTP/Torrent/magnet', async function () {
80 this.timeout(120000) 73 this.timeout(120000)
81 74
82 const baseAttributes = { 75 const baseAttributes = {
83 channelId: server.videoChannel.id, 76 channelId: server.store.channel.id,
84 privacy: VideoPrivacy.PUBLIC 77 privacy: VideoPrivacy.PUBLIC
85 } 78 }
86 await importVideo(server.url, server.accessToken, immutableAssign(baseAttributes, { targetUrl: getGoodVideoUrl() })) 79 await server.imports.importVideo({ attributes: { ...baseAttributes, targetUrl: FIXTURE_URLS.goodVideo } })
87 await importVideo(server.url, server.accessToken, immutableAssign(baseAttributes, { magnetUri: getMagnetURI() })) 80 await server.imports.importVideo({ attributes: { ...baseAttributes, magnetUri: FIXTURE_URLS.magnet } })
88 await importVideo(server.url, server.accessToken, immutableAssign(baseAttributes, { torrentfile: 'video-720p.torrent' as any })) 81 await server.imports.importVideo({ attributes: { ...baseAttributes, torrentfile: 'video-720p.torrent' as any } })
89 82
90 await waitJobs([ server ]) 83 await waitJobs([ server ])
91 84
92 const res = await getMyVideoImports(server.url, server.accessToken) 85 const { total, data: videoImports } = await server.imports.getMyVideoImports()
86 expect(total).to.equal(3)
93 87
94 expect(res.body.total).to.equal(3)
95 const videoImports: VideoImport[] = res.body.data
96 expect(videoImports).to.have.lengthOf(3) 88 expect(videoImports).to.have.lengthOf(3)
97 89
98 for (const videoImport of videoImports) { 90 for (const videoImport of videoImports) {
@@ -106,43 +98,34 @@ describe('Test upload quota', function () {
106 describe('When having a daily video quota', function () { 98 describe('When having a daily video quota', function () {
107 99
108 it('Should fail with a user having too many videos daily', async function () { 100 it('Should fail with a user having too many videos daily', async function () {
109 await updateUser({ 101 await server.users.update({ userId: rootId, videoQuotaDaily: 42 })
110 url: server.url,
111 userId: rootId,
112 accessToken: server.accessToken,
113 videoQuotaDaily: 42
114 })
115 102
116 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'legacy') 103 await command.upload({ expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'legacy' })
117 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'resumable') 104 await command.upload({ expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'resumable' })
118 }) 105 })
119 }) 106 })
120 107
121 describe('When having an absolute and daily video quota', function () { 108 describe('When having an absolute and daily video quota', function () {
122 it('Should fail if exceeding total quota', async function () { 109 it('Should fail if exceeding total quota', async function () {
123 await updateUser({ 110 await server.users.update({
124 url: server.url,
125 userId: rootId, 111 userId: rootId,
126 accessToken: server.accessToken,
127 videoQuota: 42, 112 videoQuota: 42,
128 videoQuotaDaily: 1024 * 1024 * 1024 113 videoQuotaDaily: 1024 * 1024 * 1024
129 }) 114 })
130 115
131 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'legacy') 116 await command.upload({ expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'legacy' })
132 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'resumable') 117 await command.upload({ expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'resumable' })
133 }) 118 })
134 119
135 it('Should fail if exceeding daily quota', async function () { 120 it('Should fail if exceeding daily quota', async function () {
136 await updateUser({ 121 await server.users.update({
137 url: server.url,
138 userId: rootId, 122 userId: rootId,
139 accessToken: server.accessToken,
140 videoQuota: 1024 * 1024 * 1024, 123 videoQuota: 1024 * 1024 * 1024,
141 videoQuotaDaily: 42 124 videoQuotaDaily: 42
142 }) 125 })
143 126
144 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'legacy') 127 await command.upload({ expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'legacy' })
145 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'resumable') 128 await command.upload({ expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'resumable' })
146 }) 129 })
147 }) 130 })
148 131
diff --git a/server/tests/api/check-params/user-notifications.ts b/server/tests/api/check-params/user-notifications.ts
index 26d4423f9..17edf5aa1 100644
--- a/server/tests/api/check-params/user-notifications.ts
+++ b/server/tests/api/check-params/user-notifications.ts
@@ -2,35 +2,30 @@
2 2
3import 'mocha' 3import 'mocha'
4import { io } from 'socket.io-client' 4import { io } from 'socket.io-client'
5
6import { 5import {
6 checkBadCountPagination,
7 checkBadSortPagination,
8 checkBadStartPagination,
7 cleanupTests, 9 cleanupTests,
8 flushAndRunServer, 10 createSingleServer,
9 immutableAssign,
10 makeGetRequest, 11 makeGetRequest,
11 makePostBodyRequest, 12 makePostBodyRequest,
12 makePutBodyRequest, 13 makePutBodyRequest,
13 ServerInfo, 14 PeerTubeServer,
14 setAccessTokensToServers, 15 setAccessTokensToServers,
15 wait 16 wait
16} from '../../../../shared/extra-utils' 17} from '@shared/extra-utils'
17import { 18import { HttpStatusCode, UserNotificationSetting, UserNotificationSettingValue } from '@shared/models'
18 checkBadCountPagination,
19 checkBadSortPagination,
20 checkBadStartPagination
21} from '../../../../shared/extra-utils/requests/check-api-params'
22import { UserNotificationSetting, UserNotificationSettingValue } from '../../../../shared/models/users'
23import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
24 19
25describe('Test user notifications API validators', function () { 20describe('Test user notifications API validators', function () {
26 let server: ServerInfo 21 let server: PeerTubeServer
27 22
28 // --------------------------------------------------------------- 23 // ---------------------------------------------------------------
29 24
30 before(async function () { 25 before(async function () {
31 this.timeout(30000) 26 this.timeout(30000)
32 27
33 server = await flushAndRunServer(1) 28 server = await createSingleServer(1)
34 29
35 await setAccessTokensToServers([ server ]) 30 await setAccessTokensToServers([ server ])
36 }) 31 })
@@ -58,7 +53,7 @@ describe('Test user notifications API validators', function () {
58 unread: 'toto' 53 unread: 'toto'
59 }, 54 },
60 token: server.accessToken, 55 token: server.accessToken,
61 statusCodeExpected: HttpStatusCode.OK_200 56 expectedStatus: HttpStatusCode.OK_200
62 }) 57 })
63 }) 58 })
64 59
@@ -66,7 +61,7 @@ describe('Test user notifications API validators', function () {
66 await makeGetRequest({ 61 await makeGetRequest({
67 url: server.url, 62 url: server.url,
68 path, 63 path,
69 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 64 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
70 }) 65 })
71 }) 66 })
72 67
@@ -75,7 +70,7 @@ describe('Test user notifications API validators', function () {
75 url: server.url, 70 url: server.url,
76 path, 71 path,
77 token: server.accessToken, 72 token: server.accessToken,
78 statusCodeExpected: HttpStatusCode.OK_200 73 expectedStatus: HttpStatusCode.OK_200
79 }) 74 })
80 }) 75 })
81 }) 76 })
@@ -91,7 +86,7 @@ describe('Test user notifications API validators', function () {
91 ids: [ 'hello' ] 86 ids: [ 'hello' ]
92 }, 87 },
93 token: server.accessToken, 88 token: server.accessToken,
94 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 89 expectedStatus: HttpStatusCode.BAD_REQUEST_400
95 }) 90 })
96 91
97 await makePostBodyRequest({ 92 await makePostBodyRequest({
@@ -101,7 +96,7 @@ describe('Test user notifications API validators', function () {
101 ids: [ ] 96 ids: [ ]
102 }, 97 },
103 token: server.accessToken, 98 token: server.accessToken,
104 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 99 expectedStatus: HttpStatusCode.BAD_REQUEST_400
105 }) 100 })
106 101
107 await makePostBodyRequest({ 102 await makePostBodyRequest({
@@ -111,7 +106,7 @@ describe('Test user notifications API validators', function () {
111 ids: 5 106 ids: 5
112 }, 107 },
113 token: server.accessToken, 108 token: server.accessToken,
114 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 109 expectedStatus: HttpStatusCode.BAD_REQUEST_400
115 }) 110 })
116 }) 111 })
117 112
@@ -122,7 +117,7 @@ describe('Test user notifications API validators', function () {
122 fields: { 117 fields: {
123 ids: [ 5 ] 118 ids: [ 5 ]
124 }, 119 },
125 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 120 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
126 }) 121 })
127 }) 122 })
128 123
@@ -134,7 +129,7 @@ describe('Test user notifications API validators', function () {
134 ids: [ 5 ] 129 ids: [ 5 ]
135 }, 130 },
136 token: server.accessToken, 131 token: server.accessToken,
137 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 132 expectedStatus: HttpStatusCode.NO_CONTENT_204
138 }) 133 })
139 }) 134 })
140 }) 135 })
@@ -146,7 +141,7 @@ describe('Test user notifications API validators', function () {
146 await makePostBodyRequest({ 141 await makePostBodyRequest({
147 url: server.url, 142 url: server.url,
148 path, 143 path,
149 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 144 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
150 }) 145 })
151 }) 146 })
152 147
@@ -155,7 +150,7 @@ describe('Test user notifications API validators', function () {
155 url: server.url, 150 url: server.url,
156 path, 151 path,
157 token: server.accessToken, 152 token: server.accessToken,
158 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 153 expectedStatus: HttpStatusCode.NO_CONTENT_204
159 }) 154 })
160 }) 155 })
161 }) 156 })
@@ -187,32 +182,32 @@ describe('Test user notifications API validators', function () {
187 path, 182 path,
188 token: server.accessToken, 183 token: server.accessToken,
189 fields: { newVideoFromSubscription: UserNotificationSettingValue.WEB }, 184 fields: { newVideoFromSubscription: UserNotificationSettingValue.WEB },
190 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 185 expectedStatus: HttpStatusCode.BAD_REQUEST_400
191 }) 186 })
192 }) 187 })
193 188
194 it('Should fail with incorrect field values', async function () { 189 it('Should fail with incorrect field values', async function () {
195 { 190 {
196 const fields = immutableAssign(correctFields, { newCommentOnMyVideo: 15 }) 191 const fields = { ...correctFields, newCommentOnMyVideo: 15 }
197 192
198 await makePutBodyRequest({ 193 await makePutBodyRequest({
199 url: server.url, 194 url: server.url,
200 path, 195 path,
201 token: server.accessToken, 196 token: server.accessToken,
202 fields, 197 fields,
203 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 198 expectedStatus: HttpStatusCode.BAD_REQUEST_400
204 }) 199 })
205 } 200 }
206 201
207 { 202 {
208 const fields = immutableAssign(correctFields, { newCommentOnMyVideo: 'toto' }) 203 const fields = { ...correctFields, newCommentOnMyVideo: 'toto' }
209 204
210 await makePutBodyRequest({ 205 await makePutBodyRequest({
211 url: server.url, 206 url: server.url,
212 path, 207 path,
213 fields, 208 fields,
214 token: server.accessToken, 209 token: server.accessToken,
215 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 210 expectedStatus: HttpStatusCode.BAD_REQUEST_400
216 }) 211 })
217 } 212 }
218 }) 213 })
@@ -222,7 +217,7 @@ describe('Test user notifications API validators', function () {
222 url: server.url, 217 url: server.url,
223 path, 218 path,
224 fields: correctFields, 219 fields: correctFields,
225 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 220 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
226 }) 221 })
227 }) 222 })
228 223
@@ -232,7 +227,7 @@ describe('Test user notifications API validators', function () {
232 path, 227 path,
233 token: server.accessToken, 228 token: server.accessToken,
234 fields: correctFields, 229 fields: correctFields,
235 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 230 expectedStatus: HttpStatusCode.NO_CONTENT_204
236 }) 231 })
237 }) 232 })
238 }) 233 })
diff --git a/server/tests/api/check-params/user-subscriptions.ts b/server/tests/api/check-params/user-subscriptions.ts
index 538201647..624069c80 100644
--- a/server/tests/api/check-params/user-subscriptions.ts
+++ b/server/tests/api/check-params/user-subscriptions.ts
@@ -1,30 +1,24 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4
5import { 4import {
5 checkBadCountPagination,
6 checkBadSortPagination,
7 checkBadStartPagination,
6 cleanupTests, 8 cleanupTests,
7 createUser, 9 createSingleServer,
8 flushAndRunServer,
9 makeDeleteRequest, 10 makeDeleteRequest,
10 makeGetRequest, 11 makeGetRequest,
11 makePostBodyRequest, 12 makePostBodyRequest,
12 ServerInfo, 13 PeerTubeServer,
13 setAccessTokensToServers, 14 setAccessTokensToServers,
14 userLogin 15 waitJobs
15} from '../../../../shared/extra-utils' 16} from '@shared/extra-utils'
16 17import { HttpStatusCode } from '@shared/models'
17import {
18 checkBadCountPagination,
19 checkBadSortPagination,
20 checkBadStartPagination
21} from '../../../../shared/extra-utils/requests/check-api-params'
22import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
23import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
24 18
25describe('Test user subscriptions API validators', function () { 19describe('Test user subscriptions API validators', function () {
26 const path = '/api/v1/users/me/subscriptions' 20 const path = '/api/v1/users/me/subscriptions'
27 let server: ServerInfo 21 let server: PeerTubeServer
28 let userAccessToken = '' 22 let userAccessToken = ''
29 23
30 // --------------------------------------------------------------- 24 // ---------------------------------------------------------------
@@ -32,7 +26,7 @@ describe('Test user subscriptions API validators', function () {
32 before(async function () { 26 before(async function () {
33 this.timeout(30000) 27 this.timeout(30000)
34 28
35 server = await flushAndRunServer(1) 29 server = await createSingleServer(1)
36 30
37 await setAccessTokensToServers([ server ]) 31 await setAccessTokensToServers([ server ])
38 32
@@ -40,8 +34,8 @@ describe('Test user subscriptions API validators', function () {
40 username: 'user1', 34 username: 'user1',
41 password: 'my super password' 35 password: 'my super password'
42 } 36 }
43 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 37 await server.users.create({ username: user.username, password: user.password })
44 userAccessToken = await userLogin(server, user) 38 userAccessToken = await server.login.getAccessToken(user)
45 }) 39 })
46 40
47 describe('When listing my subscriptions', function () { 41 describe('When listing my subscriptions', function () {
@@ -61,7 +55,7 @@ describe('Test user subscriptions API validators', function () {
61 await makeGetRequest({ 55 await makeGetRequest({
62 url: server.url, 56 url: server.url,
63 path, 57 path,
64 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 58 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
65 }) 59 })
66 }) 60 })
67 61
@@ -70,7 +64,7 @@ describe('Test user subscriptions API validators', function () {
70 url: server.url, 64 url: server.url,
71 path, 65 path,
72 token: userAccessToken, 66 token: userAccessToken,
73 statusCodeExpected: HttpStatusCode.OK_200 67 expectedStatus: HttpStatusCode.OK_200
74 }) 68 })
75 }) 69 })
76 }) 70 })
@@ -94,7 +88,7 @@ describe('Test user subscriptions API validators', function () {
94 await makeGetRequest({ 88 await makeGetRequest({
95 url: server.url, 89 url: server.url,
96 path, 90 path,
97 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 91 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
98 }) 92 })
99 }) 93 })
100 94
@@ -103,7 +97,7 @@ describe('Test user subscriptions API validators', function () {
103 url: server.url, 97 url: server.url,
104 path, 98 path,
105 token: userAccessToken, 99 token: userAccessToken,
106 statusCodeExpected: HttpStatusCode.OK_200 100 expectedStatus: HttpStatusCode.OK_200
107 }) 101 })
108 }) 102 })
109 }) 103 })
@@ -114,7 +108,7 @@ describe('Test user subscriptions API validators', function () {
114 url: server.url, 108 url: server.url,
115 path, 109 path,
116 fields: { uri: 'user1_channel@localhost:' + server.port }, 110 fields: { uri: 'user1_channel@localhost:' + server.port },
117 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 111 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
118 }) 112 })
119 }) 113 })
120 114
@@ -124,7 +118,7 @@ describe('Test user subscriptions API validators', function () {
124 path, 118 path,
125 token: server.accessToken, 119 token: server.accessToken,
126 fields: { uri: 'root' }, 120 fields: { uri: 'root' },
127 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 121 expectedStatus: HttpStatusCode.BAD_REQUEST_400
128 }) 122 })
129 123
130 await makePostBodyRequest({ 124 await makePostBodyRequest({
@@ -132,7 +126,7 @@ describe('Test user subscriptions API validators', function () {
132 path, 126 path,
133 token: server.accessToken, 127 token: server.accessToken,
134 fields: { uri: 'root@' }, 128 fields: { uri: 'root@' },
135 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 129 expectedStatus: HttpStatusCode.BAD_REQUEST_400
136 }) 130 })
137 131
138 await makePostBodyRequest({ 132 await makePostBodyRequest({
@@ -140,7 +134,7 @@ describe('Test user subscriptions API validators', function () {
140 path, 134 path,
141 token: server.accessToken, 135 token: server.accessToken,
142 fields: { uri: 'root@hello@' }, 136 fields: { uri: 'root@hello@' },
143 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 137 expectedStatus: HttpStatusCode.BAD_REQUEST_400
144 }) 138 })
145 }) 139 })
146 140
@@ -152,7 +146,7 @@ describe('Test user subscriptions API validators', function () {
152 path, 146 path,
153 token: server.accessToken, 147 token: server.accessToken,
154 fields: { uri: 'user1_channel@localhost:' + server.port }, 148 fields: { uri: 'user1_channel@localhost:' + server.port },
155 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 149 expectedStatus: HttpStatusCode.NO_CONTENT_204
156 }) 150 })
157 151
158 await waitJobs([ server ]) 152 await waitJobs([ server ])
@@ -164,7 +158,7 @@ describe('Test user subscriptions API validators', function () {
164 await makeGetRequest({ 158 await makeGetRequest({
165 url: server.url, 159 url: server.url,
166 path: path + '/user1_channel@localhost:' + server.port, 160 path: path + '/user1_channel@localhost:' + server.port,
167 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 161 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
168 }) 162 })
169 }) 163 })
170 164
@@ -173,21 +167,21 @@ describe('Test user subscriptions API validators', function () {
173 url: server.url, 167 url: server.url,
174 path: path + '/root', 168 path: path + '/root',
175 token: server.accessToken, 169 token: server.accessToken,
176 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 170 expectedStatus: HttpStatusCode.BAD_REQUEST_400
177 }) 171 })
178 172
179 await makeGetRequest({ 173 await makeGetRequest({
180 url: server.url, 174 url: server.url,
181 path: path + '/root@', 175 path: path + '/root@',
182 token: server.accessToken, 176 token: server.accessToken,
183 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 177 expectedStatus: HttpStatusCode.BAD_REQUEST_400
184 }) 178 })
185 179
186 await makeGetRequest({ 180 await makeGetRequest({
187 url: server.url, 181 url: server.url,
188 path: path + '/root@hello@', 182 path: path + '/root@hello@',
189 token: server.accessToken, 183 token: server.accessToken,
190 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 184 expectedStatus: HttpStatusCode.BAD_REQUEST_400
191 }) 185 })
192 }) 186 })
193 187
@@ -196,7 +190,7 @@ describe('Test user subscriptions API validators', function () {
196 url: server.url, 190 url: server.url,
197 path: path + '/root1@localhost:' + server.port, 191 path: path + '/root1@localhost:' + server.port,
198 token: server.accessToken, 192 token: server.accessToken,
199 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 193 expectedStatus: HttpStatusCode.NOT_FOUND_404
200 }) 194 })
201 }) 195 })
202 196
@@ -205,7 +199,7 @@ describe('Test user subscriptions API validators', function () {
205 url: server.url, 199 url: server.url,
206 path: path + '/user1_channel@localhost:' + server.port, 200 path: path + '/user1_channel@localhost:' + server.port,
207 token: server.accessToken, 201 token: server.accessToken,
208 statusCodeExpected: HttpStatusCode.OK_200 202 expectedStatus: HttpStatusCode.OK_200
209 }) 203 })
210 }) 204 })
211 }) 205 })
@@ -217,7 +211,7 @@ describe('Test user subscriptions API validators', function () {
217 await makeGetRequest({ 211 await makeGetRequest({
218 url: server.url, 212 url: server.url,
219 path: existPath, 213 path: existPath,
220 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 214 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
221 }) 215 })
222 }) 216 })
223 217
@@ -227,7 +221,7 @@ describe('Test user subscriptions API validators', function () {
227 path: existPath, 221 path: existPath,
228 query: { uris: 'toto' }, 222 query: { uris: 'toto' },
229 token: server.accessToken, 223 token: server.accessToken,
230 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 224 expectedStatus: HttpStatusCode.BAD_REQUEST_400
231 }) 225 })
232 226
233 await makeGetRequest({ 227 await makeGetRequest({
@@ -235,7 +229,7 @@ describe('Test user subscriptions API validators', function () {
235 path: existPath, 229 path: existPath,
236 query: { 'uris[]': 1 }, 230 query: { 'uris[]': 1 },
237 token: server.accessToken, 231 token: server.accessToken,
238 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 232 expectedStatus: HttpStatusCode.BAD_REQUEST_400
239 }) 233 })
240 }) 234 })
241 235
@@ -245,7 +239,7 @@ describe('Test user subscriptions API validators', function () {
245 path: existPath, 239 path: existPath,
246 query: { 'uris[]': 'coucou@localhost:' + server.port }, 240 query: { 'uris[]': 'coucou@localhost:' + server.port },
247 token: server.accessToken, 241 token: server.accessToken,
248 statusCodeExpected: HttpStatusCode.OK_200 242 expectedStatus: HttpStatusCode.OK_200
249 }) 243 })
250 }) 244 })
251 }) 245 })
@@ -255,7 +249,7 @@ describe('Test user subscriptions API validators', function () {
255 await makeDeleteRequest({ 249 await makeDeleteRequest({
256 url: server.url, 250 url: server.url,
257 path: path + '/user1_channel@localhost:' + server.port, 251 path: path + '/user1_channel@localhost:' + server.port,
258 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 252 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
259 }) 253 })
260 }) 254 })
261 255
@@ -264,21 +258,21 @@ describe('Test user subscriptions API validators', function () {
264 url: server.url, 258 url: server.url,
265 path: path + '/root', 259 path: path + '/root',
266 token: server.accessToken, 260 token: server.accessToken,
267 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 261 expectedStatus: HttpStatusCode.BAD_REQUEST_400
268 }) 262 })
269 263
270 await makeDeleteRequest({ 264 await makeDeleteRequest({
271 url: server.url, 265 url: server.url,
272 path: path + '/root@', 266 path: path + '/root@',
273 token: server.accessToken, 267 token: server.accessToken,
274 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 268 expectedStatus: HttpStatusCode.BAD_REQUEST_400
275 }) 269 })
276 270
277 await makeDeleteRequest({ 271 await makeDeleteRequest({
278 url: server.url, 272 url: server.url,
279 path: path + '/root@hello@', 273 path: path + '/root@hello@',
280 token: server.accessToken, 274 token: server.accessToken,
281 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 275 expectedStatus: HttpStatusCode.BAD_REQUEST_400
282 }) 276 })
283 }) 277 })
284 278
@@ -287,7 +281,7 @@ describe('Test user subscriptions API validators', function () {
287 url: server.url, 281 url: server.url,
288 path: path + '/root1@localhost:' + server.port, 282 path: path + '/root1@localhost:' + server.port,
289 token: server.accessToken, 283 token: server.accessToken,
290 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 284 expectedStatus: HttpStatusCode.NOT_FOUND_404
291 }) 285 })
292 }) 286 })
293 287
@@ -296,7 +290,7 @@ describe('Test user subscriptions API validators', function () {
296 url: server.url, 290 url: server.url,
297 path: path + '/user1_channel@localhost:' + server.port, 291 path: path + '/user1_channel@localhost:' + server.port,
298 token: server.accessToken, 292 token: server.accessToken,
299 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 293 expectedStatus: HttpStatusCode.NO_CONTENT_204
300 }) 294 })
301 }) 295 })
302 }) 296 })
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts
index 70a872ce5..9d8f933db 100644
--- a/server/tests/api/check-params/users.ts
+++ b/server/tests/api/check-params/users.ts
@@ -2,43 +2,24 @@
2 2
3import 'mocha' 3import 'mocha'
4import { omit } from 'lodash' 4import { omit } from 'lodash'
5import { User, UserRole, VideoCreateResult } from '../../../../shared'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { 5import {
8 addVideoChannel,
9 blockUser,
10 buildAbsoluteFixturePath, 6 buildAbsoluteFixturePath,
7 checkBadCountPagination,
8 checkBadSortPagination,
9 checkBadStartPagination,
11 cleanupTests, 10 cleanupTests,
12 createUser, 11 createSingleServer,
13 deleteMe,
14 flushAndRunServer,
15 getMyUserInformation,
16 getMyUserVideoRating,
17 getUserScopedTokens,
18 getUsersList,
19 immutableAssign,
20 killallServers, 12 killallServers,
21 makeGetRequest, 13 makeGetRequest,
22 makePostBodyRequest, 14 makePostBodyRequest,
23 makePutBodyRequest, 15 makePutBodyRequest,
24 makeUploadRequest, 16 makeUploadRequest,
25 registerUser, 17 MockSmtpServer,
26 removeUser, 18 PeerTubeServer,
27 renewUserScopedTokens,
28 reRunServer,
29 ServerInfo,
30 setAccessTokensToServers, 19 setAccessTokensToServers,
31 unblockUser, 20 UsersCommand
32 uploadVideo, 21} from '@shared/extra-utils'
33 userLogin 22import { HttpStatusCode, UserAdminFlag, UserRole, VideoCreateResult } from '@shared/models'
34} from '../../../../shared/extra-utils'
35import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
36import {
37 checkBadCountPagination,
38 checkBadSortPagination,
39 checkBadStartPagination
40} from '../../../../shared/extra-utils/requests/check-api-params'
41import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
42 23
43describe('Test users API validators', function () { 24describe('Test users API validators', function () {
44 const path = '/api/v1/users/' 25 const path = '/api/v1/users/'
@@ -46,10 +27,10 @@ describe('Test users API validators', function () {
46 let rootId: number 27 let rootId: number
47 let moderatorId: number 28 let moderatorId: number
48 let video: VideoCreateResult 29 let video: VideoCreateResult
49 let server: ServerInfo 30 let server: PeerTubeServer
50 let serverWithRegistrationDisabled: ServerInfo 31 let serverWithRegistrationDisabled: PeerTubeServer
51 let userAccessToken = '' 32 let userToken = ''
52 let moderatorAccessToken = '' 33 let moderatorToken = ''
53 let emailPort: number 34 let emailPort: number
54 let overrideConfig: Object 35 let overrideConfig: Object
55 36
@@ -65,8 +46,8 @@ describe('Test users API validators', function () {
65 46
66 { 47 {
67 const res = await Promise.all([ 48 const res = await Promise.all([
68 flushAndRunServer(1, overrideConfig), 49 createSingleServer(1, overrideConfig),
69 flushAndRunServer(2) 50 createSingleServer(2)
70 ]) 51 ])
71 52
72 server = res[0] 53 server = res[0]
@@ -76,66 +57,31 @@ describe('Test users API validators', function () {
76 } 57 }
77 58
78 { 59 {
79 const user = { 60 const user = { username: 'user1' }
80 username: 'user1', 61 await server.users.create({ ...user })
81 password: 'my super password' 62 userToken = await server.login.getAccessToken(user)
82 }
83
84 const videoQuota = 42000000
85 await createUser({
86 url: server.url,
87 accessToken: server.accessToken,
88 username: user.username,
89 password: user.password,
90 videoQuota: videoQuota
91 })
92 userAccessToken = await userLogin(server, user)
93 } 63 }
94 64
95 { 65 {
96 const moderator = { 66 const moderator = { username: 'moderator1' }
97 username: 'moderator1', 67 await server.users.create({ ...moderator, role: UserRole.MODERATOR })
98 password: 'super password' 68 moderatorToken = await server.login.getAccessToken(moderator)
99 }
100
101 await createUser({
102 url: server.url,
103 accessToken: server.accessToken,
104 username: moderator.username,
105 password: moderator.password,
106 role: UserRole.MODERATOR
107 })
108
109 moderatorAccessToken = await userLogin(server, moderator)
110 } 69 }
111 70
112 { 71 {
113 const moderator = { 72 const moderator = { username: 'moderator2' }
114 username: 'moderator2', 73 await server.users.create({ ...moderator, role: UserRole.MODERATOR })
115 password: 'super password'
116 }
117
118 await createUser({
119 url: server.url,
120 accessToken: server.accessToken,
121 username: moderator.username,
122 password: moderator.password,
123 role: UserRole.MODERATOR
124 })
125 } 74 }
126 75
127 { 76 {
128 const res = await uploadVideo(server.url, server.accessToken, {}) 77 video = await server.videos.upload()
129 video = res.body.video
130 } 78 }
131 79
132 { 80 {
133 const res = await getUsersList(server.url, server.accessToken) 81 const { data } = await server.users.list()
134 const users: User[] = res.body.data 82 userId = data.find(u => u.username === 'user1').id
135 83 rootId = data.find(u => u.username === 'root').id
136 userId = users.find(u => u.username === 'user1').id 84 moderatorId = data.find(u => u.username === 'moderator2').id
137 rootId = users.find(u => u.username === 'root').id
138 moderatorId = users.find(u => u.username === 'moderator2').id
139 } 85 }
140 }) 86 })
141 87
@@ -156,7 +102,7 @@ describe('Test users API validators', function () {
156 await makeGetRequest({ 102 await makeGetRequest({
157 url: server.url, 103 url: server.url,
158 path, 104 path,
159 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 105 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
160 }) 106 })
161 }) 107 })
162 108
@@ -164,8 +110,8 @@ describe('Test users API validators', function () {
164 await makeGetRequest({ 110 await makeGetRequest({
165 url: server.url, 111 url: server.url,
166 path, 112 path,
167 token: userAccessToken, 113 token: userToken,
168 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 114 expectedStatus: HttpStatusCode.FORBIDDEN_403
169 }) 115 })
170 }) 116 })
171 }) 117 })
@@ -182,25 +128,25 @@ describe('Test users API validators', function () {
182 } 128 }
183 129
184 it('Should fail with a too small username', async function () { 130 it('Should fail with a too small username', async function () {
185 const fields = immutableAssign(baseCorrectParams, { username: '' }) 131 const fields = { ...baseCorrectParams, username: '' }
186 132
187 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 133 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
188 }) 134 })
189 135
190 it('Should fail with a too long username', async function () { 136 it('Should fail with a too long username', async function () {
191 const fields = immutableAssign(baseCorrectParams, { username: 'super'.repeat(50) }) 137 const fields = { ...baseCorrectParams, username: 'super'.repeat(50) }
192 138
193 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 139 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
194 }) 140 })
195 141
196 it('Should fail with a not lowercase username', async function () { 142 it('Should fail with a not lowercase username', async function () {
197 const fields = immutableAssign(baseCorrectParams, { username: 'Toto' }) 143 const fields = { ...baseCorrectParams, username: 'Toto' }
198 144
199 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 145 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
200 }) 146 })
201 147
202 it('Should fail with an incorrect username', async function () { 148 it('Should fail with an incorrect username', async function () {
203 const fields = immutableAssign(baseCorrectParams, { username: 'my username' }) 149 const fields = { ...baseCorrectParams, username: 'my username' }
204 150
205 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 151 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
206 }) 152 })
@@ -212,25 +158,25 @@ describe('Test users API validators', function () {
212 }) 158 })
213 159
214 it('Should fail with an invalid email', async function () { 160 it('Should fail with an invalid email', async function () {
215 const fields = immutableAssign(baseCorrectParams, { email: 'test_example.com' }) 161 const fields = { ...baseCorrectParams, email: 'test_example.com' }
216 162
217 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 163 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
218 }) 164 })
219 165
220 it('Should fail with a too small password', async function () { 166 it('Should fail with a too small password', async function () {
221 const fields = immutableAssign(baseCorrectParams, { password: 'bla' }) 167 const fields = { ...baseCorrectParams, password: 'bla' }
222 168
223 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 169 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
224 }) 170 })
225 171
226 it('Should fail with a too long password', async function () { 172 it('Should fail with a too long password', async function () {
227 const fields = immutableAssign(baseCorrectParams, { password: 'super'.repeat(61) }) 173 const fields = { ...baseCorrectParams, password: 'super'.repeat(61) }
228 174
229 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 175 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
230 }) 176 })
231 177
232 it('Should fail with empty password and no smtp configured', async function () { 178 it('Should fail with empty password and no smtp configured', async function () {
233 const fields = immutableAssign(baseCorrectParams, { password: '' }) 179 const fields = { ...baseCorrectParams, password: '' }
234 180
235 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 181 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
236 }) 182 })
@@ -238,33 +184,37 @@ describe('Test users API validators', function () {
238 it('Should succeed with no password on a server with smtp enabled', async function () { 184 it('Should succeed with no password on a server with smtp enabled', async function () {
239 this.timeout(20000) 185 this.timeout(20000)
240 186
241 killallServers([ server ]) 187 await killallServers([ server ])
188
189 const config = {
190 ...overrideConfig,
242 191
243 const config = immutableAssign(overrideConfig, {
244 smtp: { 192 smtp: {
245 hostname: 'localhost', 193 hostname: 'localhost',
246 port: emailPort 194 port: emailPort
247 } 195 }
248 }) 196 }
249 await reRunServer(server, config) 197 await server.run(config)
198
199 const fields = {
200 ...baseCorrectParams,
250 201
251 const fields = immutableAssign(baseCorrectParams, {
252 password: '', 202 password: '',
253 username: 'create_password', 203 username: 'create_password',
254 email: 'create_password@example.com' 204 email: 'create_password@example.com'
255 }) 205 }
256 206
257 await makePostBodyRequest({ 207 await makePostBodyRequest({
258 url: server.url, 208 url: server.url,
259 path: path, 209 path: path,
260 token: server.accessToken, 210 token: server.accessToken,
261 fields, 211 fields,
262 statusCodeExpected: HttpStatusCode.OK_200 212 expectedStatus: HttpStatusCode.OK_200
263 }) 213 })
264 }) 214 })
265 215
266 it('Should fail with invalid admin flags', async function () { 216 it('Should fail with invalid admin flags', async function () {
267 const fields = immutableAssign(baseCorrectParams, { adminFlags: 'toto' }) 217 const fields = { ...baseCorrectParams, adminFlags: 'toto' }
268 218
269 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 219 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
270 }) 220 })
@@ -275,31 +225,31 @@ describe('Test users API validators', function () {
275 path, 225 path,
276 token: 'super token', 226 token: 'super token',
277 fields: baseCorrectParams, 227 fields: baseCorrectParams,
278 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 228 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
279 }) 229 })
280 }) 230 })
281 231
282 it('Should fail if we add a user with the same username', async function () { 232 it('Should fail if we add a user with the same username', async function () {
283 const fields = immutableAssign(baseCorrectParams, { username: 'user1' }) 233 const fields = { ...baseCorrectParams, username: 'user1' }
284 234
285 await makePostBodyRequest({ 235 await makePostBodyRequest({
286 url: server.url, 236 url: server.url,
287 path, 237 path,
288 token: server.accessToken, 238 token: server.accessToken,
289 fields, 239 fields,
290 statusCodeExpected: HttpStatusCode.CONFLICT_409 240 expectedStatus: HttpStatusCode.CONFLICT_409
291 }) 241 })
292 }) 242 })
293 243
294 it('Should fail if we add a user with the same email', async function () { 244 it('Should fail if we add a user with the same email', async function () {
295 const fields = immutableAssign(baseCorrectParams, { email: 'user1@example.com' }) 245 const fields = { ...baseCorrectParams, email: 'user1@example.com' }
296 246
297 await makePostBodyRequest({ 247 await makePostBodyRequest({
298 url: server.url, 248 url: server.url,
299 path, 249 path,
300 token: server.accessToken, 250 token: server.accessToken,
301 fields, 251 fields,
302 statusCodeExpected: HttpStatusCode.CONFLICT_409 252 expectedStatus: HttpStatusCode.CONFLICT_409
303 }) 253 })
304 }) 254 })
305 255
@@ -316,13 +266,13 @@ describe('Test users API validators', function () {
316 }) 266 })
317 267
318 it('Should fail with an invalid videoQuota', async function () { 268 it('Should fail with an invalid videoQuota', async function () {
319 const fields = immutableAssign(baseCorrectParams, { videoQuota: -5 }) 269 const fields = { ...baseCorrectParams, videoQuota: -5 }
320 270
321 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 271 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
322 }) 272 })
323 273
324 it('Should fail with an invalid videoQuotaDaily', async function () { 274 it('Should fail with an invalid videoQuotaDaily', async function () {
325 const fields = immutableAssign(baseCorrectParams, { videoQuotaDaily: -7 }) 275 const fields = { ...baseCorrectParams, videoQuotaDaily: -7 }
326 276
327 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 277 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
328 }) 278 })
@@ -334,46 +284,46 @@ describe('Test users API validators', function () {
334 }) 284 })
335 285
336 it('Should fail with an invalid user role', async function () { 286 it('Should fail with an invalid user role', async function () {
337 const fields = immutableAssign(baseCorrectParams, { role: 88989 }) 287 const fields = { ...baseCorrectParams, role: 88989 }
338 288
339 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 289 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
340 }) 290 })
341 291
342 it('Should fail with a "peertube" username', async function () { 292 it('Should fail with a "peertube" username', async function () {
343 const fields = immutableAssign(baseCorrectParams, { username: 'peertube' }) 293 const fields = { ...baseCorrectParams, username: 'peertube' }
344 294
345 await makePostBodyRequest({ 295 await makePostBodyRequest({
346 url: server.url, 296 url: server.url,
347 path, 297 path,
348 token: server.accessToken, 298 token: server.accessToken,
349 fields, 299 fields,
350 statusCodeExpected: HttpStatusCode.CONFLICT_409 300 expectedStatus: HttpStatusCode.CONFLICT_409
351 }) 301 })
352 }) 302 })
353 303
354 it('Should fail to create a moderator or an admin with a moderator', async function () { 304 it('Should fail to create a moderator or an admin with a moderator', async function () {
355 for (const role of [ UserRole.MODERATOR, UserRole.ADMINISTRATOR ]) { 305 for (const role of [ UserRole.MODERATOR, UserRole.ADMINISTRATOR ]) {
356 const fields = immutableAssign(baseCorrectParams, { role }) 306 const fields = { ...baseCorrectParams, role }
357 307
358 await makePostBodyRequest({ 308 await makePostBodyRequest({
359 url: server.url, 309 url: server.url,
360 path, 310 path,
361 token: moderatorAccessToken, 311 token: moderatorToken,
362 fields, 312 fields,
363 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 313 expectedStatus: HttpStatusCode.FORBIDDEN_403
364 }) 314 })
365 } 315 }
366 }) 316 })
367 317
368 it('Should succeed to create a user with a moderator', async function () { 318 it('Should succeed to create a user with a moderator', async function () {
369 const fields = immutableAssign(baseCorrectParams, { username: 'a4656', email: 'a4656@example.com', role: UserRole.USER }) 319 const fields = { ...baseCorrectParams, username: 'a4656', email: 'a4656@example.com', role: UserRole.USER }
370 320
371 await makePostBodyRequest({ 321 await makePostBodyRequest({
372 url: server.url, 322 url: server.url,
373 path, 323 path,
374 token: moderatorAccessToken, 324 token: moderatorToken,
375 fields, 325 fields,
376 statusCodeExpected: HttpStatusCode.OK_200 326 expectedStatus: HttpStatusCode.OK_200
377 }) 327 })
378 }) 328 })
379 329
@@ -383,16 +333,13 @@ describe('Test users API validators', function () {
383 path, 333 path,
384 token: server.accessToken, 334 token: server.accessToken,
385 fields: baseCorrectParams, 335 fields: baseCorrectParams,
386 statusCodeExpected: HttpStatusCode.OK_200 336 expectedStatus: HttpStatusCode.OK_200
387 }) 337 })
388 }) 338 })
389 339
390 it('Should fail with a non admin user', async function () { 340 it('Should fail with a non admin user', async function () {
391 const user = { 341 const user = { username: 'user1' }
392 username: 'user1', 342 userToken = await server.login.getAccessToken(user)
393 password: 'my super password'
394 }
395 userAccessToken = await userLogin(server, user)
396 343
397 const fields = { 344 const fields = {
398 username: 'user3', 345 username: 'user3',
@@ -400,11 +347,12 @@ describe('Test users API validators', function () {
400 password: 'my super password', 347 password: 'my super password',
401 videoQuota: 42000000 348 videoQuota: 42000000
402 } 349 }
403 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 350 await makePostBodyRequest({ url: server.url, path, token: userToken, fields, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
404 }) 351 })
405 }) 352 })
406 353
407 describe('When updating my account', function () { 354 describe('When updating my account', function () {
355
408 it('Should fail with an invalid email attribute', async function () { 356 it('Should fail with an invalid email attribute', async function () {
409 const fields = { 357 const fields = {
410 email: 'blabla' 358 email: 'blabla'
@@ -415,29 +363,29 @@ describe('Test users API validators', function () {
415 363
416 it('Should fail with a too small password', async function () { 364 it('Should fail with a too small password', async function () {
417 const fields = { 365 const fields = {
418 currentPassword: 'my super password', 366 currentPassword: 'password',
419 password: 'bla' 367 password: 'bla'
420 } 368 }
421 369
422 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 370 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
423 }) 371 })
424 372
425 it('Should fail with a too long password', async function () { 373 it('Should fail with a too long password', async function () {
426 const fields = { 374 const fields = {
427 currentPassword: 'my super password', 375 currentPassword: 'password',
428 password: 'super'.repeat(61) 376 password: 'super'.repeat(61)
429 } 377 }
430 378
431 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 379 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
432 }) 380 })
433 381
434 it('Should fail without the current password', async function () { 382 it('Should fail without the current password', async function () {
435 const fields = { 383 const fields = {
436 currentPassword: 'my super password', 384 currentPassword: 'password',
437 password: 'super'.repeat(61) 385 password: 'super'.repeat(61)
438 } 386 }
439 387
440 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 388 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
441 }) 389 })
442 390
443 it('Should fail with an invalid current password', async function () { 391 it('Should fail with an invalid current password', async function () {
@@ -449,9 +397,9 @@ describe('Test users API validators', function () {
449 await makePutBodyRequest({ 397 await makePutBodyRequest({
450 url: server.url, 398 url: server.url,
451 path: path + 'me', 399 path: path + 'me',
452 token: userAccessToken, 400 token: userToken,
453 fields, 401 fields,
454 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 402 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
455 }) 403 })
456 }) 404 })
457 405
@@ -460,7 +408,7 @@ describe('Test users API validators', function () {
460 nsfwPolicy: 'hello' 408 nsfwPolicy: 'hello'
461 } 409 }
462 410
463 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 411 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
464 }) 412 })
465 413
466 it('Should fail with an invalid autoPlayVideo attribute', async function () { 414 it('Should fail with an invalid autoPlayVideo attribute', async function () {
@@ -468,7 +416,7 @@ describe('Test users API validators', function () {
468 autoPlayVideo: -1 416 autoPlayVideo: -1
469 } 417 }
470 418
471 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 419 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
472 }) 420 })
473 421
474 it('Should fail with an invalid autoPlayNextVideo attribute', async function () { 422 it('Should fail with an invalid autoPlayNextVideo attribute', async function () {
@@ -476,7 +424,7 @@ describe('Test users API validators', function () {
476 autoPlayNextVideo: -1 424 autoPlayNextVideo: -1
477 } 425 }
478 426
479 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 427 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
480 }) 428 })
481 429
482 it('Should fail with an invalid videosHistoryEnabled attribute', async function () { 430 it('Should fail with an invalid videosHistoryEnabled attribute', async function () {
@@ -484,12 +432,12 @@ describe('Test users API validators', function () {
484 videosHistoryEnabled: -1 432 videosHistoryEnabled: -1
485 } 433 }
486 434
487 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 435 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
488 }) 436 })
489 437
490 it('Should fail with an non authenticated user', async function () { 438 it('Should fail with an non authenticated user', async function () {
491 const fields = { 439 const fields = {
492 currentPassword: 'my super password', 440 currentPassword: 'password',
493 password: 'my super password' 441 password: 'my super password'
494 } 442 }
495 443
@@ -498,7 +446,7 @@ describe('Test users API validators', function () {
498 path: path + 'me', 446 path: path + 'me',
499 token: 'super token', 447 token: 'super token',
500 fields, 448 fields,
501 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 449 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
502 }) 450 })
503 }) 451 })
504 452
@@ -507,7 +455,7 @@ describe('Test users API validators', function () {
507 description: 'super'.repeat(201) 455 description: 'super'.repeat(201)
508 } 456 }
509 457
510 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 458 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
511 }) 459 })
512 460
513 it('Should fail with an invalid videoLanguages attribute', async function () { 461 it('Should fail with an invalid videoLanguages attribute', async function () {
@@ -516,7 +464,7 @@ describe('Test users API validators', function () {
516 videoLanguages: 'toto' 464 videoLanguages: 'toto'
517 } 465 }
518 466
519 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 467 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
520 } 468 }
521 469
522 { 470 {
@@ -529,18 +477,18 @@ describe('Test users API validators', function () {
529 videoLanguages: languages 477 videoLanguages: languages
530 } 478 }
531 479
532 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 480 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
533 } 481 }
534 }) 482 })
535 483
536 it('Should fail with an invalid theme', async function () { 484 it('Should fail with an invalid theme', async function () {
537 const fields = { theme: 'invalid' } 485 const fields = { theme: 'invalid' }
538 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 486 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
539 }) 487 })
540 488
541 it('Should fail with an unknown theme', async function () { 489 it('Should fail with an unknown theme', async function () {
542 const fields = { theme: 'peertube-theme-unknown' } 490 const fields = { theme: 'peertube-theme-unknown' }
543 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 491 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
544 }) 492 })
545 493
546 it('Should fail with an invalid noInstanceConfigWarningModal attribute', async function () { 494 it('Should fail with an invalid noInstanceConfigWarningModal attribute', async function () {
@@ -548,7 +496,7 @@ describe('Test users API validators', function () {
548 noInstanceConfigWarningModal: -1 496 noInstanceConfigWarningModal: -1
549 } 497 }
550 498
551 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 499 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
552 }) 500 })
553 501
554 it('Should fail with an invalid noWelcomeModal attribute', async function () { 502 it('Should fail with an invalid noWelcomeModal attribute', async function () {
@@ -556,12 +504,12 @@ describe('Test users API validators', function () {
556 noWelcomeModal: -1 504 noWelcomeModal: -1
557 } 505 }
558 506
559 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 507 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
560 }) 508 })
561 509
562 it('Should succeed to change password with the correct params', async function () { 510 it('Should succeed to change password with the correct params', async function () {
563 const fields = { 511 const fields = {
564 currentPassword: 'my super password', 512 currentPassword: 'password',
565 password: 'my super password', 513 password: 'my super password',
566 nsfwPolicy: 'blur', 514 nsfwPolicy: 'blur',
567 autoPlayVideo: false, 515 autoPlayVideo: false,
@@ -574,9 +522,9 @@ describe('Test users API validators', function () {
574 await makePutBodyRequest({ 522 await makePutBodyRequest({
575 url: server.url, 523 url: server.url,
576 path: path + 'me', 524 path: path + 'me',
577 token: userAccessToken, 525 token: userToken,
578 fields, 526 fields,
579 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 527 expectedStatus: HttpStatusCode.NO_CONTENT_204
580 }) 528 })
581 }) 529 })
582 530
@@ -589,9 +537,9 @@ describe('Test users API validators', function () {
589 await makePutBodyRequest({ 537 await makePutBodyRequest({
590 url: server.url, 538 url: server.url,
591 path: path + 'me', 539 path: path + 'me',
592 token: userAccessToken, 540 token: userToken,
593 fields, 541 fields,
594 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 542 expectedStatus: HttpStatusCode.NO_CONTENT_204
595 }) 543 })
596 }) 544 })
597 }) 545 })
@@ -623,7 +571,7 @@ describe('Test users API validators', function () {
623 path: path + '/me/avatar/pick', 571 path: path + '/me/avatar/pick',
624 fields, 572 fields,
625 attaches, 573 attaches,
626 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 574 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
627 }) 575 })
628 }) 576 })
629 577
@@ -638,7 +586,7 @@ describe('Test users API validators', function () {
638 token: server.accessToken, 586 token: server.accessToken,
639 fields, 587 fields,
640 attaches, 588 attaches,
641 statusCodeExpected: HttpStatusCode.OK_200 589 expectedStatus: HttpStatusCode.OK_200
642 }) 590 })
643 }) 591 })
644 }) 592 })
@@ -646,28 +594,28 @@ describe('Test users API validators', function () {
646 describe('When managing my scoped tokens', function () { 594 describe('When managing my scoped tokens', function () {
647 595
648 it('Should fail to get my scoped tokens with an non authenticated user', async function () { 596 it('Should fail to get my scoped tokens with an non authenticated user', async function () {
649 await getUserScopedTokens(server.url, null, HttpStatusCode.UNAUTHORIZED_401) 597 await server.users.getMyScopedTokens({ token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
650 }) 598 })
651 599
652 it('Should fail to get my scoped tokens with a bad token', async function () { 600 it('Should fail to get my scoped tokens with a bad token', async function () {
653 await getUserScopedTokens(server.url, 'bad', HttpStatusCode.UNAUTHORIZED_401) 601 await server.users.getMyScopedTokens({ token: 'bad', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
654 602
655 }) 603 })
656 604
657 it('Should succeed to get my scoped tokens', async function () { 605 it('Should succeed to get my scoped tokens', async function () {
658 await getUserScopedTokens(server.url, server.accessToken) 606 await server.users.getMyScopedTokens()
659 }) 607 })
660 608
661 it('Should fail to renew my scoped tokens with an non authenticated user', async function () { 609 it('Should fail to renew my scoped tokens with an non authenticated user', async function () {
662 await renewUserScopedTokens(server.url, null, HttpStatusCode.UNAUTHORIZED_401) 610 await server.users.renewMyScopedTokens({ token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
663 }) 611 })
664 612
665 it('Should fail to renew my scoped tokens with a bad token', async function () { 613 it('Should fail to renew my scoped tokens with a bad token', async function () {
666 await renewUserScopedTokens(server.url, 'bad', HttpStatusCode.UNAUTHORIZED_401) 614 await server.users.renewMyScopedTokens({ token: 'bad', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
667 }) 615 })
668 616
669 it('Should succeed to renew my scoped tokens', async function () { 617 it('Should succeed to renew my scoped tokens', async function () {
670 await renewUserScopedTokens(server.url, server.accessToken) 618 await server.users.renewMyScopedTokens()
671 }) 619 })
672 }) 620 })
673 621
@@ -678,16 +626,16 @@ describe('Test users API validators', function () {
678 url: server.url, 626 url: server.url,
679 path: path + userId, 627 path: path + userId,
680 token: 'super token', 628 token: 'super token',
681 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 629 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
682 }) 630 })
683 }) 631 })
684 632
685 it('Should fail with a non admin user', async function () { 633 it('Should fail with a non admin user', async function () {
686 await makeGetRequest({ url: server.url, path, token: userAccessToken, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 634 await makeGetRequest({ url: server.url, path, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
687 }) 635 })
688 636
689 it('Should succeed with the correct params', async function () { 637 it('Should succeed with the correct params', async function () {
690 await makeGetRequest({ url: server.url, path: path + userId, token: server.accessToken, statusCodeExpected: HttpStatusCode.OK_200 }) 638 await makeGetRequest({ url: server.url, path: path + userId, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
691 }) 639 })
692 }) 640 })
693 641
@@ -727,7 +675,7 @@ describe('Test users API validators', function () {
727 675
728 it('Should fail with a too small password', async function () { 676 it('Should fail with a too small password', async function () {
729 const fields = { 677 const fields = {
730 currentPassword: 'my super password', 678 currentPassword: 'password',
731 password: 'bla' 679 password: 'bla'
732 } 680 }
733 681
@@ -736,7 +684,7 @@ describe('Test users API validators', function () {
736 684
737 it('Should fail with a too long password', async function () { 685 it('Should fail with a too long password', async function () {
738 const fields = { 686 const fields = {
739 currentPassword: 'my super password', 687 currentPassword: 'password',
740 password: 'super'.repeat(61) 688 password: 'super'.repeat(61)
741 } 689 }
742 690
@@ -753,7 +701,7 @@ describe('Test users API validators', function () {
753 path: path + userId, 701 path: path + userId,
754 token: 'super token', 702 token: 'super token',
755 fields, 703 fields,
756 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 704 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
757 }) 705 })
758 }) 706 })
759 707
@@ -779,9 +727,9 @@ describe('Test users API validators', function () {
779 await makePutBodyRequest({ 727 await makePutBodyRequest({
780 url: server.url, 728 url: server.url,
781 path: path + moderatorId, 729 path: path + moderatorId,
782 token: moderatorAccessToken, 730 token: moderatorToken,
783 fields, 731 fields,
784 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 732 expectedStatus: HttpStatusCode.FORBIDDEN_403
785 }) 733 })
786 }) 734 })
787 735
@@ -793,9 +741,9 @@ describe('Test users API validators', function () {
793 await makePutBodyRequest({ 741 await makePutBodyRequest({
794 url: server.url, 742 url: server.url,
795 path: path + userId, 743 path: path + userId,
796 token: moderatorAccessToken, 744 token: moderatorToken,
797 fields, 745 fields,
798 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 746 expectedStatus: HttpStatusCode.NO_CONTENT_204
799 }) 747 })
800 }) 748 })
801 749
@@ -812,38 +760,44 @@ describe('Test users API validators', function () {
812 path: path + userId, 760 path: path + userId,
813 token: server.accessToken, 761 token: server.accessToken,
814 fields, 762 fields,
815 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 763 expectedStatus: HttpStatusCode.NO_CONTENT_204
816 }) 764 })
817 }) 765 })
818 }) 766 })
819 767
820 describe('When getting my information', function () { 768 describe('When getting my information', function () {
821 it('Should fail with a non authenticated user', async function () { 769 it('Should fail with a non authenticated user', async function () {
822 await getMyUserInformation(server.url, 'fake_token', HttpStatusCode.UNAUTHORIZED_401) 770 await server.users.getMyInfo({ token: 'fake_token', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
823 }) 771 })
824 772
825 it('Should success with the correct parameters', async function () { 773 it('Should success with the correct parameters', async function () {
826 await getMyUserInformation(server.url, userAccessToken) 774 await server.users.getMyInfo({ token: userToken })
827 }) 775 })
828 }) 776 })
829 777
830 describe('When getting my video rating', function () { 778 describe('When getting my video rating', function () {
779 let command: UsersCommand
780
781 before(function () {
782 command = server.users
783 })
784
831 it('Should fail with a non authenticated user', async function () { 785 it('Should fail with a non authenticated user', async function () {
832 await getMyUserVideoRating(server.url, 'fake_token', video.id, HttpStatusCode.UNAUTHORIZED_401) 786 await command.getMyRating({ token: 'fake_token', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
833 }) 787 })
834 788
835 it('Should fail with an incorrect video uuid', async function () { 789 it('Should fail with an incorrect video uuid', async function () {
836 await getMyUserVideoRating(server.url, server.accessToken, 'blabla', HttpStatusCode.BAD_REQUEST_400) 790 await command.getMyRating({ videoId: 'blabla', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
837 }) 791 })
838 792
839 it('Should fail with an unknown video', async function () { 793 it('Should fail with an unknown video', async function () {
840 await getMyUserVideoRating(server.url, server.accessToken, '4da6fde3-88f7-4d16-b119-108df5630b06', HttpStatusCode.NOT_FOUND_404) 794 await command.getMyRating({ videoId: '4da6fde3-88f7-4d16-b119-108df5630b06', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
841 }) 795 })
842 796
843 it('Should succeed with the correct parameters', async function () { 797 it('Should succeed with the correct parameters', async function () {
844 await getMyUserVideoRating(server.url, server.accessToken, video.id) 798 await command.getMyRating({ videoId: video.id })
845 await getMyUserVideoRating(server.url, server.accessToken, video.uuid) 799 await command.getMyRating({ videoId: video.uuid })
846 await getMyUserVideoRating(server.url, server.accessToken, video.shortUUID) 800 await command.getMyRating({ videoId: video.shortUUID })
847 }) 801 })
848 }) 802 })
849 803
@@ -851,80 +805,93 @@ describe('Test users API validators', function () {
851 const path = '/api/v1/accounts/user1/ratings' 805 const path = '/api/v1/accounts/user1/ratings'
852 806
853 it('Should fail with a bad start pagination', async function () { 807 it('Should fail with a bad start pagination', async function () {
854 await checkBadStartPagination(server.url, path, userAccessToken) 808 await checkBadStartPagination(server.url, path, userToken)
855 }) 809 })
856 810
857 it('Should fail with a bad count pagination', async function () { 811 it('Should fail with a bad count pagination', async function () {
858 await checkBadCountPagination(server.url, path, userAccessToken) 812 await checkBadCountPagination(server.url, path, userToken)
859 }) 813 })
860 814
861 it('Should fail with an incorrect sort', async function () { 815 it('Should fail with an incorrect sort', async function () {
862 await checkBadSortPagination(server.url, path, userAccessToken) 816 await checkBadSortPagination(server.url, path, userToken)
863 }) 817 })
864 818
865 it('Should fail with a unauthenticated user', async function () { 819 it('Should fail with a unauthenticated user', async function () {
866 await makeGetRequest({ url: server.url, path, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 820 await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
867 }) 821 })
868 822
869 it('Should fail with a another user', async function () { 823 it('Should fail with a another user', async function () {
870 await makeGetRequest({ url: server.url, path, token: server.accessToken, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 824 await makeGetRequest({ url: server.url, path, token: server.accessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
871 }) 825 })
872 826
873 it('Should fail with a bad type', async function () { 827 it('Should fail with a bad type', async function () {
874 await makeGetRequest({ 828 await makeGetRequest({
875 url: server.url, 829 url: server.url,
876 path, 830 path,
877 token: userAccessToken, 831 token: userToken,
878 query: { rating: 'toto ' }, 832 query: { rating: 'toto ' },
879 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 833 expectedStatus: HttpStatusCode.BAD_REQUEST_400
880 }) 834 })
881 }) 835 })
882 836
883 it('Should succeed with the correct params', async function () { 837 it('Should succeed with the correct params', async function () {
884 await makeGetRequest({ url: server.url, path, token: userAccessToken, statusCodeExpected: HttpStatusCode.OK_200 }) 838 await makeGetRequest({ url: server.url, path, token: userToken, expectedStatus: HttpStatusCode.OK_200 })
885 }) 839 })
886 }) 840 })
887 841
888 describe('When blocking/unblocking/removing user', function () { 842 describe('When blocking/unblocking/removing user', function () {
843
889 it('Should fail with an incorrect id', async function () { 844 it('Should fail with an incorrect id', async function () {
890 await removeUser(server.url, 'blabla', server.accessToken, HttpStatusCode.BAD_REQUEST_400) 845 const options = { userId: 'blabla' as any, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }
891 await blockUser(server.url, 'blabla', server.accessToken, HttpStatusCode.BAD_REQUEST_400) 846
892 await unblockUser(server.url, 'blabla', server.accessToken, HttpStatusCode.BAD_REQUEST_400) 847 await server.users.remove(options)
848 await server.users.banUser({ userId: 'blabla' as any, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
849 await server.users.unbanUser({ userId: 'blabla' as any, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
893 }) 850 })
894 851
895 it('Should fail with the root user', async function () { 852 it('Should fail with the root user', async function () {
896 await removeUser(server.url, rootId, server.accessToken, HttpStatusCode.BAD_REQUEST_400) 853 const options = { userId: rootId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }
897 await blockUser(server.url, rootId, server.accessToken, HttpStatusCode.BAD_REQUEST_400) 854
898 await unblockUser(server.url, rootId, server.accessToken, HttpStatusCode.BAD_REQUEST_400) 855 await server.users.remove(options)
856 await server.users.banUser(options)
857 await server.users.unbanUser(options)
899 }) 858 })
900 859
901 it('Should return 404 with a non existing id', async function () { 860 it('Should return 404 with a non existing id', async function () {
902 await removeUser(server.url, 4545454, server.accessToken, HttpStatusCode.NOT_FOUND_404) 861 const options = { userId: 4545454, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
903 await blockUser(server.url, 4545454, server.accessToken, HttpStatusCode.NOT_FOUND_404) 862
904 await unblockUser(server.url, 4545454, server.accessToken, HttpStatusCode.NOT_FOUND_404) 863 await server.users.remove(options)
864 await server.users.banUser(options)
865 await server.users.unbanUser(options)
905 }) 866 })
906 867
907 it('Should fail with a non admin user', async function () { 868 it('Should fail with a non admin user', async function () {
908 await removeUser(server.url, userId, userAccessToken, HttpStatusCode.FORBIDDEN_403) 869 const options = { userId, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }
909 await blockUser(server.url, userId, userAccessToken, HttpStatusCode.FORBIDDEN_403) 870
910 await unblockUser(server.url, userId, userAccessToken, HttpStatusCode.FORBIDDEN_403) 871 await server.users.remove(options)
872 await server.users.banUser(options)
873 await server.users.unbanUser(options)
911 }) 874 })
912 875
913 it('Should fail on a moderator with a moderator', async function () { 876 it('Should fail on a moderator with a moderator', async function () {
914 await removeUser(server.url, moderatorId, moderatorAccessToken, HttpStatusCode.FORBIDDEN_403) 877 const options = { userId: moderatorId, token: moderatorToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }
915 await blockUser(server.url, moderatorId, moderatorAccessToken, HttpStatusCode.FORBIDDEN_403) 878
916 await unblockUser(server.url, moderatorId, moderatorAccessToken, HttpStatusCode.FORBIDDEN_403) 879 await server.users.remove(options)
880 await server.users.banUser(options)
881 await server.users.unbanUser(options)
917 }) 882 })
918 883
919 it('Should succeed on a user with a moderator', async function () { 884 it('Should succeed on a user with a moderator', async function () {
920 await blockUser(server.url, userId, moderatorAccessToken) 885 const options = { userId, token: moderatorToken }
921 await unblockUser(server.url, userId, moderatorAccessToken) 886
887 await server.users.banUser(options)
888 await server.users.unbanUser(options)
922 }) 889 })
923 }) 890 })
924 891
925 describe('When deleting our account', function () { 892 describe('When deleting our account', function () {
926 it('Should fail with with the root account', async function () { 893 it('Should fail with with the root account', async function () {
927 await deleteMe(server.url, server.accessToken, HttpStatusCode.BAD_REQUEST_400) 894 await server.users.deleteMe({ expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
928 }) 895 })
929 }) 896 })
930 897
@@ -938,19 +905,19 @@ describe('Test users API validators', function () {
938 } 905 }
939 906
940 it('Should fail with a too small username', async function () { 907 it('Should fail with a too small username', async function () {
941 const fields = immutableAssign(baseCorrectParams, { username: '' }) 908 const fields = { ...baseCorrectParams, username: '' }
942 909
943 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 910 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
944 }) 911 })
945 912
946 it('Should fail with a too long username', async function () { 913 it('Should fail with a too long username', async function () {
947 const fields = immutableAssign(baseCorrectParams, { username: 'super'.repeat(50) }) 914 const fields = { ...baseCorrectParams, username: 'super'.repeat(50) }
948 915
949 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 916 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
950 }) 917 })
951 918
952 it('Should fail with an incorrect username', async function () { 919 it('Should fail with an incorrect username', async function () {
953 const fields = immutableAssign(baseCorrectParams, { username: 'my username' }) 920 const fields = { ...baseCorrectParams, username: 'my username' }
954 921
955 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 922 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
956 }) 923 })
@@ -962,108 +929,108 @@ describe('Test users API validators', function () {
962 }) 929 })
963 930
964 it('Should fail with an invalid email', async function () { 931 it('Should fail with an invalid email', async function () {
965 const fields = immutableAssign(baseCorrectParams, { email: 'test_example.com' }) 932 const fields = { ...baseCorrectParams, email: 'test_example.com' }
966 933
967 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 934 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
968 }) 935 })
969 936
970 it('Should fail with a too small password', async function () { 937 it('Should fail with a too small password', async function () {
971 const fields = immutableAssign(baseCorrectParams, { password: 'bla' }) 938 const fields = { ...baseCorrectParams, password: 'bla' }
972 939
973 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 940 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
974 }) 941 })
975 942
976 it('Should fail with a too long password', async function () { 943 it('Should fail with a too long password', async function () {
977 const fields = immutableAssign(baseCorrectParams, { password: 'super'.repeat(61) }) 944 const fields = { ...baseCorrectParams, password: 'super'.repeat(61) }
978 945
979 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 946 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
980 }) 947 })
981 948
982 it('Should fail if we register a user with the same username', async function () { 949 it('Should fail if we register a user with the same username', async function () {
983 const fields = immutableAssign(baseCorrectParams, { username: 'root' }) 950 const fields = { ...baseCorrectParams, username: 'root' }
984 951
985 await makePostBodyRequest({ 952 await makePostBodyRequest({
986 url: server.url, 953 url: server.url,
987 path: registrationPath, 954 path: registrationPath,
988 token: server.accessToken, 955 token: server.accessToken,
989 fields, 956 fields,
990 statusCodeExpected: HttpStatusCode.CONFLICT_409 957 expectedStatus: HttpStatusCode.CONFLICT_409
991 }) 958 })
992 }) 959 })
993 960
994 it('Should fail with a "peertube" username', async function () { 961 it('Should fail with a "peertube" username', async function () {
995 const fields = immutableAssign(baseCorrectParams, { username: 'peertube' }) 962 const fields = { ...baseCorrectParams, username: 'peertube' }
996 963
997 await makePostBodyRequest({ 964 await makePostBodyRequest({
998 url: server.url, 965 url: server.url,
999 path: registrationPath, 966 path: registrationPath,
1000 token: server.accessToken, 967 token: server.accessToken,
1001 fields, 968 fields,
1002 statusCodeExpected: HttpStatusCode.CONFLICT_409 969 expectedStatus: HttpStatusCode.CONFLICT_409
1003 }) 970 })
1004 }) 971 })
1005 972
1006 it('Should fail if we register a user with the same email', async function () { 973 it('Should fail if we register a user with the same email', async function () {
1007 const fields = immutableAssign(baseCorrectParams, { email: 'admin' + server.internalServerNumber + '@example.com' }) 974 const fields = { ...baseCorrectParams, email: 'admin' + server.internalServerNumber + '@example.com' }
1008 975
1009 await makePostBodyRequest({ 976 await makePostBodyRequest({
1010 url: server.url, 977 url: server.url,
1011 path: registrationPath, 978 path: registrationPath,
1012 token: server.accessToken, 979 token: server.accessToken,
1013 fields, 980 fields,
1014 statusCodeExpected: HttpStatusCode.CONFLICT_409 981 expectedStatus: HttpStatusCode.CONFLICT_409
1015 }) 982 })
1016 }) 983 })
1017 984
1018 it('Should fail with a bad display name', async function () { 985 it('Should fail with a bad display name', async function () {
1019 const fields = immutableAssign(baseCorrectParams, { displayName: 'a'.repeat(150) }) 986 const fields = { ...baseCorrectParams, displayName: 'a'.repeat(150) }
1020 987
1021 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 988 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
1022 }) 989 })
1023 990
1024 it('Should fail with a bad channel name', async function () { 991 it('Should fail with a bad channel name', async function () {
1025 const fields = immutableAssign(baseCorrectParams, { channel: { name: '[]azf', displayName: 'toto' } }) 992 const fields = { ...baseCorrectParams, channel: { name: '[]azf', displayName: 'toto' } }
1026 993
1027 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 994 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
1028 }) 995 })
1029 996
1030 it('Should fail with a bad channel display name', async function () { 997 it('Should fail with a bad channel display name', async function () {
1031 const fields = immutableAssign(baseCorrectParams, { channel: { name: 'toto', displayName: '' } }) 998 const fields = { ...baseCorrectParams, channel: { name: 'toto', displayName: '' } }
1032 999
1033 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 1000 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
1034 }) 1001 })
1035 1002
1036 it('Should fail with a channel name that is the same as username', async function () { 1003 it('Should fail with a channel name that is the same as username', async function () {
1037 const source = { username: 'super_user', channel: { name: 'super_user', displayName: 'display name' } } 1004 const source = { username: 'super_user', channel: { name: 'super_user', displayName: 'display name' } }
1038 const fields = immutableAssign(baseCorrectParams, source) 1005 const fields = { ...baseCorrectParams, ...source }
1039 1006
1040 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 1007 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
1041 }) 1008 })
1042 1009
1043 it('Should fail with an existing channel', async function () { 1010 it('Should fail with an existing channel', async function () {
1044 const videoChannelAttributesArg = { name: 'existing_channel', displayName: 'hello', description: 'super description' } 1011 const attributes = { name: 'existing_channel', displayName: 'hello', description: 'super description' }
1045 await addVideoChannel(server.url, server.accessToken, videoChannelAttributesArg) 1012 await server.channels.create({ attributes })
1046 1013
1047 const fields = immutableAssign(baseCorrectParams, { channel: { name: 'existing_channel', displayName: 'toto' } }) 1014 const fields = { ...baseCorrectParams, channel: { name: 'existing_channel', displayName: 'toto' } }
1048 1015
1049 await makePostBodyRequest({ 1016 await makePostBodyRequest({
1050 url: server.url, 1017 url: server.url,
1051 path: registrationPath, 1018 path: registrationPath,
1052 token: server.accessToken, 1019 token: server.accessToken,
1053 fields, 1020 fields,
1054 statusCodeExpected: HttpStatusCode.CONFLICT_409 1021 expectedStatus: HttpStatusCode.CONFLICT_409
1055 }) 1022 })
1056 }) 1023 })
1057 1024
1058 it('Should succeed with the correct params', async function () { 1025 it('Should succeed with the correct params', async function () {
1059 const fields = immutableAssign(baseCorrectParams, { channel: { name: 'super_channel', displayName: 'toto' } }) 1026 const fields = { ...baseCorrectParams, channel: { name: 'super_channel', displayName: 'toto' } }
1060 1027
1061 await makePostBodyRequest({ 1028 await makePostBodyRequest({
1062 url: server.url, 1029 url: server.url,
1063 path: registrationPath, 1030 path: registrationPath,
1064 token: server.accessToken, 1031 token: server.accessToken,
1065 fields: fields, 1032 fields: fields,
1066 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 1033 expectedStatus: HttpStatusCode.NO_CONTENT_204
1067 }) 1034 })
1068 }) 1035 })
1069 1036
@@ -1079,14 +1046,14 @@ describe('Test users API validators', function () {
1079 path: registrationPath, 1046 path: registrationPath,
1080 token: serverWithRegistrationDisabled.accessToken, 1047 token: serverWithRegistrationDisabled.accessToken,
1081 fields, 1048 fields,
1082 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 1049 expectedStatus: HttpStatusCode.FORBIDDEN_403
1083 }) 1050 })
1084 }) 1051 })
1085 }) 1052 })
1086 1053
1087 describe('When registering multiple users on a server with users limit', function () { 1054 describe('When registering multiple users on a server with users limit', function () {
1088 it('Should fail when after 3 registrations', async function () { 1055 it('Should fail when after 3 registrations', async function () {
1089 await registerUser(server.url, 'user42', 'super password', HttpStatusCode.FORBIDDEN_403) 1056 await server.users.register({ username: 'user42', expectedStatus: HttpStatusCode.FORBIDDEN_403 })
1090 }) 1057 })
1091 }) 1058 })
1092 1059
@@ -1113,7 +1080,7 @@ describe('Test users API validators', function () {
1113 path, 1080 path,
1114 token: server.accessToken, 1081 token: server.accessToken,
1115 fields, 1082 fields,
1116 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 1083 expectedStatus: HttpStatusCode.NO_CONTENT_204
1117 }) 1084 })
1118 }) 1085 })
1119 }) 1086 })
@@ -1141,7 +1108,7 @@ describe('Test users API validators', function () {
1141 path, 1108 path,
1142 token: server.accessToken, 1109 token: server.accessToken,
1143 fields, 1110 fields,
1144 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 1111 expectedStatus: HttpStatusCode.NO_CONTENT_204
1145 }) 1112 })
1146 }) 1113 })
1147 }) 1114 })
diff --git a/server/tests/api/check-params/video-blacklist.ts b/server/tests/api/check-params/video-blacklist.ts
index ce7f5fa17..1f926d227 100644
--- a/server/tests/api/check-params/video-blacklist.ts
+++ b/server/tests/api/check-params/video-blacklist.ts
@@ -1,46 +1,37 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4 4import { expect } from 'chai'
5import { 5import {
6 BlacklistCommand,
7 checkBadCountPagination,
8 checkBadSortPagination,
9 checkBadStartPagination,
6 cleanupTests, 10 cleanupTests,
7 createUser, 11 createMultipleServers,
8 doubleFollow, 12 doubleFollow,
9 flushAndRunMultipleServers,
10 getBlacklistedVideosList,
11 getVideo,
12 getVideoWithToken,
13 makePostBodyRequest, 13 makePostBodyRequest,
14 makePutBodyRequest, 14 makePutBodyRequest,
15 removeVideoFromBlacklist, 15 PeerTubeServer,
16 ServerInfo,
17 setAccessTokensToServers, 16 setAccessTokensToServers,
18 uploadVideo,
19 userLogin,
20 waitJobs 17 waitJobs
21} from '../../../../shared/extra-utils' 18} from '@shared/extra-utils'
22import { 19import { HttpStatusCode, VideoBlacklistType } from '@shared/models'
23 checkBadCountPagination,
24 checkBadSortPagination,
25 checkBadStartPagination
26} from '../../../../shared/extra-utils/requests/check-api-params'
27import { VideoBlacklistType, VideoDetails } from '../../../../shared/models/videos'
28import { expect } from 'chai'
29import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
30 20
31describe('Test video blacklist API validators', function () { 21describe('Test video blacklist API validators', function () {
32 let servers: ServerInfo[] 22 let servers: PeerTubeServer[]
33 let notBlacklistedVideoId: number 23 let notBlacklistedVideoId: string
34 let remoteVideoUUID: string 24 let remoteVideoUUID: string
35 let userAccessToken1 = '' 25 let userAccessToken1 = ''
36 let userAccessToken2 = '' 26 let userAccessToken2 = ''
27 let command: BlacklistCommand
37 28
38 // --------------------------------------------------------------- 29 // ---------------------------------------------------------------
39 30
40 before(async function () { 31 before(async function () {
41 this.timeout(120000) 32 this.timeout(120000)
42 33
43 servers = await flushAndRunMultipleServers(2) 34 servers = await createMultipleServers(2)
44 35
45 await setAccessTokensToServers(servers) 36 await setAccessTokensToServers(servers)
46 await doubleFollow(servers[0], servers[1]) 37 await doubleFollow(servers[0], servers[1])
@@ -48,40 +39,41 @@ describe('Test video blacklist API validators', function () {
48 { 39 {
49 const username = 'user1' 40 const username = 'user1'
50 const password = 'my super password' 41 const password = 'my super password'
51 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: username, password: password }) 42 await servers[0].users.create({ username: username, password: password })
52 userAccessToken1 = await userLogin(servers[0], { username, password }) 43 userAccessToken1 = await servers[0].login.getAccessToken({ username, password })
53 } 44 }
54 45
55 { 46 {
56 const username = 'user2' 47 const username = 'user2'
57 const password = 'my super password' 48 const password = 'my super password'
58 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: username, password: password }) 49 await servers[0].users.create({ username: username, password: password })
59 userAccessToken2 = await userLogin(servers[0], { username, password }) 50 userAccessToken2 = await servers[0].login.getAccessToken({ username, password })
60 } 51 }
61 52
62 { 53 {
63 const res = await uploadVideo(servers[0].url, userAccessToken1, {}) 54 servers[0].store.videoCreated = await servers[0].videos.upload({ token: userAccessToken1 })
64 servers[0].video = res.body.video
65 } 55 }
66 56
67 { 57 {
68 const res = await uploadVideo(servers[0].url, servers[0].accessToken, {}) 58 const { uuid } = await servers[0].videos.upload()
69 notBlacklistedVideoId = res.body.video.uuid 59 notBlacklistedVideoId = uuid
70 } 60 }
71 61
72 { 62 {
73 const res = await uploadVideo(servers[1].url, servers[1].accessToken, {}) 63 const { uuid } = await servers[1].videos.upload()
74 remoteVideoUUID = res.body.video.uuid 64 remoteVideoUUID = uuid
75 } 65 }
76 66
77 await waitJobs(servers) 67 await waitJobs(servers)
68
69 command = servers[0].blacklist
78 }) 70 })
79 71
80 describe('When adding a video in blacklist', function () { 72 describe('When adding a video in blacklist', function () {
81 const basePath = '/api/v1/videos/' 73 const basePath = '/api/v1/videos/'
82 74
83 it('Should fail with nothing', async function () { 75 it('Should fail with nothing', async function () {
84 const path = basePath + servers[0].video + '/blacklist' 76 const path = basePath + servers[0].store.videoCreated + '/blacklist'
85 const fields = {} 77 const fields = {}
86 await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields }) 78 await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields })
87 }) 79 })
@@ -93,25 +85,25 @@ describe('Test video blacklist API validators', function () {
93 }) 85 })
94 86
95 it('Should fail with a non authenticated user', async function () { 87 it('Should fail with a non authenticated user', async function () {
96 const path = basePath + servers[0].video + '/blacklist' 88 const path = basePath + servers[0].store.videoCreated + '/blacklist'
97 const fields = {} 89 const fields = {}
98 await makePostBodyRequest({ url: servers[0].url, path, token: 'hello', fields, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 90 await makePostBodyRequest({ url: servers[0].url, path, token: 'hello', fields, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
99 }) 91 })
100 92
101 it('Should fail with a non admin user', async function () { 93 it('Should fail with a non admin user', async function () {
102 const path = basePath + servers[0].video + '/blacklist' 94 const path = basePath + servers[0].store.videoCreated + '/blacklist'
103 const fields = {} 95 const fields = {}
104 await makePostBodyRequest({ 96 await makePostBodyRequest({
105 url: servers[0].url, 97 url: servers[0].url,
106 path, 98 path,
107 token: userAccessToken2, 99 token: userAccessToken2,
108 fields, 100 fields,
109 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 101 expectedStatus: HttpStatusCode.FORBIDDEN_403
110 }) 102 })
111 }) 103 })
112 104
113 it('Should fail with an invalid reason', async function () { 105 it('Should fail with an invalid reason', async function () {
114 const path = basePath + servers[0].video.uuid + '/blacklist' 106 const path = basePath + servers[0].store.videoCreated.uuid + '/blacklist'
115 const fields = { reason: 'a'.repeat(305) } 107 const fields = { reason: 'a'.repeat(305) }
116 108
117 await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields }) 109 await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields })
@@ -126,12 +118,12 @@ describe('Test video blacklist API validators', function () {
126 path, 118 path,
127 token: servers[0].accessToken, 119 token: servers[0].accessToken,
128 fields, 120 fields,
129 statusCodeExpected: HttpStatusCode.CONFLICT_409 121 expectedStatus: HttpStatusCode.CONFLICT_409
130 }) 122 })
131 }) 123 })
132 124
133 it('Should succeed with the correct params', async function () { 125 it('Should succeed with the correct params', async function () {
134 const path = basePath + servers[0].video.uuid + '/blacklist' 126 const path = basePath + servers[0].store.videoCreated.uuid + '/blacklist'
135 const fields = {} 127 const fields = {}
136 128
137 await makePostBodyRequest({ 129 await makePostBodyRequest({
@@ -139,7 +131,7 @@ describe('Test video blacklist API validators', function () {
139 path, 131 path,
140 token: servers[0].accessToken, 132 token: servers[0].accessToken,
141 fields, 133 fields,
142 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 134 expectedStatus: HttpStatusCode.NO_CONTENT_204
143 }) 135 })
144 }) 136 })
145 }) 137 })
@@ -161,37 +153,37 @@ describe('Test video blacklist API validators', function () {
161 path, 153 path,
162 token: servers[0].accessToken, 154 token: servers[0].accessToken,
163 fields, 155 fields,
164 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 156 expectedStatus: HttpStatusCode.NOT_FOUND_404
165 }) 157 })
166 }) 158 })
167 159
168 it('Should fail with a non authenticated user', async function () { 160 it('Should fail with a non authenticated user', async function () {
169 const path = basePath + servers[0].video + '/blacklist' 161 const path = basePath + servers[0].store.videoCreated + '/blacklist'
170 const fields = {} 162 const fields = {}
171 await makePutBodyRequest({ url: servers[0].url, path, token: 'hello', fields, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 163 await makePutBodyRequest({ url: servers[0].url, path, token: 'hello', fields, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
172 }) 164 })
173 165
174 it('Should fail with a non admin user', async function () { 166 it('Should fail with a non admin user', async function () {
175 const path = basePath + servers[0].video + '/blacklist' 167 const path = basePath + servers[0].store.videoCreated + '/blacklist'
176 const fields = {} 168 const fields = {}
177 await makePutBodyRequest({ 169 await makePutBodyRequest({
178 url: servers[0].url, 170 url: servers[0].url,
179 path, 171 path,
180 token: userAccessToken2, 172 token: userAccessToken2,
181 fields, 173 fields,
182 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 174 expectedStatus: HttpStatusCode.FORBIDDEN_403
183 }) 175 })
184 }) 176 })
185 177
186 it('Should fail with an invalid reason', async function () { 178 it('Should fail with an invalid reason', async function () {
187 const path = basePath + servers[0].video.uuid + '/blacklist' 179 const path = basePath + servers[0].store.videoCreated.uuid + '/blacklist'
188 const fields = { reason: 'a'.repeat(305) } 180 const fields = { reason: 'a'.repeat(305) }
189 181
190 await makePutBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields }) 182 await makePutBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields })
191 }) 183 })
192 184
193 it('Should succeed with the correct params', async function () { 185 it('Should succeed with the correct params', async function () {
194 const path = basePath + servers[0].video.shortUUID + '/blacklist' 186 const path = basePath + servers[0].store.videoCreated.shortUUID + '/blacklist'
195 const fields = { reason: 'hello' } 187 const fields = { reason: 'hello' }
196 188
197 await makePutBodyRequest({ 189 await makePutBodyRequest({
@@ -199,7 +191,7 @@ describe('Test video blacklist API validators', function () {
199 path, 191 path,
200 token: servers[0].accessToken, 192 token: servers[0].accessToken,
201 fields, 193 fields,
202 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 194 expectedStatus: HttpStatusCode.NO_CONTENT_204
203 }) 195 })
204 }) 196 })
205 }) 197 })
@@ -207,52 +199,61 @@ describe('Test video blacklist API validators', function () {
207 describe('When getting blacklisted video', function () { 199 describe('When getting blacklisted video', function () {
208 200
209 it('Should fail with a non authenticated user', async function () { 201 it('Should fail with a non authenticated user', async function () {
210 await getVideo(servers[0].url, servers[0].video.uuid, HttpStatusCode.UNAUTHORIZED_401) 202 await servers[0].videos.get({ id: servers[0].store.videoCreated.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
211 }) 203 })
212 204
213 it('Should fail with another user', async function () { 205 it('Should fail with another user', async function () {
214 await getVideoWithToken(servers[0].url, userAccessToken2, servers[0].video.uuid, HttpStatusCode.FORBIDDEN_403) 206 await servers[0].videos.getWithToken({
207 token: userAccessToken2,
208 id: servers[0].store.videoCreated.uuid,
209 expectedStatus: HttpStatusCode.FORBIDDEN_403
210 })
215 }) 211 })
216 212
217 it('Should succeed with the owner authenticated user', async function () { 213 it('Should succeed with the owner authenticated user', async function () {
218 const res = await getVideoWithToken(servers[0].url, userAccessToken1, servers[0].video.uuid, HttpStatusCode.OK_200) 214 const video = await servers[0].videos.getWithToken({ token: userAccessToken1, id: servers[0].store.videoCreated.uuid })
219 const video: VideoDetails = res.body
220
221 expect(video.blacklisted).to.be.true 215 expect(video.blacklisted).to.be.true
222 }) 216 })
223 217
224 it('Should succeed with an admin', async function () { 218 it('Should succeed with an admin', async function () {
225 const video = servers[0].video 219 const video = servers[0].store.videoCreated
226 220
227 for (const id of [ video.id, video.uuid, video.shortUUID ]) { 221 for (const id of [ video.id, video.uuid, video.shortUUID ]) {
228 const res = await getVideoWithToken(servers[0].url, servers[0].accessToken, id, HttpStatusCode.OK_200) 222 const video = await servers[0].videos.getWithToken({ id, expectedStatus: HttpStatusCode.OK_200 })
229 const video: VideoDetails = res.body
230
231 expect(video.blacklisted).to.be.true 223 expect(video.blacklisted).to.be.true
232 } 224 }
233 }) 225 })
234 }) 226 })
235 227
236 describe('When removing a video in blacklist', function () { 228 describe('When removing a video in blacklist', function () {
229
237 it('Should fail with a non authenticated user', async function () { 230 it('Should fail with a non authenticated user', async function () {
238 await removeVideoFromBlacklist(servers[0].url, 'fake token', servers[0].video.uuid, HttpStatusCode.UNAUTHORIZED_401) 231 await command.remove({
232 token: 'fake token',
233 videoId: servers[0].store.videoCreated.uuid,
234 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
235 })
239 }) 236 })
240 237
241 it('Should fail with a non admin user', async function () { 238 it('Should fail with a non admin user', async function () {
242 await removeVideoFromBlacklist(servers[0].url, userAccessToken2, servers[0].video.uuid, HttpStatusCode.FORBIDDEN_403) 239 await command.remove({
240 token: userAccessToken2,
241 videoId: servers[0].store.videoCreated.uuid,
242 expectedStatus: HttpStatusCode.FORBIDDEN_403
243 })
243 }) 244 })
244 245
245 it('Should fail with an incorrect id', async function () { 246 it('Should fail with an incorrect id', async function () {
246 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, 'hello', HttpStatusCode.BAD_REQUEST_400) 247 await command.remove({ videoId: 'hello', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
247 }) 248 })
248 249
249 it('Should fail with a not blacklisted video', async function () { 250 it('Should fail with a not blacklisted video', async function () {
250 // The video was not added to the blacklist so it should fail 251 // The video was not added to the blacklist so it should fail
251 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, notBlacklistedVideoId, HttpStatusCode.NOT_FOUND_404) 252 await command.remove({ videoId: notBlacklistedVideoId, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
252 }) 253 })
253 254
254 it('Should succeed with the correct params', async function () { 255 it('Should succeed with the correct params', async function () {
255 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, servers[0].video.uuid, HttpStatusCode.NO_CONTENT_204) 256 await command.remove({ videoId: servers[0].store.videoCreated.uuid, expectedStatus: HttpStatusCode.NO_CONTENT_204 })
256 }) 257 })
257 }) 258 })
258 259
@@ -260,11 +261,11 @@ describe('Test video blacklist API validators', function () {
260 const basePath = '/api/v1/videos/blacklist/' 261 const basePath = '/api/v1/videos/blacklist/'
261 262
262 it('Should fail with a non authenticated user', async function () { 263 it('Should fail with a non authenticated user', async function () {
263 await getBlacklistedVideosList({ url: servers[0].url, token: 'fake token', specialStatus: HttpStatusCode.UNAUTHORIZED_401 }) 264 await servers[0].blacklist.list({ token: 'fake token', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
264 }) 265 })
265 266
266 it('Should fail with a non admin user', async function () { 267 it('Should fail with a non admin user', async function () {
267 await getBlacklistedVideosList({ url: servers[0].url, token: userAccessToken2, specialStatus: HttpStatusCode.FORBIDDEN_403 }) 268 await servers[0].blacklist.list({ token: userAccessToken2, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
268 }) 269 })
269 270
270 it('Should fail with a bad start pagination', async function () { 271 it('Should fail with a bad start pagination', async function () {
@@ -280,16 +281,11 @@ describe('Test video blacklist API validators', function () {
280 }) 281 })
281 282
282 it('Should fail with an invalid type', async function () { 283 it('Should fail with an invalid type', async function () {
283 await getBlacklistedVideosList({ 284 await servers[0].blacklist.list({ type: 0, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
284 url: servers[0].url,
285 token: servers[0].accessToken,
286 type: 0,
287 specialStatus: HttpStatusCode.BAD_REQUEST_400
288 })
289 }) 285 })
290 286
291 it('Should succeed with the correct parameters', async function () { 287 it('Should succeed with the correct parameters', async function () {
292 await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, type: VideoBlacklistType.MANUAL }) 288 await servers[0].blacklist.list({ type: VideoBlacklistType.MANUAL })
293 }) 289 })
294 }) 290 })
295 291
diff --git a/server/tests/api/check-params/video-captions.ts b/server/tests/api/check-params/video-captions.ts
index c0595c04d..90f429314 100644
--- a/server/tests/api/check-params/video-captions.ts
+++ b/server/tests/api/check-params/video-captions.ts
@@ -1,27 +1,22 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { VideoCreateResult } from '@shared/models'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6import { 4import {
7 buildAbsoluteFixturePath, 5 buildAbsoluteFixturePath,
8 cleanupTests, 6 cleanupTests,
9 createUser, 7 createSingleServer,
10 flushAndRunServer,
11 makeDeleteRequest, 8 makeDeleteRequest,
12 makeGetRequest, 9 makeGetRequest,
13 makeUploadRequest, 10 makeUploadRequest,
14 ServerInfo, 11 PeerTubeServer,
15 setAccessTokensToServers, 12 setAccessTokensToServers
16 uploadVideo, 13} from '@shared/extra-utils'
17 userLogin 14import { HttpStatusCode, VideoCreateResult } from '@shared/models'
18} from '../../../../shared/extra-utils'
19import { createVideoCaption } from '../../../../shared/extra-utils/videos/video-captions'
20 15
21describe('Test video captions API validator', function () { 16describe('Test video captions API validator', function () {
22 const path = '/api/v1/videos/' 17 const path = '/api/v1/videos/'
23 18
24 let server: ServerInfo 19 let server: PeerTubeServer
25 let userAccessToken: string 20 let userAccessToken: string
26 let video: VideoCreateResult 21 let video: VideoCreateResult
27 22
@@ -30,22 +25,19 @@ describe('Test video captions API validator', function () {
30 before(async function () { 25 before(async function () {
31 this.timeout(30000) 26 this.timeout(30000)
32 27
33 server = await flushAndRunServer(1) 28 server = await createSingleServer(1)
34 29
35 await setAccessTokensToServers([ server ]) 30 await setAccessTokensToServers([ server ])
36 31
37 { 32 video = await server.videos.upload()
38 const res = await uploadVideo(server.url, server.accessToken, {})
39 video = res.body.video
40 }
41 33
42 { 34 {
43 const user = { 35 const user = {
44 username: 'user1', 36 username: 'user1',
45 password: 'my super password' 37 password: 'my super password'
46 } 38 }
47 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 39 await server.users.create({ username: user.username, password: user.password })
48 userAccessToken = await userLogin(server, user) 40 userAccessToken = await server.login.getAccessToken(user)
49 } 41 }
50 }) 42 })
51 43
@@ -74,7 +66,7 @@ describe('Test video captions API validator', function () {
74 token: server.accessToken, 66 token: server.accessToken,
75 fields, 67 fields,
76 attaches, 68 attaches,
77 statusCodeExpected: 404 69 expectedStatus: 404
78 }) 70 })
79 }) 71 })
80 72
@@ -110,7 +102,7 @@ describe('Test video captions API validator', function () {
110 path: captionPath, 102 path: captionPath,
111 fields, 103 fields,
112 attaches, 104 attaches,
113 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 105 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
114 }) 106 })
115 }) 107 })
116 108
@@ -123,7 +115,7 @@ describe('Test video captions API validator', function () {
123 token: 'blabla', 115 token: 'blabla',
124 fields, 116 fields,
125 attaches, 117 attaches,
126 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 118 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
127 }) 119 })
128 }) 120 })
129 121
@@ -141,7 +133,7 @@ describe('Test video captions API validator', function () {
141 // token: server.accessToken, 133 // token: server.accessToken,
142 // fields, 134 // fields,
143 // attaches, 135 // attaches,
144 // statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 136 // expectedStatus: HttpStatusCode.BAD_REQUEST_400
145 // }) 137 // })
146 // }) 138 // })
147 139
@@ -154,14 +146,12 @@ describe('Test video captions API validator', function () {
154 // videoId: video.uuid, 146 // videoId: video.uuid,
155 // fixture: 'subtitle-bad.txt', 147 // fixture: 'subtitle-bad.txt',
156 // mimeType: 'application/octet-stream', 148 // mimeType: 'application/octet-stream',
157 // statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 149 // expectedStatus: HttpStatusCode.BAD_REQUEST_400
158 // }) 150 // })
159 // }) 151 // })
160 152
161 it('Should succeed with a valid captionfile extension and octet-stream mime type', async function () { 153 it('Should succeed with a valid captionfile extension and octet-stream mime type', async function () {
162 await createVideoCaption({ 154 await server.captions.add({
163 url: server.url,
164 accessToken: server.accessToken,
165 language: 'zh', 155 language: 'zh',
166 videoId: video.uuid, 156 videoId: video.uuid,
167 fixture: 'subtitle-good.srt', 157 fixture: 'subtitle-good.srt',
@@ -183,7 +173,7 @@ describe('Test video captions API validator', function () {
183 // token: server.accessToken, 173 // token: server.accessToken,
184 // fields, 174 // fields,
185 // attaches, 175 // attaches,
186 // statusCodeExpected: HttpStatusCode.INTERNAL_SERVER_ERROR_500 176 // expectedStatus: HttpStatusCode.INTERNAL_SERVER_ERROR_500
187 // }) 177 // })
188 // }) 178 // })
189 179
@@ -196,7 +186,7 @@ describe('Test video captions API validator', function () {
196 token: server.accessToken, 186 token: server.accessToken,
197 fields, 187 fields,
198 attaches, 188 attaches,
199 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 189 expectedStatus: HttpStatusCode.NO_CONTENT_204
200 }) 190 })
201 }) 191 })
202 }) 192 })
@@ -210,12 +200,12 @@ describe('Test video captions API validator', function () {
210 await makeGetRequest({ 200 await makeGetRequest({
211 url: server.url, 201 url: server.url,
212 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06/captions', 202 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06/captions',
213 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 203 expectedStatus: HttpStatusCode.NOT_FOUND_404
214 }) 204 })
215 }) 205 })
216 206
217 it('Should success with the correct parameters', async function () { 207 it('Should success with the correct parameters', async function () {
218 await makeGetRequest({ url: server.url, path: path + video.shortUUID + '/captions', statusCodeExpected: HttpStatusCode.OK_200 }) 208 await makeGetRequest({ url: server.url, path: path + video.shortUUID + '/captions', expectedStatus: HttpStatusCode.OK_200 })
219 }) 209 })
220 }) 210 })
221 211
@@ -233,7 +223,7 @@ describe('Test video captions API validator', function () {
233 url: server.url, 223 url: server.url,
234 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06/captions/fr', 224 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06/captions/fr',
235 token: server.accessToken, 225 token: server.accessToken,
236 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 226 expectedStatus: HttpStatusCode.NOT_FOUND_404
237 }) 227 })
238 }) 228 })
239 229
@@ -257,12 +247,12 @@ describe('Test video captions API validator', function () {
257 247
258 it('Should fail without access token', async function () { 248 it('Should fail without access token', async function () {
259 const captionPath = path + video.shortUUID + '/captions/fr' 249 const captionPath = path + video.shortUUID + '/captions/fr'
260 await makeDeleteRequest({ url: server.url, path: captionPath, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 250 await makeDeleteRequest({ url: server.url, path: captionPath, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
261 }) 251 })
262 252
263 it('Should fail with a bad access token', async function () { 253 it('Should fail with a bad access token', async function () {
264 const captionPath = path + video.shortUUID + '/captions/fr' 254 const captionPath = path + video.shortUUID + '/captions/fr'
265 await makeDeleteRequest({ url: server.url, path: captionPath, token: 'coucou', statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 255 await makeDeleteRequest({ url: server.url, path: captionPath, token: 'coucou', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
266 }) 256 })
267 257
268 it('Should fail with another user', async function () { 258 it('Should fail with another user', async function () {
@@ -271,7 +261,7 @@ describe('Test video captions API validator', function () {
271 url: server.url, 261 url: server.url,
272 path: captionPath, 262 path: captionPath,
273 token: userAccessToken, 263 token: userAccessToken,
274 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 264 expectedStatus: HttpStatusCode.FORBIDDEN_403
275 }) 265 })
276 }) 266 })
277 267
@@ -281,7 +271,7 @@ describe('Test video captions API validator', function () {
281 url: server.url, 271 url: server.url,
282 path: captionPath, 272 path: captionPath,
283 token: server.accessToken, 273 token: server.accessToken,
284 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 274 expectedStatus: HttpStatusCode.NO_CONTENT_204
285 }) 275 })
286 }) 276 })
287 }) 277 })
diff --git a/server/tests/api/check-params/video-channels.ts b/server/tests/api/check-params/video-channels.ts
index 5c02afd31..2e63916d4 100644
--- a/server/tests/api/check-params/video-channels.ts
+++ b/server/tests/api/check-params/video-channels.ts
@@ -3,43 +3,37 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { omit } from 'lodash' 5import { omit } from 'lodash'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { 6import {
8 buildAbsoluteFixturePath, 7 buildAbsoluteFixturePath,
8 ChannelsCommand,
9 checkBadCountPagination,
10 checkBadSortPagination,
11 checkBadStartPagination,
9 cleanupTests, 12 cleanupTests,
10 createUser, 13 createSingleServer,
11 deleteVideoChannel,
12 flushAndRunServer,
13 getAccountVideoChannelsList,
14 immutableAssign,
15 makeGetRequest, 14 makeGetRequest,
16 makePostBodyRequest, 15 makePostBodyRequest,
17 makePutBodyRequest, 16 makePutBodyRequest,
18 makeUploadRequest, 17 makeUploadRequest,
19 ServerInfo, 18 PeerTubeServer,
20 setAccessTokensToServers, 19 setAccessTokensToServers
21 userLogin 20} from '@shared/extra-utils'
22} from '../../../../shared/extra-utils' 21import { HttpStatusCode, VideoChannelUpdate } from '@shared/models'
23import {
24 checkBadCountPagination,
25 checkBadSortPagination,
26 checkBadStartPagination
27} from '../../../../shared/extra-utils/requests/check-api-params'
28import { VideoChannelUpdate } from '../../../../shared/models/videos'
29 22
30const expect = chai.expect 23const expect = chai.expect
31 24
32describe('Test video channels API validator', function () { 25describe('Test video channels API validator', function () {
33 const videoChannelPath = '/api/v1/video-channels' 26 const videoChannelPath = '/api/v1/video-channels'
34 let server: ServerInfo 27 let server: PeerTubeServer
35 let accessTokenUser: string 28 let accessTokenUser: string
29 let command: ChannelsCommand
36 30
37 // --------------------------------------------------------------- 31 // ---------------------------------------------------------------
38 32
39 before(async function () { 33 before(async function () {
40 this.timeout(30000) 34 this.timeout(30000)
41 35
42 server = await flushAndRunServer(1) 36 server = await createSingleServer(1)
43 37
44 await setAccessTokensToServers([ server ]) 38 await setAccessTokensToServers([ server ])
45 39
@@ -49,9 +43,11 @@ describe('Test video channels API validator', function () {
49 } 43 }
50 44
51 { 45 {
52 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 46 await server.users.create({ username: user.username, password: user.password })
53 accessTokenUser = await userLogin(server, user) 47 accessTokenUser = await server.login.getAccessToken(user)
54 } 48 }
49
50 command = server.channels
55 }) 51 })
56 52
57 describe('When listing a video channels', function () { 53 describe('When listing a video channels', function () {
@@ -84,14 +80,14 @@ describe('Test video channels API validator', function () {
84 }) 80 })
85 81
86 it('Should fail with a unknown account', async function () { 82 it('Should fail with a unknown account', async function () {
87 await getAccountVideoChannelsList({ url: server.url, accountName: 'unknown', specialStatus: HttpStatusCode.NOT_FOUND_404 }) 83 await server.channels.listByAccount({ accountName: 'unknown', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
88 }) 84 })
89 85
90 it('Should succeed with the correct parameters', async function () { 86 it('Should succeed with the correct parameters', async function () {
91 await makeGetRequest({ 87 await makeGetRequest({
92 url: server.url, 88 url: server.url,
93 path: accountChannelPath, 89 path: accountChannelPath,
94 statusCodeExpected: HttpStatusCode.OK_200 90 expectedStatus: HttpStatusCode.OK_200
95 }) 91 })
96 }) 92 })
97 }) 93 })
@@ -110,7 +106,7 @@ describe('Test video channels API validator', function () {
110 path: videoChannelPath, 106 path: videoChannelPath,
111 token: 'none', 107 token: 'none',
112 fields: baseCorrectParams, 108 fields: baseCorrectParams,
113 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 109 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
114 }) 110 })
115 }) 111 })
116 112
@@ -125,7 +121,7 @@ describe('Test video channels API validator', function () {
125 }) 121 })
126 122
127 it('Should fail with a bad name', async function () { 123 it('Should fail with a bad name', async function () {
128 const fields = immutableAssign(baseCorrectParams, { name: 'super name' }) 124 const fields = { ...baseCorrectParams, name: 'super name' }
129 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) 125 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
130 }) 126 })
131 127
@@ -135,17 +131,17 @@ describe('Test video channels API validator', function () {
135 }) 131 })
136 132
137 it('Should fail with a long name', async function () { 133 it('Should fail with a long name', async function () {
138 const fields = immutableAssign(baseCorrectParams, { displayName: 'super'.repeat(25) }) 134 const fields = { ...baseCorrectParams, displayName: 'super'.repeat(25) }
139 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) 135 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
140 }) 136 })
141 137
142 it('Should fail with a long description', async function () { 138 it('Should fail with a long description', async function () {
143 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(201) }) 139 const fields = { ...baseCorrectParams, description: 'super'.repeat(201) }
144 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) 140 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
145 }) 141 })
146 142
147 it('Should fail with a long support text', async function () { 143 it('Should fail with a long support text', async function () {
148 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) }) 144 const fields = { ...baseCorrectParams, support: 'super'.repeat(201) }
149 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) 145 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
150 }) 146 })
151 147
@@ -155,7 +151,7 @@ describe('Test video channels API validator', function () {
155 path: videoChannelPath, 151 path: videoChannelPath,
156 token: server.accessToken, 152 token: server.accessToken,
157 fields: baseCorrectParams, 153 fields: baseCorrectParams,
158 statusCodeExpected: HttpStatusCode.OK_200 154 expectedStatus: HttpStatusCode.OK_200
159 }) 155 })
160 }) 156 })
161 157
@@ -165,7 +161,7 @@ describe('Test video channels API validator', function () {
165 path: videoChannelPath, 161 path: videoChannelPath,
166 token: server.accessToken, 162 token: server.accessToken,
167 fields: baseCorrectParams, 163 fields: baseCorrectParams,
168 statusCodeExpected: HttpStatusCode.CONFLICT_409 164 expectedStatus: HttpStatusCode.CONFLICT_409
169 }) 165 })
170 }) 166 })
171 }) 167 })
@@ -189,7 +185,7 @@ describe('Test video channels API validator', function () {
189 path, 185 path,
190 token: 'hi', 186 token: 'hi',
191 fields: baseCorrectParams, 187 fields: baseCorrectParams,
192 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 188 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
193 }) 189 })
194 }) 190 })
195 191
@@ -199,27 +195,27 @@ describe('Test video channels API validator', function () {
199 path, 195 path,
200 token: accessTokenUser, 196 token: accessTokenUser,
201 fields: baseCorrectParams, 197 fields: baseCorrectParams,
202 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 198 expectedStatus: HttpStatusCode.FORBIDDEN_403
203 }) 199 })
204 }) 200 })
205 201
206 it('Should fail with a long name', async function () { 202 it('Should fail with a long name', async function () {
207 const fields = immutableAssign(baseCorrectParams, { displayName: 'super'.repeat(25) }) 203 const fields = { ...baseCorrectParams, displayName: 'super'.repeat(25) }
208 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 204 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
209 }) 205 })
210 206
211 it('Should fail with a long description', async function () { 207 it('Should fail with a long description', async function () {
212 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(201) }) 208 const fields = { ...baseCorrectParams, description: 'super'.repeat(201) }
213 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 209 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
214 }) 210 })
215 211
216 it('Should fail with a long support text', async function () { 212 it('Should fail with a long support text', async function () {
217 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) }) 213 const fields = { ...baseCorrectParams, support: 'super'.repeat(201) }
218 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 214 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
219 }) 215 })
220 216
221 it('Should fail with a bad bulkVideosSupportUpdate field', async function () { 217 it('Should fail with a bad bulkVideosSupportUpdate field', async function () {
222 const fields = immutableAssign(baseCorrectParams, { bulkVideosSupportUpdate: 'super' }) 218 const fields = { ...baseCorrectParams, bulkVideosSupportUpdate: 'super' }
223 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 219 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
224 }) 220 })
225 221
@@ -229,7 +225,7 @@ describe('Test video channels API validator', function () {
229 path, 225 path,
230 token: server.accessToken, 226 token: server.accessToken,
231 fields: baseCorrectParams, 227 fields: baseCorrectParams,
232 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 228 expectedStatus: HttpStatusCode.NO_CONTENT_204
233 }) 229 })
234 }) 230 })
235 }) 231 })
@@ -274,7 +270,7 @@ describe('Test video channels API validator', function () {
274 path: `${path}/${type}/pick`, 270 path: `${path}/${type}/pick`,
275 fields, 271 fields,
276 attaches, 272 attaches,
277 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 273 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
278 }) 274 })
279 } 275 }
280 }) 276 })
@@ -291,7 +287,7 @@ describe('Test video channels API validator', function () {
291 token: server.accessToken, 287 token: server.accessToken,
292 fields, 288 fields,
293 attaches, 289 attaches,
294 statusCodeExpected: HttpStatusCode.OK_200 290 expectedStatus: HttpStatusCode.OK_200
295 }) 291 })
296 } 292 }
297 }) 293 })
@@ -302,7 +298,7 @@ describe('Test video channels API validator', function () {
302 const res = await makeGetRequest({ 298 const res = await makeGetRequest({
303 url: server.url, 299 url: server.url,
304 path: videoChannelPath, 300 path: videoChannelPath,
305 statusCodeExpected: HttpStatusCode.OK_200 301 expectedStatus: HttpStatusCode.OK_200
306 }) 302 })
307 303
308 expect(res.body.data).to.be.an('array') 304 expect(res.body.data).to.be.an('array')
@@ -312,7 +308,7 @@ describe('Test video channels API validator', function () {
312 await makeGetRequest({ 308 await makeGetRequest({
313 url: server.url, 309 url: server.url,
314 path: videoChannelPath + '/super_channel2', 310 path: videoChannelPath + '/super_channel2',
315 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 311 expectedStatus: HttpStatusCode.NOT_FOUND_404
316 }) 312 })
317 }) 313 })
318 314
@@ -320,30 +316,30 @@ describe('Test video channels API validator', function () {
320 await makeGetRequest({ 316 await makeGetRequest({
321 url: server.url, 317 url: server.url,
322 path: videoChannelPath + '/super_channel', 318 path: videoChannelPath + '/super_channel',
323 statusCodeExpected: HttpStatusCode.OK_200 319 expectedStatus: HttpStatusCode.OK_200
324 }) 320 })
325 }) 321 })
326 }) 322 })
327 323
328 describe('When deleting a video channel', function () { 324 describe('When deleting a video channel', function () {
329 it('Should fail with a non authenticated user', async function () { 325 it('Should fail with a non authenticated user', async function () {
330 await deleteVideoChannel(server.url, 'coucou', 'super_channel', HttpStatusCode.UNAUTHORIZED_401) 326 await command.delete({ token: 'coucou', channelName: 'super_channel', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
331 }) 327 })
332 328
333 it('Should fail with another authenticated user', async function () { 329 it('Should fail with another authenticated user', async function () {
334 await deleteVideoChannel(server.url, accessTokenUser, 'super_channel', HttpStatusCode.FORBIDDEN_403) 330 await command.delete({ token: accessTokenUser, channelName: 'super_channel', expectedStatus: HttpStatusCode.FORBIDDEN_403 })
335 }) 331 })
336 332
337 it('Should fail with an unknown video channel id', async function () { 333 it('Should fail with an unknown video channel id', async function () {
338 await deleteVideoChannel(server.url, server.accessToken, 'super_channel2', HttpStatusCode.NOT_FOUND_404) 334 await command.delete({ channelName: 'super_channel2', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
339 }) 335 })
340 336
341 it('Should succeed with the correct parameters', async function () { 337 it('Should succeed with the correct parameters', async function () {
342 await deleteVideoChannel(server.url, server.accessToken, 'super_channel') 338 await command.delete({ channelName: 'super_channel' })
343 }) 339 })
344 340
345 it('Should fail to delete the last user video channel', async function () { 341 it('Should fail to delete the last user video channel', async function () {
346 await deleteVideoChannel(server.url, server.accessToken, 'root_channel', HttpStatusCode.CONFLICT_409) 342 await command.delete({ channelName: 'root_channel', expectedStatus: HttpStatusCode.CONFLICT_409 })
347 }) 343 })
348 }) 344 })
349 345
diff --git a/server/tests/api/check-params/video-comments.ts b/server/tests/api/check-params/video-comments.ts
index a38420851..2d9ee1e0d 100644
--- a/server/tests/api/check-params/video-comments.ts
+++ b/server/tests/api/check-params/video-comments.ts
@@ -2,33 +2,26 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoCreateResult } from '@shared/models'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { 5import {
6 checkBadCountPagination,
7 checkBadSortPagination,
8 checkBadStartPagination,
8 cleanupTests, 9 cleanupTests,
9 createUser, 10 createSingleServer,
10 flushAndRunServer,
11 makeDeleteRequest, 11 makeDeleteRequest,
12 makeGetRequest, 12 makeGetRequest,
13 makePostBodyRequest, 13 makePostBodyRequest,
14 ServerInfo, 14 PeerTubeServer,
15 setAccessTokensToServers, 15 setAccessTokensToServers
16 uploadVideo, 16} from '@shared/extra-utils'
17 userLogin 17import { HttpStatusCode, VideoCreateResult } from '@shared/models'
18} from '../../../../shared/extra-utils'
19import {
20 checkBadCountPagination,
21 checkBadSortPagination,
22 checkBadStartPagination
23} from '../../../../shared/extra-utils/requests/check-api-params'
24import { addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments'
25 18
26const expect = chai.expect 19const expect = chai.expect
27 20
28describe('Test video comments API validator', function () { 21describe('Test video comments API validator', function () {
29 let pathThread: string 22 let pathThread: string
30 let pathComment: string 23 let pathComment: string
31 let server: ServerInfo 24 let server: PeerTubeServer
32 let video: VideoCreateResult 25 let video: VideoCreateResult
33 let userAccessToken: string 26 let userAccessToken: string
34 let userAccessToken2: string 27 let userAccessToken2: string
@@ -39,32 +32,31 @@ describe('Test video comments API validator', function () {
39 before(async function () { 32 before(async function () {
40 this.timeout(30000) 33 this.timeout(30000)
41 34
42 server = await flushAndRunServer(1) 35 server = await createSingleServer(1)
43 36
44 await setAccessTokensToServers([ server ]) 37 await setAccessTokensToServers([ server ])
45 38
46 { 39 {
47 const res = await uploadVideo(server.url, server.accessToken, {}) 40 video = await server.videos.upload({ attributes: {} })
48 video = res.body.video
49 pathThread = '/api/v1/videos/' + video.uuid + '/comment-threads' 41 pathThread = '/api/v1/videos/' + video.uuid + '/comment-threads'
50 } 42 }
51 43
52 { 44 {
53 const res = await addVideoCommentThread(server.url, server.accessToken, video.uuid, 'coucou') 45 const created = await server.comments.createThread({ videoId: video.uuid, text: 'coucou' })
54 commentId = res.body.comment.id 46 commentId = created.id
55 pathComment = '/api/v1/videos/' + video.uuid + '/comments/' + commentId 47 pathComment = '/api/v1/videos/' + video.uuid + '/comments/' + commentId
56 } 48 }
57 49
58 { 50 {
59 const user = { username: 'user1', password: 'my super password' } 51 const user = { username: 'user1', password: 'my super password' }
60 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 52 await server.users.create({ username: user.username, password: user.password })
61 userAccessToken = await userLogin(server, user) 53 userAccessToken = await server.login.getAccessToken(user)
62 } 54 }
63 55
64 { 56 {
65 const user = { username: 'user2', password: 'my super password' } 57 const user = { username: 'user2', password: 'my super password' }
66 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 58 await server.users.create({ username: user.username, password: user.password })
67 userAccessToken2 = await userLogin(server, user) 59 userAccessToken2 = await server.login.getAccessToken(user)
68 } 60 }
69 }) 61 })
70 62
@@ -85,7 +77,7 @@ describe('Test video comments API validator', function () {
85 await makeGetRequest({ 77 await makeGetRequest({
86 url: server.url, 78 url: server.url,
87 path: '/api/v1/videos/ba708d62-e3d7-45d9-9d73-41b9097cc02d/comment-threads', 79 path: '/api/v1/videos/ba708d62-e3d7-45d9-9d73-41b9097cc02d/comment-threads',
88 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 80 expectedStatus: HttpStatusCode.NOT_FOUND_404
89 }) 81 })
90 }) 82 })
91 }) 83 })
@@ -95,7 +87,7 @@ describe('Test video comments API validator', function () {
95 await makeGetRequest({ 87 await makeGetRequest({
96 url: server.url, 88 url: server.url,
97 path: '/api/v1/videos/ba708d62-e3d7-45d9-9d73-41b9097cc02d/comment-threads/' + commentId, 89 path: '/api/v1/videos/ba708d62-e3d7-45d9-9d73-41b9097cc02d/comment-threads/' + commentId,
98 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 90 expectedStatus: HttpStatusCode.NOT_FOUND_404
99 }) 91 })
100 }) 92 })
101 93
@@ -103,7 +95,7 @@ describe('Test video comments API validator', function () {
103 await makeGetRequest({ 95 await makeGetRequest({
104 url: server.url, 96 url: server.url,
105 path: '/api/v1/videos/' + video.shortUUID + '/comment-threads/156', 97 path: '/api/v1/videos/' + video.shortUUID + '/comment-threads/156',
106 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 98 expectedStatus: HttpStatusCode.NOT_FOUND_404
107 }) 99 })
108 }) 100 })
109 101
@@ -111,7 +103,7 @@ describe('Test video comments API validator', function () {
111 await makeGetRequest({ 103 await makeGetRequest({
112 url: server.url, 104 url: server.url,
113 path: '/api/v1/videos/' + video.shortUUID + '/comment-threads/' + commentId, 105 path: '/api/v1/videos/' + video.shortUUID + '/comment-threads/' + commentId,
114 statusCodeExpected: HttpStatusCode.OK_200 106 expectedStatus: HttpStatusCode.OK_200
115 }) 107 })
116 }) 108 })
117 }) 109 })
@@ -127,7 +119,7 @@ describe('Test video comments API validator', function () {
127 path: pathThread, 119 path: pathThread,
128 token: 'none', 120 token: 'none',
129 fields, 121 fields,
130 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 122 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
131 }) 123 })
132 }) 124 })
133 125
@@ -160,7 +152,7 @@ describe('Test video comments API validator', function () {
160 path, 152 path,
161 token: server.accessToken, 153 token: server.accessToken,
162 fields, 154 fields,
163 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 155 expectedStatus: HttpStatusCode.NOT_FOUND_404
164 }) 156 })
165 }) 157 })
166 158
@@ -173,7 +165,7 @@ describe('Test video comments API validator', function () {
173 path: pathThread, 165 path: pathThread,
174 token: server.accessToken, 166 token: server.accessToken,
175 fields, 167 fields,
176 statusCodeExpected: HttpStatusCode.OK_200 168 expectedStatus: HttpStatusCode.OK_200
177 }) 169 })
178 }) 170 })
179 }) 171 })
@@ -188,7 +180,7 @@ describe('Test video comments API validator', function () {
188 path: pathComment, 180 path: pathComment,
189 token: 'none', 181 token: 'none',
190 fields, 182 fields,
191 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 183 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
192 }) 184 })
193 }) 185 })
194 186
@@ -221,7 +213,7 @@ describe('Test video comments API validator', function () {
221 path, 213 path,
222 token: server.accessToken, 214 token: server.accessToken,
223 fields, 215 fields,
224 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 216 expectedStatus: HttpStatusCode.NOT_FOUND_404
225 }) 217 })
226 }) 218 })
227 219
@@ -235,7 +227,7 @@ describe('Test video comments API validator', function () {
235 path, 227 path,
236 token: server.accessToken, 228 token: server.accessToken,
237 fields, 229 fields,
238 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 230 expectedStatus: HttpStatusCode.NOT_FOUND_404
239 }) 231 })
240 }) 232 })
241 233
@@ -248,14 +240,14 @@ describe('Test video comments API validator', function () {
248 path: pathComment, 240 path: pathComment,
249 token: server.accessToken, 241 token: server.accessToken,
250 fields, 242 fields,
251 statusCodeExpected: HttpStatusCode.OK_200 243 expectedStatus: HttpStatusCode.OK_200
252 }) 244 })
253 }) 245 })
254 }) 246 })
255 247
256 describe('When removing video comments', function () { 248 describe('When removing video comments', function () {
257 it('Should fail with a non authenticated user', async function () { 249 it('Should fail with a non authenticated user', async function () {
258 await makeDeleteRequest({ url: server.url, path: pathComment, token: 'none', statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 250 await makeDeleteRequest({ url: server.url, path: pathComment, token: 'none', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
259 }) 251 })
260 252
261 it('Should fail with another user', async function () { 253 it('Should fail with another user', async function () {
@@ -263,32 +255,32 @@ describe('Test video comments API validator', function () {
263 url: server.url, 255 url: server.url,
264 path: pathComment, 256 path: pathComment,
265 token: userAccessToken, 257 token: userAccessToken,
266 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 258 expectedStatus: HttpStatusCode.FORBIDDEN_403
267 }) 259 })
268 }) 260 })
269 261
270 it('Should fail with an incorrect video', async function () { 262 it('Should fail with an incorrect video', async function () {
271 const path = '/api/v1/videos/ba708d62-e3d7-45d9-9d73-41b9097cc02d/comments/' + commentId 263 const path = '/api/v1/videos/ba708d62-e3d7-45d9-9d73-41b9097cc02d/comments/' + commentId
272 await makeDeleteRequest({ url: server.url, path, token: server.accessToken, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) 264 await makeDeleteRequest({ url: server.url, path, token: server.accessToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
273 }) 265 })
274 266
275 it('Should fail with an incorrect comment', async function () { 267 it('Should fail with an incorrect comment', async function () {
276 const path = '/api/v1/videos/' + video.uuid + '/comments/124' 268 const path = '/api/v1/videos/' + video.uuid + '/comments/124'
277 await makeDeleteRequest({ url: server.url, path, token: server.accessToken, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) 269 await makeDeleteRequest({ url: server.url, path, token: server.accessToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
278 }) 270 })
279 271
280 it('Should succeed with the same user', async function () { 272 it('Should succeed with the same user', async function () {
281 let commentToDelete: number 273 let commentToDelete: number
282 274
283 { 275 {
284 const res = await addVideoCommentThread(server.url, userAccessToken, video.uuid, 'hello') 276 const created = await server.comments.createThread({ videoId: video.uuid, token: userAccessToken, text: 'hello' })
285 commentToDelete = res.body.comment.id 277 commentToDelete = created.id
286 } 278 }
287 279
288 const path = '/api/v1/videos/' + video.uuid + '/comments/' + commentToDelete 280 const path = '/api/v1/videos/' + video.uuid + '/comments/' + commentToDelete
289 281
290 await makeDeleteRequest({ url: server.url, path, token: userAccessToken2, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 282 await makeDeleteRequest({ url: server.url, path, token: userAccessToken2, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
291 await makeDeleteRequest({ url: server.url, path, token: userAccessToken, statusCodeExpected: HttpStatusCode.NO_CONTENT_204 }) 283 await makeDeleteRequest({ url: server.url, path, token: userAccessToken, expectedStatus: HttpStatusCode.NO_CONTENT_204 })
292 }) 284 })
293 285
294 it('Should succeed with the owner of the video', async function () { 286 it('Should succeed with the owner of the video', async function () {
@@ -296,19 +288,19 @@ describe('Test video comments API validator', function () {
296 let anotherVideoUUID: string 288 let anotherVideoUUID: string
297 289
298 { 290 {
299 const res = await uploadVideo(server.url, userAccessToken, { name: 'video' }) 291 const { uuid } = await server.videos.upload({ token: userAccessToken, attributes: { name: 'video' } })
300 anotherVideoUUID = res.body.video.uuid 292 anotherVideoUUID = uuid
301 } 293 }
302 294
303 { 295 {
304 const res = await addVideoCommentThread(server.url, server.accessToken, anotherVideoUUID, 'hello') 296 const created = await server.comments.createThread({ videoId: anotherVideoUUID, text: 'hello' })
305 commentToDelete = res.body.comment.id 297 commentToDelete = created.id
306 } 298 }
307 299
308 const path = '/api/v1/videos/' + anotherVideoUUID + '/comments/' + commentToDelete 300 const path = '/api/v1/videos/' + anotherVideoUUID + '/comments/' + commentToDelete
309 301
310 await makeDeleteRequest({ url: server.url, path, token: userAccessToken2, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 302 await makeDeleteRequest({ url: server.url, path, token: userAccessToken2, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
311 await makeDeleteRequest({ url: server.url, path, token: userAccessToken, statusCodeExpected: HttpStatusCode.NO_CONTENT_204 }) 303 await makeDeleteRequest({ url: server.url, path, token: userAccessToken, expectedStatus: HttpStatusCode.NO_CONTENT_204 })
312 }) 304 })
313 305
314 it('Should succeed with the correct parameters', async function () { 306 it('Should succeed with the correct parameters', async function () {
@@ -316,15 +308,14 @@ describe('Test video comments API validator', function () {
316 url: server.url, 308 url: server.url,
317 path: pathComment, 309 path: pathComment,
318 token: server.accessToken, 310 token: server.accessToken,
319 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 311 expectedStatus: HttpStatusCode.NO_CONTENT_204
320 }) 312 })
321 }) 313 })
322 }) 314 })
323 315
324 describe('When a video has comments disabled', function () { 316 describe('When a video has comments disabled', function () {
325 before(async function () { 317 before(async function () {
326 const res = await uploadVideo(server.url, server.accessToken, { commentsEnabled: false }) 318 video = await server.videos.upload({ attributes: { commentsEnabled: false } })
327 video = res.body.video
328 pathThread = '/api/v1/videos/' + video.uuid + '/comment-threads' 319 pathThread = '/api/v1/videos/' + video.uuid + '/comment-threads'
329 }) 320 })
330 321
@@ -332,7 +323,7 @@ describe('Test video comments API validator', function () {
332 const res = await makeGetRequest({ 323 const res = await makeGetRequest({
333 url: server.url, 324 url: server.url,
334 path: pathThread, 325 path: pathThread,
335 statusCodeExpected: HttpStatusCode.OK_200 326 expectedStatus: HttpStatusCode.OK_200
336 }) 327 })
337 expect(res.body.total).to.equal(0) 328 expect(res.body.total).to.equal(0)
338 expect(res.body.data).to.have.lengthOf(0) 329 expect(res.body.data).to.have.lengthOf(0)
@@ -349,7 +340,7 @@ describe('Test video comments API validator', function () {
349 path: pathThread, 340 path: pathThread,
350 token: server.accessToken, 341 token: server.accessToken,
351 fields, 342 fields,
352 statusCodeExpected: HttpStatusCode.CONFLICT_409 343 expectedStatus: HttpStatusCode.CONFLICT_409
353 }) 344 })
354 }) 345 })
355 346
@@ -375,7 +366,7 @@ describe('Test video comments API validator', function () {
375 await makeGetRequest({ 366 await makeGetRequest({
376 url: server.url, 367 url: server.url,
377 path, 368 path,
378 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 369 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
379 }) 370 })
380 }) 371 })
381 372
@@ -384,7 +375,7 @@ describe('Test video comments API validator', function () {
384 url: server.url, 375 url: server.url,
385 path, 376 path,
386 token: userAccessToken, 377 token: userAccessToken,
387 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 378 expectedStatus: HttpStatusCode.FORBIDDEN_403
388 }) 379 })
389 }) 380 })
390 381
@@ -399,7 +390,7 @@ describe('Test video comments API validator', function () {
399 searchAccount: 'toto', 390 searchAccount: 'toto',
400 searchVideo: 'toto' 391 searchVideo: 'toto'
401 }, 392 },
402 statusCodeExpected: HttpStatusCode.OK_200 393 expectedStatus: HttpStatusCode.OK_200
403 }) 394 })
404 }) 395 })
405 }) 396 })
diff --git a/server/tests/api/check-params/video-imports.ts b/server/tests/api/check-params/video-imports.ts
index a27b624d0..d6d745488 100644
--- a/server/tests/api/check-params/video-imports.ts
+++ b/server/tests/api/check-params/video-imports.ts
@@ -2,33 +2,25 @@
2 2
3import 'mocha' 3import 'mocha'
4import { omit } from 'lodash' 4import { omit } from 'lodash'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6import { 5import {
7 buildAbsoluteFixturePath, 6 buildAbsoluteFixturePath,
7 checkBadCountPagination,
8 checkBadSortPagination,
9 checkBadStartPagination,
8 cleanupTests, 10 cleanupTests,
9 createUser, 11 createSingleServer,
10 flushAndRunServer, 12 FIXTURE_URLS,
11 getMyUserInformation,
12 immutableAssign,
13 makeGetRequest, 13 makeGetRequest,
14 makePostBodyRequest, 14 makePostBodyRequest,
15 makeUploadRequest, 15 makeUploadRequest,
16 ServerInfo, 16 PeerTubeServer,
17 setAccessTokensToServers, 17 setAccessTokensToServers
18 updateCustomSubConfig, 18} from '@shared/extra-utils'
19 userLogin 19import { HttpStatusCode, VideoPrivacy } from '@shared/models'
20} from '../../../../shared/extra-utils'
21import {
22 checkBadCountPagination,
23 checkBadSortPagination,
24 checkBadStartPagination
25} from '../../../../shared/extra-utils/requests/check-api-params'
26import { getGoodVideoUrl, getMagnetURI } from '../../../../shared/extra-utils/videos/video-imports'
27import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
28 20
29describe('Test video imports API validator', function () { 21describe('Test video imports API validator', function () {
30 const path = '/api/v1/videos/imports' 22 const path = '/api/v1/videos/imports'
31 let server: ServerInfo 23 let server: PeerTubeServer
32 let userAccessToken = '' 24 let userAccessToken = ''
33 let channelId: number 25 let channelId: number
34 26
@@ -37,18 +29,18 @@ describe('Test video imports API validator', function () {
37 before(async function () { 29 before(async function () {
38 this.timeout(30000) 30 this.timeout(30000)
39 31
40 server = await flushAndRunServer(1) 32 server = await createSingleServer(1)
41 33
42 await setAccessTokensToServers([ server ]) 34 await setAccessTokensToServers([ server ])
43 35
44 const username = 'user1' 36 const username = 'user1'
45 const password = 'my super password' 37 const password = 'my super password'
46 await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) 38 await server.users.create({ username: username, password: password })
47 userAccessToken = await userLogin(server, { username, password }) 39 userAccessToken = await server.login.getAccessToken({ username, password })
48 40
49 { 41 {
50 const res = await getMyUserInformation(server.url, server.accessToken) 42 const { videoChannels } = await server.users.getMyInfo()
51 channelId = res.body.videoChannels[0].id 43 channelId = videoChannels[0].id
52 } 44 }
53 }) 45 })
54 46
@@ -68,7 +60,7 @@ describe('Test video imports API validator', function () {
68 }) 60 })
69 61
70 it('Should success with the correct parameters', async function () { 62 it('Should success with the correct parameters', async function () {
71 await makeGetRequest({ url: server.url, path: myPath, statusCodeExpected: HttpStatusCode.OK_200, token: server.accessToken }) 63 await makeGetRequest({ url: server.url, path: myPath, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken })
72 }) 64 })
73 }) 65 })
74 66
@@ -77,7 +69,7 @@ describe('Test video imports API validator', function () {
77 69
78 before(function () { 70 before(function () {
79 baseCorrectParams = { 71 baseCorrectParams = {
80 targetUrl: getGoodVideoUrl(), 72 targetUrl: FIXTURE_URLS.goodVideo,
81 name: 'my super name', 73 name: 'my super name',
82 category: 5, 74 category: 5,
83 licence: 1, 75 licence: 1,
@@ -106,48 +98,48 @@ describe('Test video imports API validator', function () {
106 path, 98 path,
107 token: server.accessToken, 99 token: server.accessToken,
108 fields, 100 fields,
109 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 101 expectedStatus: HttpStatusCode.BAD_REQUEST_400
110 }) 102 })
111 }) 103 })
112 104
113 it('Should fail with a bad target url', async function () { 105 it('Should fail with a bad target url', async function () {
114 const fields = immutableAssign(baseCorrectParams, { targetUrl: 'htt://hello' }) 106 const fields = { ...baseCorrectParams, targetUrl: 'htt://hello' }
115 107
116 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 108 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
117 }) 109 })
118 110
119 it('Should fail with a long name', async function () { 111 it('Should fail with a long name', async function () {
120 const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(65) }) 112 const fields = { ...baseCorrectParams, name: 'super'.repeat(65) }
121 113
122 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 114 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
123 }) 115 })
124 116
125 it('Should fail with a bad category', async function () { 117 it('Should fail with a bad category', async function () {
126 const fields = immutableAssign(baseCorrectParams, { category: 125 }) 118 const fields = { ...baseCorrectParams, category: 125 }
127 119
128 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 120 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
129 }) 121 })
130 122
131 it('Should fail with a bad licence', async function () { 123 it('Should fail with a bad licence', async function () {
132 const fields = immutableAssign(baseCorrectParams, { licence: 125 }) 124 const fields = { ...baseCorrectParams, licence: 125 }
133 125
134 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 126 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
135 }) 127 })
136 128
137 it('Should fail with a bad language', async function () { 129 it('Should fail with a bad language', async function () {
138 const fields = immutableAssign(baseCorrectParams, { language: 'a'.repeat(15) }) 130 const fields = { ...baseCorrectParams, language: 'a'.repeat(15) }
139 131
140 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 132 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
141 }) 133 })
142 134
143 it('Should fail with a long description', async function () { 135 it('Should fail with a long description', async function () {
144 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) }) 136 const fields = { ...baseCorrectParams, description: 'super'.repeat(2500) }
145 137
146 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 138 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
147 }) 139 })
148 140
149 it('Should fail with a long support text', async function () { 141 it('Should fail with a long support text', async function () {
150 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) }) 142 const fields = { ...baseCorrectParams, support: 'super'.repeat(201) }
151 143
152 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 144 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
153 }) 145 })
@@ -159,7 +151,7 @@ describe('Test video imports API validator', function () {
159 }) 151 })
160 152
161 it('Should fail with a bad channel', async function () { 153 it('Should fail with a bad channel', async function () {
162 const fields = immutableAssign(baseCorrectParams, { channelId: 545454 }) 154 const fields = { ...baseCorrectParams, channelId: 545454 }
163 155
164 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 156 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
165 }) 157 })
@@ -169,31 +161,31 @@ describe('Test video imports API validator', function () {
169 username: 'fake', 161 username: 'fake',
170 password: 'fake_password' 162 password: 'fake_password'
171 } 163 }
172 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 164 await server.users.create({ username: user.username, password: user.password })
173 165
174 const accessTokenUser = await userLogin(server, user) 166 const accessTokenUser = await server.login.getAccessToken(user)
175 const res = await getMyUserInformation(server.url, accessTokenUser) 167 const { videoChannels } = await server.users.getMyInfo({ token: accessTokenUser })
176 const customChannelId = res.body.videoChannels[0].id 168 const customChannelId = videoChannels[0].id
177 169
178 const fields = immutableAssign(baseCorrectParams, { channelId: customChannelId }) 170 const fields = { ...baseCorrectParams, channelId: customChannelId }
179 171
180 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 172 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields })
181 }) 173 })
182 174
183 it('Should fail with too many tags', async function () { 175 it('Should fail with too many tags', async function () {
184 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }) 176 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }
185 177
186 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 178 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
187 }) 179 })
188 180
189 it('Should fail with a tag length too low', async function () { 181 it('Should fail with a tag length too low', async function () {
190 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 't' ] }) 182 const fields = { ...baseCorrectParams, tags: [ 'tag1', 't' ] }
191 183
192 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 184 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
193 }) 185 })
194 186
195 it('Should fail with a tag length too big', async function () { 187 it('Should fail with a tag length too big', async function () {
196 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }) 188 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }
197 189
198 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 190 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
199 }) 191 })
@@ -245,7 +237,7 @@ describe('Test video imports API validator', function () {
245 237
246 it('Should fail with an invalid magnet URI', async function () { 238 it('Should fail with an invalid magnet URI', async function () {
247 let fields = omit(baseCorrectParams, 'targetUrl') 239 let fields = omit(baseCorrectParams, 'targetUrl')
248 fields = immutableAssign(fields, { magnetUri: 'blabla' }) 240 fields = { ...fields, magnetUri: 'blabla' }
249 241
250 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 242 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
251 }) 243 })
@@ -258,19 +250,21 @@ describe('Test video imports API validator', function () {
258 path, 250 path,
259 token: server.accessToken, 251 token: server.accessToken,
260 fields: baseCorrectParams, 252 fields: baseCorrectParams,
261 statusCodeExpected: HttpStatusCode.OK_200 253 expectedStatus: HttpStatusCode.OK_200
262 }) 254 })
263 }) 255 })
264 256
265 it('Should forbid to import http videos', async function () { 257 it('Should forbid to import http videos', async function () {
266 await updateCustomSubConfig(server.url, server.accessToken, { 258 await server.config.updateCustomSubConfig({
267 import: { 259 newConfig: {
268 videos: { 260 import: {
269 http: { 261 videos: {
270 enabled: false 262 http: {
271 }, 263 enabled: false
272 torrent: { 264 },
273 enabled: true 265 torrent: {
266 enabled: true
267 }
274 } 268 }
275 } 269 }
276 } 270 }
@@ -281,33 +275,35 @@ describe('Test video imports API validator', function () {
281 path, 275 path,
282 token: server.accessToken, 276 token: server.accessToken,
283 fields: baseCorrectParams, 277 fields: baseCorrectParams,
284 statusCodeExpected: HttpStatusCode.CONFLICT_409 278 expectedStatus: HttpStatusCode.CONFLICT_409
285 }) 279 })
286 }) 280 })
287 281
288 it('Should forbid to import torrent videos', async function () { 282 it('Should forbid to import torrent videos', async function () {
289 await updateCustomSubConfig(server.url, server.accessToken, { 283 await server.config.updateCustomSubConfig({
290 import: { 284 newConfig: {
291 videos: { 285 import: {
292 http: { 286 videos: {
293 enabled: true 287 http: {
294 }, 288 enabled: true
295 torrent: { 289 },
296 enabled: false 290 torrent: {
291 enabled: false
292 }
297 } 293 }
298 } 294 }
299 } 295 }
300 }) 296 })
301 297
302 let fields = omit(baseCorrectParams, 'targetUrl') 298 let fields = omit(baseCorrectParams, 'targetUrl')
303 fields = immutableAssign(fields, { magnetUri: getMagnetURI() }) 299 fields = { ...fields, magnetUri: FIXTURE_URLS.magnet }
304 300
305 await makePostBodyRequest({ 301 await makePostBodyRequest({
306 url: server.url, 302 url: server.url,
307 path, 303 path,
308 token: server.accessToken, 304 token: server.accessToken,
309 fields, 305 fields,
310 statusCodeExpected: HttpStatusCode.CONFLICT_409 306 expectedStatus: HttpStatusCode.CONFLICT_409
311 }) 307 })
312 308
313 fields = omit(fields, 'magnetUri') 309 fields = omit(fields, 'magnetUri')
@@ -321,7 +317,7 @@ describe('Test video imports API validator', function () {
321 token: server.accessToken, 317 token: server.accessToken,
322 fields, 318 fields,
323 attaches, 319 attaches,
324 statusCodeExpected: HttpStatusCode.CONFLICT_409 320 expectedStatus: HttpStatusCode.CONFLICT_409
325 }) 321 })
326 }) 322 })
327 }) 323 })
diff --git a/server/tests/api/check-params/video-playlists.ts b/server/tests/api/check-params/video-playlists.ts
index 18253d11a..e4d541b48 100644
--- a/server/tests/api/check-params/video-playlists.ts
+++ b/server/tests/api/check-params/video-playlists.ts
@@ -1,34 +1,31 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { VideoPlaylistCreateResult, VideoPlaylistPrivacy, VideoPlaylistType } from '@shared/models'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6import { 4import {
7 addVideoInPlaylist,
8 checkBadCountPagination, 5 checkBadCountPagination,
9 checkBadSortPagination, 6 checkBadSortPagination,
10 checkBadStartPagination, 7 checkBadStartPagination,
11 cleanupTests, 8 cleanupTests,
12 createVideoPlaylist, 9 createSingleServer,
13 deleteVideoPlaylist,
14 flushAndRunServer,
15 generateUserAccessToken,
16 getAccountPlaylistsListWithToken,
17 getVideoPlaylist,
18 immutableAssign,
19 makeGetRequest, 10 makeGetRequest,
20 removeVideoFromPlaylist, 11 PeerTubeServer,
21 reorderVideosPlaylist, 12 PlaylistsCommand,
22 ServerInfo,
23 setAccessTokensToServers, 13 setAccessTokensToServers,
24 setDefaultVideoChannel, 14 setDefaultVideoChannel
25 updateVideoPlaylist, 15} from '@shared/extra-utils'
26 updateVideoPlaylistElement, 16import {
27 uploadVideoAndGetId 17 HttpStatusCode,
28} from '../../../../shared/extra-utils' 18 VideoPlaylistCreate,
19 VideoPlaylistCreateResult,
20 VideoPlaylistElementCreate,
21 VideoPlaylistElementUpdate,
22 VideoPlaylistPrivacy,
23 VideoPlaylistReorder,
24 VideoPlaylistType
25} from '@shared/models'
29 26
30describe('Test video playlists API validator', function () { 27describe('Test video playlists API validator', function () {
31 let server: ServerInfo 28 let server: PeerTubeServer
32 let userAccessToken: string 29 let userAccessToken: string
33 30
34 let playlist: VideoPlaylistCreateResult 31 let playlist: VideoPlaylistCreateResult
@@ -36,49 +33,54 @@ describe('Test video playlists API validator', function () {
36 33
37 let watchLaterPlaylistId: number 34 let watchLaterPlaylistId: number
38 let videoId: number 35 let videoId: number
39 let playlistElementId: number 36 let elementId: number
37
38 let command: PlaylistsCommand
40 39
41 // --------------------------------------------------------------- 40 // ---------------------------------------------------------------
42 41
43 before(async function () { 42 before(async function () {
44 this.timeout(30000) 43 this.timeout(30000)
45 44
46 server = await flushAndRunServer(1) 45 server = await createSingleServer(1)
47 46
48 await setAccessTokensToServers([ server ]) 47 await setAccessTokensToServers([ server ])
49 await setDefaultVideoChannel([ server ]) 48 await setDefaultVideoChannel([ server ])
50 49
51 userAccessToken = await generateUserAccessToken(server, 'user1') 50 userAccessToken = await server.users.generateUserAndToken('user1')
52 videoId = (await uploadVideoAndGetId({ server, videoName: 'video 1' })).id 51 videoId = (await server.videos.quickUpload({ name: 'video 1' })).id
52
53 command = server.playlists
53 54
54 { 55 {
55 const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, VideoPlaylistType.WATCH_LATER) 56 const { data } = await command.listByAccount({
56 watchLaterPlaylistId = res.body.data[0].id 57 token: server.accessToken,
58 handle: 'root',
59 start: 0,
60 count: 5,
61 playlistType: VideoPlaylistType.WATCH_LATER
62 })
63 watchLaterPlaylistId = data[0].id
57 } 64 }
58 65
59 { 66 {
60 const res = await createVideoPlaylist({ 67 playlist = await command.create({
61 url: server.url, 68 attributes: {
62 token: server.accessToken,
63 playlistAttrs: {
64 displayName: 'super playlist', 69 displayName: 'super playlist',
65 privacy: VideoPlaylistPrivacy.PUBLIC, 70 privacy: VideoPlaylistPrivacy.PUBLIC,
66 videoChannelId: server.videoChannel.id 71 videoChannelId: server.store.channel.id
67 } 72 }
68 }) 73 })
69 playlist = res.body.videoPlaylist
70 } 74 }
71 75
72 { 76 {
73 const res = await createVideoPlaylist({ 77 const created = await command.create({
74 url: server.url, 78 attributes: {
75 token: server.accessToken,
76 playlistAttrs: {
77 displayName: 'private', 79 displayName: 'private',
78 privacy: VideoPlaylistPrivacy.PRIVATE 80 privacy: VideoPlaylistPrivacy.PRIVATE
79 } 81 }
80 }) 82 })
81 privatePlaylistUUID = res.body.videoPlaylist.uuid 83 privatePlaylistUUID = created.uuid
82 } 84 }
83 }) 85 })
84 86
@@ -117,7 +119,7 @@ describe('Test video playlists API validator', function () {
117 await makeGetRequest({ 119 await makeGetRequest({
118 url: server.url, 120 url: server.url,
119 path: accountPath, 121 path: accountPath,
120 statusCodeExpected: HttpStatusCode.NOT_FOUND_404, 122 expectedStatus: HttpStatusCode.NOT_FOUND_404,
121 token: server.accessToken 123 token: server.accessToken
122 }) 124 })
123 }) 125 })
@@ -128,18 +130,18 @@ describe('Test video playlists API validator', function () {
128 await makeGetRequest({ 130 await makeGetRequest({
129 url: server.url, 131 url: server.url,
130 path: accountPath, 132 path: accountPath,
131 statusCodeExpected: HttpStatusCode.NOT_FOUND_404, 133 expectedStatus: HttpStatusCode.NOT_FOUND_404,
132 token: server.accessToken 134 token: server.accessToken
133 }) 135 })
134 }) 136 })
135 137
136 it('Should success with the correct parameters', async function () { 138 it('Should success with the correct parameters', async function () {
137 await makeGetRequest({ url: server.url, path: globalPath, statusCodeExpected: HttpStatusCode.OK_200, token: server.accessToken }) 139 await makeGetRequest({ url: server.url, path: globalPath, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken })
138 await makeGetRequest({ url: server.url, path: accountPath, statusCodeExpected: HttpStatusCode.OK_200, token: server.accessToken }) 140 await makeGetRequest({ url: server.url, path: accountPath, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken })
139 await makeGetRequest({ 141 await makeGetRequest({
140 url: server.url, 142 url: server.url,
141 path: videoChannelPath, 143 path: videoChannelPath,
142 statusCodeExpected: HttpStatusCode.OK_200, 144 expectedStatus: HttpStatusCode.OK_200,
143 token: server.accessToken 145 token: server.accessToken
144 }) 146 })
145 }) 147 })
@@ -157,141 +159,144 @@ describe('Test video playlists API validator', function () {
157 }) 159 })
158 160
159 it('Should success with the correct parameters', async function () { 161 it('Should success with the correct parameters', async function () {
160 await makeGetRequest({ url: server.url, path: path + playlist.shortUUID + '/videos', statusCodeExpected: HttpStatusCode.OK_200 }) 162 await makeGetRequest({ url: server.url, path: path + playlist.shortUUID + '/videos', expectedStatus: HttpStatusCode.OK_200 })
161 }) 163 })
162 }) 164 })
163 165
164 describe('When getting a video playlist', function () { 166 describe('When getting a video playlist', function () {
165 it('Should fail with a bad id or uuid', async function () { 167 it('Should fail with a bad id or uuid', async function () {
166 await getVideoPlaylist(server.url, 'toto', HttpStatusCode.BAD_REQUEST_400) 168 await command.get({ playlistId: 'toto', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
167 }) 169 })
168 170
169 it('Should fail with an unknown playlist', async function () { 171 it('Should fail with an unknown playlist', async function () {
170 await getVideoPlaylist(server.url, 42, HttpStatusCode.NOT_FOUND_404) 172 await command.get({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
171 }) 173 })
172 174
173 it('Should fail to get an unlisted playlist with the number id', async function () { 175 it('Should fail to get an unlisted playlist with the number id', async function () {
174 const res = await createVideoPlaylist({ 176 const playlist = await command.create({
175 url: server.url, 177 attributes: {
176 token: server.accessToken,
177 playlistAttrs: {
178 displayName: 'super playlist', 178 displayName: 'super playlist',
179 videoChannelId: server.videoChannel.id, 179 videoChannelId: server.store.channel.id,
180 privacy: VideoPlaylistPrivacy.UNLISTED 180 privacy: VideoPlaylistPrivacy.UNLISTED
181 } 181 }
182 }) 182 })
183 const playlist = res.body.videoPlaylist
184 183
185 await getVideoPlaylist(server.url, playlist.id, HttpStatusCode.NOT_FOUND_404) 184 await command.get({ playlistId: playlist.id, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
186 await getVideoPlaylist(server.url, playlist.uuid, HttpStatusCode.OK_200) 185 await command.get({ playlistId: playlist.uuid, expectedStatus: HttpStatusCode.OK_200 })
187 }) 186 })
188 187
189 it('Should succeed with the correct params', async function () { 188 it('Should succeed with the correct params', async function () {
190 await getVideoPlaylist(server.url, playlist.uuid, HttpStatusCode.OK_200) 189 await command.get({ playlistId: playlist.uuid, expectedStatus: HttpStatusCode.OK_200 })
191 }) 190 })
192 }) 191 })
193 192
194 describe('When creating/updating a video playlist', function () { 193 describe('When creating/updating a video playlist', function () {
195 const getBase = (playlistAttrs: any = {}, wrapper: any = {}) => { 194 const getBase = (
196 return Object.assign({ 195 attributes?: Partial<VideoPlaylistCreate>,
197 expectedStatus: HttpStatusCode.BAD_REQUEST_400, 196 wrapper?: Partial<Parameters<PlaylistsCommand['create']>[0]>
198 url: server.url, 197 ) => {
199 token: server.accessToken, 198 return {
200 playlistAttrs: Object.assign({ 199 attributes: {
201 displayName: 'display name', 200 displayName: 'display name',
202 privacy: VideoPlaylistPrivacy.UNLISTED, 201 privacy: VideoPlaylistPrivacy.UNLISTED,
203 thumbnailfile: 'thumbnail.jpg', 202 thumbnailfile: 'thumbnail.jpg',
204 videoChannelId: server.videoChannel.id 203 videoChannelId: server.store.channel.id,
205 }, playlistAttrs) 204
206 }, wrapper) 205 ...attributes
206 },
207
208 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
209
210 ...wrapper
211 }
207 } 212 }
208 const getUpdate = (params: any, playlistId: number | string) => { 213 const getUpdate = (params: any, playlistId: number | string) => {
209 return immutableAssign(params, { playlistId: playlistId }) 214 return { ...params, playlistId: playlistId }
210 } 215 }
211 216
212 it('Should fail with an unauthenticated user', async function () { 217 it('Should fail with an unauthenticated user', async function () {
213 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) 218 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
214 219
215 await createVideoPlaylist(params) 220 await command.create(params)
216 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 221 await command.update(getUpdate(params, playlist.shortUUID))
217 }) 222 })
218 223
219 it('Should fail without displayName', async function () { 224 it('Should fail without displayName', async function () {
220 const params = getBase({ displayName: undefined }) 225 const params = getBase({ displayName: undefined })
221 226
222 await createVideoPlaylist(params) 227 await command.create(params)
223 }) 228 })
224 229
225 it('Should fail with an incorrect display name', async function () { 230 it('Should fail with an incorrect display name', async function () {
226 const params = getBase({ displayName: 's'.repeat(300) }) 231 const params = getBase({ displayName: 's'.repeat(300) })
227 232
228 await createVideoPlaylist(params) 233 await command.create(params)
229 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 234 await command.update(getUpdate(params, playlist.shortUUID))
230 }) 235 })
231 236
232 it('Should fail with an incorrect description', async function () { 237 it('Should fail with an incorrect description', async function () {
233 const params = getBase({ description: 't' }) 238 const params = getBase({ description: 't' })
234 239
235 await createVideoPlaylist(params) 240 await command.create(params)
236 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 241 await command.update(getUpdate(params, playlist.shortUUID))
237 }) 242 })
238 243
239 it('Should fail with an incorrect privacy', async function () { 244 it('Should fail with an incorrect privacy', async function () {
240 const params = getBase({ privacy: 45 }) 245 const params = getBase({ privacy: 45 })
241 246
242 await createVideoPlaylist(params) 247 await command.create(params)
243 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 248 await command.update(getUpdate(params, playlist.shortUUID))
244 }) 249 })
245 250
246 it('Should fail with an unknown video channel id', async function () { 251 it('Should fail with an unknown video channel id', async function () {
247 const params = getBase({ videoChannelId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 252 const params = getBase({ videoChannelId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 })
248 253
249 await createVideoPlaylist(params) 254 await command.create(params)
250 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 255 await command.update(getUpdate(params, playlist.shortUUID))
251 }) 256 })
252 257
253 it('Should fail with an incorrect thumbnail file', async function () { 258 it('Should fail with an incorrect thumbnail file', async function () {
254 const params = getBase({ thumbnailfile: 'video_short.mp4' }) 259 const params = getBase({ thumbnailfile: 'video_short.mp4' })
255 260
256 await createVideoPlaylist(params) 261 await command.create(params)
257 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 262 await command.update(getUpdate(params, playlist.shortUUID))
258 }) 263 })
259 264
260 it('Should fail with a thumbnail file too big', async function () { 265 it('Should fail with a thumbnail file too big', async function () {
261 const params = getBase({ thumbnailfile: 'preview-big.png' }) 266 const params = getBase({ thumbnailfile: 'preview-big.png' })
262 267
263 await createVideoPlaylist(params) 268 await command.create(params)
264 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 269 await command.update(getUpdate(params, playlist.shortUUID))
265 }) 270 })
266 271
267 it('Should fail to set "public" a playlist not assigned to a channel', async function () { 272 it('Should fail to set "public" a playlist not assigned to a channel', async function () {
268 const params = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: undefined }) 273 const params = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: undefined })
269 const params2 = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: 'null' }) 274 const params2 = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: 'null' as any })
270 const params3 = getBase({ privacy: undefined, videoChannelId: 'null' }) 275 const params3 = getBase({ privacy: undefined, videoChannelId: 'null' as any })
271 276
272 await createVideoPlaylist(params) 277 await command.create(params)
273 await createVideoPlaylist(params2) 278 await command.create(params2)
274 await updateVideoPlaylist(getUpdate(params, privatePlaylistUUID)) 279 await command.update(getUpdate(params, privatePlaylistUUID))
275 await updateVideoPlaylist(getUpdate(params2, playlist.shortUUID)) 280 await command.update(getUpdate(params2, playlist.shortUUID))
276 await updateVideoPlaylist(getUpdate(params3, playlist.shortUUID)) 281 await command.update(getUpdate(params3, playlist.shortUUID))
277 }) 282 })
278 283
279 it('Should fail with an unknown playlist to update', async function () { 284 it('Should fail with an unknown playlist to update', async function () {
280 await updateVideoPlaylist(getUpdate( 285 await command.update(getUpdate(
281 getBase({}, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }), 286 getBase({}, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }),
282 42 287 42
283 )) 288 ))
284 }) 289 })
285 290
286 it('Should fail to update a playlist of another user', async function () { 291 it('Should fail to update a playlist of another user', async function () {
287 await updateVideoPlaylist(getUpdate( 292 await command.update(getUpdate(
288 getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }), 293 getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }),
289 playlist.shortUUID 294 playlist.shortUUID
290 )) 295 ))
291 }) 296 })
292 297
293 it('Should fail to update the watch later playlist', async function () { 298 it('Should fail to update the watch later playlist', async function () {
294 await updateVideoPlaylist(getUpdate( 299 await command.update(getUpdate(
295 getBase({}, { expectedStatus: HttpStatusCode.BAD_REQUEST_400 }), 300 getBase({}, { expectedStatus: HttpStatusCode.BAD_REQUEST_400 }),
296 watchLaterPlaylistId 301 watchLaterPlaylistId
297 )) 302 ))
@@ -300,146 +305,158 @@ describe('Test video playlists API validator', function () {
300 it('Should succeed with the correct params', async function () { 305 it('Should succeed with the correct params', async function () {
301 { 306 {
302 const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 }) 307 const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 })
303 await createVideoPlaylist(params) 308 await command.create(params)
304 } 309 }
305 310
306 { 311 {
307 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 }) 312 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
308 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 313 await command.update(getUpdate(params, playlist.shortUUID))
309 } 314 }
310 }) 315 })
311 }) 316 })
312 317
313 describe('When adding an element in a playlist', function () { 318 describe('When adding an element in a playlist', function () {
314 const getBase = (elementAttrs: any = {}, wrapper: any = {}) => { 319 const getBase = (
315 return Object.assign({ 320 attributes?: Partial<VideoPlaylistElementCreate>,
316 expectedStatus: HttpStatusCode.BAD_REQUEST_400, 321 wrapper?: Partial<Parameters<PlaylistsCommand['addElement']>[0]>
317 url: server.url, 322 ) => {
318 token: server.accessToken, 323 return {
319 playlistId: playlist.id, 324 attributes: {
320 elementAttrs: Object.assign({
321 videoId, 325 videoId,
322 startTimestamp: 2, 326 startTimestamp: 2,
323 stopTimestamp: 3 327 stopTimestamp: 3,
324 }, elementAttrs) 328
325 }, wrapper) 329 ...attributes
330 },
331
332 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
333 playlistId: playlist.id,
334
335 ...wrapper
336 }
326 } 337 }
327 338
328 it('Should fail with an unauthenticated user', async function () { 339 it('Should fail with an unauthenticated user', async function () {
329 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) 340 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
330 await addVideoInPlaylist(params) 341 await command.addElement(params)
331 }) 342 })
332 343
333 it('Should fail with the playlist of another user', async function () { 344 it('Should fail with the playlist of another user', async function () {
334 const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 345 const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
335 await addVideoInPlaylist(params) 346 await command.addElement(params)
336 }) 347 })
337 348
338 it('Should fail with an unknown or incorrect playlist id', async function () { 349 it('Should fail with an unknown or incorrect playlist id', async function () {
339 { 350 {
340 const params = getBase({}, { playlistId: 'toto' }) 351 const params = getBase({}, { playlistId: 'toto' })
341 await addVideoInPlaylist(params) 352 await command.addElement(params)
342 } 353 }
343 354
344 { 355 {
345 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 356 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
346 await addVideoInPlaylist(params) 357 await command.addElement(params)
347 } 358 }
348 }) 359 })
349 360
350 it('Should fail with an unknown or incorrect video id', async function () { 361 it('Should fail with an unknown or incorrect video id', async function () {
351 const params = getBase({ videoId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 362 const params = getBase({ videoId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 })
352 await addVideoInPlaylist(params) 363 await command.addElement(params)
353 }) 364 })
354 365
355 it('Should fail with a bad start/stop timestamp', async function () { 366 it('Should fail with a bad start/stop timestamp', async function () {
356 { 367 {
357 const params = getBase({ startTimestamp: -42 }) 368 const params = getBase({ startTimestamp: -42 })
358 await addVideoInPlaylist(params) 369 await command.addElement(params)
359 } 370 }
360 371
361 { 372 {
362 const params = getBase({ stopTimestamp: 'toto' as any }) 373 const params = getBase({ stopTimestamp: 'toto' as any })
363 await addVideoInPlaylist(params) 374 await command.addElement(params)
364 } 375 }
365 }) 376 })
366 377
367 it('Succeed with the correct params', async function () { 378 it('Succeed with the correct params', async function () {
368 const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 }) 379 const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 })
369 const res = await addVideoInPlaylist(params) 380 const created = await command.addElement(params)
370 playlistElementId = res.body.videoPlaylistElement.id 381 elementId = created.id
371 }) 382 })
372 }) 383 })
373 384
374 describe('When updating an element in a playlist', function () { 385 describe('When updating an element in a playlist', function () {
375 const getBase = (elementAttrs: any = {}, wrapper: any = {}) => { 386 const getBase = (
376 return Object.assign({ 387 attributes?: Partial<VideoPlaylistElementUpdate>,
377 url: server.url, 388 wrapper?: Partial<Parameters<PlaylistsCommand['updateElement']>[0]>
378 token: server.accessToken, 389 ) => {
379 elementAttrs: Object.assign({ 390 return {
391 attributes: {
380 startTimestamp: 1, 392 startTimestamp: 1,
381 stopTimestamp: 2 393 stopTimestamp: 2,
382 }, elementAttrs), 394
383 playlistElementId, 395 ...attributes
396 },
397
398 elementId,
384 playlistId: playlist.id, 399 playlistId: playlist.id,
385 expectedStatus: HttpStatusCode.BAD_REQUEST_400 400 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
386 }, wrapper) 401
402 ...wrapper
403 }
387 } 404 }
388 405
389 it('Should fail with an unauthenticated user', async function () { 406 it('Should fail with an unauthenticated user', async function () {
390 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) 407 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
391 await updateVideoPlaylistElement(params) 408 await command.updateElement(params)
392 }) 409 })
393 410
394 it('Should fail with the playlist of another user', async function () { 411 it('Should fail with the playlist of another user', async function () {
395 const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 412 const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
396 await updateVideoPlaylistElement(params) 413 await command.updateElement(params)
397 }) 414 })
398 415
399 it('Should fail with an unknown or incorrect playlist id', async function () { 416 it('Should fail with an unknown or incorrect playlist id', async function () {
400 { 417 {
401 const params = getBase({}, { playlistId: 'toto' }) 418 const params = getBase({}, { playlistId: 'toto' })
402 await updateVideoPlaylistElement(params) 419 await command.updateElement(params)
403 } 420 }
404 421
405 { 422 {
406 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 423 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
407 await updateVideoPlaylistElement(params) 424 await command.updateElement(params)
408 } 425 }
409 }) 426 })
410 427
411 it('Should fail with an unknown or incorrect playlistElement id', async function () { 428 it('Should fail with an unknown or incorrect playlistElement id', async function () {
412 { 429 {
413 const params = getBase({}, { playlistElementId: 'toto' }) 430 const params = getBase({}, { elementId: 'toto' })
414 await updateVideoPlaylistElement(params) 431 await command.updateElement(params)
415 } 432 }
416 433
417 { 434 {
418 const params = getBase({}, { playlistElementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 435 const params = getBase({}, { elementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
419 await updateVideoPlaylistElement(params) 436 await command.updateElement(params)
420 } 437 }
421 }) 438 })
422 439
423 it('Should fail with a bad start/stop timestamp', async function () { 440 it('Should fail with a bad start/stop timestamp', async function () {
424 { 441 {
425 const params = getBase({ startTimestamp: 'toto' as any }) 442 const params = getBase({ startTimestamp: 'toto' as any })
426 await updateVideoPlaylistElement(params) 443 await command.updateElement(params)
427 } 444 }
428 445
429 { 446 {
430 const params = getBase({ stopTimestamp: -42 }) 447 const params = getBase({ stopTimestamp: -42 })
431 await updateVideoPlaylistElement(params) 448 await command.updateElement(params)
432 } 449 }
433 }) 450 })
434 451
435 it('Should fail with an unknown element', async function () { 452 it('Should fail with an unknown element', async function () {
436 const params = getBase({}, { playlistElementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 453 const params = getBase({}, { elementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
437 await updateVideoPlaylistElement(params) 454 await command.updateElement(params)
438 }) 455 })
439 456
440 it('Succeed with the correct params', async function () { 457 it('Succeed with the correct params', async function () {
441 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 }) 458 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
442 await updateVideoPlaylistElement(params) 459 await command.updateElement(params)
443 }) 460 })
444 }) 461 })
445 462
@@ -447,110 +464,111 @@ describe('Test video playlists API validator', function () {
447 let videoId3: number 464 let videoId3: number
448 let videoId4: number 465 let videoId4: number
449 466
450 const getBase = (elementAttrs: any = {}, wrapper: any = {}) => { 467 const getBase = (
451 return Object.assign({ 468 attributes?: Partial<VideoPlaylistReorder>,
452 url: server.url, 469 wrapper?: Partial<Parameters<PlaylistsCommand['reorderElements']>[0]>
453 token: server.accessToken, 470 ) => {
454 playlistId: playlist.shortUUID, 471 return {
455 elementAttrs: Object.assign({ 472 attributes: {
456 startPosition: 1, 473 startPosition: 1,
457 insertAfterPosition: 2, 474 insertAfterPosition: 2,
458 reorderLength: 3 475 reorderLength: 3,
459 }, elementAttrs), 476
460 expectedStatus: HttpStatusCode.BAD_REQUEST_400 477 ...attributes
461 }, wrapper) 478 },
479
480 playlistId: playlist.shortUUID,
481 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
482
483 ...wrapper
484 }
462 } 485 }
463 486
464 before(async function () { 487 before(async function () {
465 videoId3 = (await uploadVideoAndGetId({ server, videoName: 'video 3' })).id 488 videoId3 = (await server.videos.quickUpload({ name: 'video 3' })).id
466 videoId4 = (await uploadVideoAndGetId({ server, videoName: 'video 4' })).id 489 videoId4 = (await server.videos.quickUpload({ name: 'video 4' })).id
467 490
468 for (const id of [ videoId3, videoId4 ]) { 491 for (const id of [ videoId3, videoId4 ]) {
469 await addVideoInPlaylist({ 492 await command.addElement({ playlistId: playlist.shortUUID, attributes: { videoId: id } })
470 url: server.url,
471 token: server.accessToken,
472 playlistId: playlist.shortUUID,
473 elementAttrs: { videoId: id }
474 })
475 } 493 }
476 }) 494 })
477 495
478 it('Should fail with an unauthenticated user', async function () { 496 it('Should fail with an unauthenticated user', async function () {
479 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) 497 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
480 await reorderVideosPlaylist(params) 498 await command.reorderElements(params)
481 }) 499 })
482 500
483 it('Should fail with the playlist of another user', async function () { 501 it('Should fail with the playlist of another user', async function () {
484 const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 502 const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
485 await reorderVideosPlaylist(params) 503 await command.reorderElements(params)
486 }) 504 })
487 505
488 it('Should fail with an invalid playlist', async function () { 506 it('Should fail with an invalid playlist', async function () {
489 { 507 {
490 const params = getBase({}, { playlistId: 'toto' }) 508 const params = getBase({}, { playlistId: 'toto' })
491 await reorderVideosPlaylist(params) 509 await command.reorderElements(params)
492 } 510 }
493 511
494 { 512 {
495 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 513 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
496 await reorderVideosPlaylist(params) 514 await command.reorderElements(params)
497 } 515 }
498 }) 516 })
499 517
500 it('Should fail with an invalid start position', async function () { 518 it('Should fail with an invalid start position', async function () {
501 { 519 {
502 const params = getBase({ startPosition: -1 }) 520 const params = getBase({ startPosition: -1 })
503 await reorderVideosPlaylist(params) 521 await command.reorderElements(params)
504 } 522 }
505 523
506 { 524 {
507 const params = getBase({ startPosition: 'toto' as any }) 525 const params = getBase({ startPosition: 'toto' as any })
508 await reorderVideosPlaylist(params) 526 await command.reorderElements(params)
509 } 527 }
510 528
511 { 529 {
512 const params = getBase({ startPosition: 42 }) 530 const params = getBase({ startPosition: 42 })
513 await reorderVideosPlaylist(params) 531 await command.reorderElements(params)
514 } 532 }
515 }) 533 })
516 534
517 it('Should fail with an invalid insert after position', async function () { 535 it('Should fail with an invalid insert after position', async function () {
518 { 536 {
519 const params = getBase({ insertAfterPosition: 'toto' as any }) 537 const params = getBase({ insertAfterPosition: 'toto' as any })
520 await reorderVideosPlaylist(params) 538 await command.reorderElements(params)
521 } 539 }
522 540
523 { 541 {
524 const params = getBase({ insertAfterPosition: -2 }) 542 const params = getBase({ insertAfterPosition: -2 })
525 await reorderVideosPlaylist(params) 543 await command.reorderElements(params)
526 } 544 }
527 545
528 { 546 {
529 const params = getBase({ insertAfterPosition: 42 }) 547 const params = getBase({ insertAfterPosition: 42 })
530 await reorderVideosPlaylist(params) 548 await command.reorderElements(params)
531 } 549 }
532 }) 550 })
533 551
534 it('Should fail with an invalid reorder length', async function () { 552 it('Should fail with an invalid reorder length', async function () {
535 { 553 {
536 const params = getBase({ reorderLength: 'toto' as any }) 554 const params = getBase({ reorderLength: 'toto' as any })
537 await reorderVideosPlaylist(params) 555 await command.reorderElements(params)
538 } 556 }
539 557
540 { 558 {
541 const params = getBase({ reorderLength: -2 }) 559 const params = getBase({ reorderLength: -2 })
542 await reorderVideosPlaylist(params) 560 await command.reorderElements(params)
543 } 561 }
544 562
545 { 563 {
546 const params = getBase({ reorderLength: 42 }) 564 const params = getBase({ reorderLength: 42 })
547 await reorderVideosPlaylist(params) 565 await command.reorderElements(params)
548 } 566 }
549 }) 567 })
550 568
551 it('Succeed with the correct params', async function () { 569 it('Succeed with the correct params', async function () {
552 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 }) 570 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
553 await reorderVideosPlaylist(params) 571 await command.reorderElements(params)
554 }) 572 })
555 }) 573 })
556 574
@@ -562,7 +580,7 @@ describe('Test video playlists API validator', function () {
562 url: server.url, 580 url: server.url,
563 path, 581 path,
564 query: { videoIds: [ 1, 2 ] }, 582 query: { videoIds: [ 1, 2 ] },
565 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 583 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
566 }) 584 })
567 }) 585 })
568 586
@@ -595,82 +613,82 @@ describe('Test video playlists API validator', function () {
595 token: server.accessToken, 613 token: server.accessToken,
596 path, 614 path,
597 query: { videoIds: [ 1, 2 ] }, 615 query: { videoIds: [ 1, 2 ] },
598 statusCodeExpected: HttpStatusCode.OK_200 616 expectedStatus: HttpStatusCode.OK_200
599 }) 617 })
600 }) 618 })
601 }) 619 })
602 620
603 describe('When deleting an element in a playlist', function () { 621 describe('When deleting an element in a playlist', function () {
604 const getBase = (wrapper: any = {}) => { 622 const getBase = (wrapper: Partial<Parameters<PlaylistsCommand['removeElement']>[0]>) => {
605 return Object.assign({ 623 return {
606 url: server.url, 624 elementId,
607 token: server.accessToken,
608 playlistElementId,
609 playlistId: playlist.uuid, 625 playlistId: playlist.uuid,
610 expectedStatus: HttpStatusCode.BAD_REQUEST_400 626 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
611 }, wrapper) 627
628 ...wrapper
629 }
612 } 630 }
613 631
614 it('Should fail with an unauthenticated user', async function () { 632 it('Should fail with an unauthenticated user', async function () {
615 const params = getBase({ token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) 633 const params = getBase({ token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
616 await removeVideoFromPlaylist(params) 634 await command.removeElement(params)
617 }) 635 })
618 636
619 it('Should fail with the playlist of another user', async function () { 637 it('Should fail with the playlist of another user', async function () {
620 const params = getBase({ token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 638 const params = getBase({ token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
621 await removeVideoFromPlaylist(params) 639 await command.removeElement(params)
622 }) 640 })
623 641
624 it('Should fail with an unknown or incorrect playlist id', async function () { 642 it('Should fail with an unknown or incorrect playlist id', async function () {
625 { 643 {
626 const params = getBase({ playlistId: 'toto' }) 644 const params = getBase({ playlistId: 'toto' })
627 await removeVideoFromPlaylist(params) 645 await command.removeElement(params)
628 } 646 }
629 647
630 { 648 {
631 const params = getBase({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 649 const params = getBase({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
632 await removeVideoFromPlaylist(params) 650 await command.removeElement(params)
633 } 651 }
634 }) 652 })
635 653
636 it('Should fail with an unknown or incorrect video id', async function () { 654 it('Should fail with an unknown or incorrect video id', async function () {
637 { 655 {
638 const params = getBase({ playlistElementId: 'toto' }) 656 const params = getBase({ elementId: 'toto' as any })
639 await removeVideoFromPlaylist(params) 657 await command.removeElement(params)
640 } 658 }
641 659
642 { 660 {
643 const params = getBase({ playlistElementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 661 const params = getBase({ elementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
644 await removeVideoFromPlaylist(params) 662 await command.removeElement(params)
645 } 663 }
646 }) 664 })
647 665
648 it('Should fail with an unknown element', async function () { 666 it('Should fail with an unknown element', async function () {
649 const params = getBase({ playlistElementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 667 const params = getBase({ elementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
650 await removeVideoFromPlaylist(params) 668 await command.removeElement(params)
651 }) 669 })
652 670
653 it('Succeed with the correct params', async function () { 671 it('Succeed with the correct params', async function () {
654 const params = getBase({ expectedStatus: HttpStatusCode.NO_CONTENT_204 }) 672 const params = getBase({ expectedStatus: HttpStatusCode.NO_CONTENT_204 })
655 await removeVideoFromPlaylist(params) 673 await command.removeElement(params)
656 }) 674 })
657 }) 675 })
658 676
659 describe('When deleting a playlist', function () { 677 describe('When deleting a playlist', function () {
660 it('Should fail with an unknown playlist', async function () { 678 it('Should fail with an unknown playlist', async function () {
661 await deleteVideoPlaylist(server.url, server.accessToken, 42, HttpStatusCode.NOT_FOUND_404) 679 await command.delete({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
662 }) 680 })
663 681
664 it('Should fail with a playlist of another user', async function () { 682 it('Should fail with a playlist of another user', async function () {
665 await deleteVideoPlaylist(server.url, userAccessToken, playlist.uuid, HttpStatusCode.FORBIDDEN_403) 683 await command.delete({ token: userAccessToken, playlistId: playlist.uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
666 }) 684 })
667 685
668 it('Should fail with the watch later playlist', async function () { 686 it('Should fail with the watch later playlist', async function () {
669 await deleteVideoPlaylist(server.url, server.accessToken, watchLaterPlaylistId, HttpStatusCode.BAD_REQUEST_400) 687 await command.delete({ playlistId: watchLaterPlaylistId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
670 }) 688 })
671 689
672 it('Should succeed with the correct params', async function () { 690 it('Should succeed with the correct params', async function () {
673 await deleteVideoPlaylist(server.url, server.accessToken, playlist.uuid) 691 await command.delete({ playlistId: playlist.uuid })
674 }) 692 })
675 }) 693 })
676 694
diff --git a/server/tests/api/check-params/videos-filter.ts b/server/tests/api/check-params/videos-filter.ts
index 4d54a4fd0..d08570bbe 100644
--- a/server/tests/api/check-params/videos-filter.ts
+++ b/server/tests/api/check-params/videos-filter.ts
@@ -3,18 +3,15 @@
3import 'mocha' 3import 'mocha'
4import { 4import {
5 cleanupTests, 5 cleanupTests,
6 createUser, 6 createSingleServer,
7 flushAndRunServer,
8 makeGetRequest, 7 makeGetRequest,
9 ServerInfo, 8 PeerTubeServer,
10 setAccessTokensToServers, 9 setAccessTokensToServers,
11 setDefaultVideoChannel, 10 setDefaultVideoChannel
12 userLogin 11} from '@shared/extra-utils'
13} from '../../../../shared/extra-utils' 12import { HttpStatusCode, UserRole } from '@shared/models'
14import { UserRole } from '../../../../shared/models/users'
15import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
16 13
17async function testEndpoints (server: ServerInfo, token: string, filter: string, statusCodeExpected: HttpStatusCode) { 14async function testEndpoints (server: PeerTubeServer, token: string, filter: string, expectedStatus: HttpStatusCode) {
18 const paths = [ 15 const paths = [
19 '/api/v1/video-channels/root_channel/videos', 16 '/api/v1/video-channels/root_channel/videos',
20 '/api/v1/accounts/root/videos', 17 '/api/v1/accounts/root/videos',
@@ -30,13 +27,13 @@ async function testEndpoints (server: ServerInfo, token: string, filter: string,
30 query: { 27 query: {
31 filter 28 filter
32 }, 29 },
33 statusCodeExpected 30 expectedStatus
34 }) 31 })
35 } 32 }
36} 33}
37 34
38describe('Test video filters validators', function () { 35describe('Test video filters validators', function () {
39 let server: ServerInfo 36 let server: PeerTubeServer
40 let userAccessToken: string 37 let userAccessToken: string
41 let moderatorAccessToken: string 38 let moderatorAccessToken: string
42 39
@@ -45,28 +42,19 @@ describe('Test video filters validators', function () {
45 before(async function () { 42 before(async function () {
46 this.timeout(30000) 43 this.timeout(30000)
47 44
48 server = await flushAndRunServer(1) 45 server = await createSingleServer(1)
49 46
50 await setAccessTokensToServers([ server ]) 47 await setAccessTokensToServers([ server ])
51 await setDefaultVideoChannel([ server ]) 48 await setDefaultVideoChannel([ server ])
52 49
53 const user = { username: 'user1', password: 'my super password' } 50 const user = { username: 'user1', password: 'my super password' }
54 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 51 await server.users.create({ username: user.username, password: user.password })
55 userAccessToken = await userLogin(server, user) 52 userAccessToken = await server.login.getAccessToken(user)
56 53
57 const moderator = { username: 'moderator', password: 'my super password' } 54 const moderator = { username: 'moderator', password: 'my super password' }
58 await createUser( 55 await server.users.create({ username: moderator.username, password: moderator.password, role: UserRole.MODERATOR })
59 { 56
60 url: server.url, 57 moderatorAccessToken = await server.login.getAccessToken(moderator)
61 accessToken: server.accessToken,
62 username: moderator.username,
63 password: moderator.password,
64 videoQuota: undefined,
65 videoQuotaDaily: undefined,
66 role: UserRole.MODERATOR
67 }
68 )
69 moderatorAccessToken = await userLogin(server, moderator)
70 }) 58 })
71 59
72 describe('When setting a video filter', function () { 60 describe('When setting a video filter', function () {
@@ -100,7 +88,7 @@ describe('Test video filters validators', function () {
100 await makeGetRequest({ 88 await makeGetRequest({
101 url: server.url, 89 url: server.url,
102 path: '/feeds/videos.json', 90 path: '/feeds/videos.json',
103 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401, 91 expectedStatus: HttpStatusCode.UNAUTHORIZED_401,
104 query: { 92 query: {
105 filter 93 filter
106 } 94 }
@@ -112,7 +100,7 @@ describe('Test video filters validators', function () {
112 await makeGetRequest({ 100 await makeGetRequest({
113 url: server.url, 101 url: server.url,
114 path: '/feeds/videos.json', 102 path: '/feeds/videos.json',
115 statusCodeExpected: HttpStatusCode.OK_200, 103 expectedStatus: HttpStatusCode.OK_200,
116 query: { 104 query: {
117 filter: 'local' 105 filter: 'local'
118 } 106 }
diff --git a/server/tests/api/check-params/videos-history.ts b/server/tests/api/check-params/videos-history.ts
index 0e91fe0a8..c3c309ed2 100644
--- a/server/tests/api/check-params/videos-history.ts
+++ b/server/tests/api/check-params/videos-history.ts
@@ -5,42 +5,39 @@ import {
5 checkBadCountPagination, 5 checkBadCountPagination,
6 checkBadStartPagination, 6 checkBadStartPagination,
7 cleanupTests, 7 cleanupTests,
8 flushAndRunServer, 8 createSingleServer,
9 makeGetRequest, 9 makeGetRequest,
10 makePostBodyRequest, 10 makePostBodyRequest,
11 makePutBodyRequest, 11 makePutBodyRequest,
12 ServerInfo, 12 PeerTubeServer,
13 setAccessTokensToServers, 13 setAccessTokensToServers
14 uploadVideo 14} from '@shared/extra-utils'
15} from '../../../../shared/extra-utils' 15import { HttpStatusCode } from '@shared/models'
16import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
17 16
18describe('Test videos history API validator', function () { 17describe('Test videos history API validator', function () {
19 const myHistoryPath = '/api/v1/users/me/history/videos' 18 const myHistoryPath = '/api/v1/users/me/history/videos'
20 const myHistoryRemove = myHistoryPath + '/remove' 19 const myHistoryRemove = myHistoryPath + '/remove'
21 let watchingPath: string 20 let watchingPath: string
22 let server: ServerInfo 21 let server: PeerTubeServer
23 22
24 // --------------------------------------------------------------- 23 // ---------------------------------------------------------------
25 24
26 before(async function () { 25 before(async function () {
27 this.timeout(30000) 26 this.timeout(30000)
28 27
29 server = await flushAndRunServer(1) 28 server = await createSingleServer(1)
30 29
31 await setAccessTokensToServers([ server ]) 30 await setAccessTokensToServers([ server ])
32 31
33 const res = await uploadVideo(server.url, server.accessToken, {}) 32 const { uuid } = await server.videos.upload()
34 const videoUUID = res.body.video.uuid 33 watchingPath = '/api/v1/videos/' + uuid + '/watching'
35
36 watchingPath = '/api/v1/videos/' + videoUUID + '/watching'
37 }) 34 })
38 35
39 describe('When notifying a user is watching a video', function () { 36 describe('When notifying a user is watching a video', function () {
40 37
41 it('Should fail with an unauthenticated user', async function () { 38 it('Should fail with an unauthenticated user', async function () {
42 const fields = { currentTime: 5 } 39 const fields = { currentTime: 5 }
43 await makePutBodyRequest({ url: server.url, path: watchingPath, fields, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 40 await makePutBodyRequest({ url: server.url, path: watchingPath, fields, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
44 }) 41 })
45 42
46 it('Should fail with an incorrect video id', async function () { 43 it('Should fail with an incorrect video id', async function () {
@@ -51,7 +48,7 @@ describe('Test videos history API validator', function () {
51 path, 48 path,
52 fields, 49 fields,
53 token: server.accessToken, 50 token: server.accessToken,
54 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 51 expectedStatus: HttpStatusCode.BAD_REQUEST_400
55 }) 52 })
56 }) 53 })
57 54
@@ -64,7 +61,7 @@ describe('Test videos history API validator', function () {
64 path, 61 path,
65 fields, 62 fields,
66 token: server.accessToken, 63 token: server.accessToken,
67 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 64 expectedStatus: HttpStatusCode.NOT_FOUND_404
68 }) 65 })
69 }) 66 })
70 67
@@ -75,7 +72,7 @@ describe('Test videos history API validator', function () {
75 path: watchingPath, 72 path: watchingPath,
76 fields, 73 fields,
77 token: server.accessToken, 74 token: server.accessToken,
78 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 75 expectedStatus: HttpStatusCode.BAD_REQUEST_400
79 }) 76 })
80 }) 77 })
81 78
@@ -87,7 +84,7 @@ describe('Test videos history API validator', function () {
87 path: watchingPath, 84 path: watchingPath,
88 fields, 85 fields,
89 token: server.accessToken, 86 token: server.accessToken,
90 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 87 expectedStatus: HttpStatusCode.NO_CONTENT_204
91 }) 88 })
92 }) 89 })
93 }) 90 })
@@ -102,17 +99,17 @@ describe('Test videos history API validator', function () {
102 }) 99 })
103 100
104 it('Should fail with an unauthenticated user', async function () { 101 it('Should fail with an unauthenticated user', async function () {
105 await makeGetRequest({ url: server.url, path: myHistoryPath, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 102 await makeGetRequest({ url: server.url, path: myHistoryPath, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
106 }) 103 })
107 104
108 it('Should succeed with the correct params', async function () { 105 it('Should succeed with the correct params', async function () {
109 await makeGetRequest({ url: server.url, token: server.accessToken, path: myHistoryPath, statusCodeExpected: HttpStatusCode.OK_200 }) 106 await makeGetRequest({ url: server.url, token: server.accessToken, path: myHistoryPath, expectedStatus: HttpStatusCode.OK_200 })
110 }) 107 })
111 }) 108 })
112 109
113 describe('When removing user videos history', function () { 110 describe('When removing user videos history', function () {
114 it('Should fail with an unauthenticated user', async function () { 111 it('Should fail with an unauthenticated user', async function () {
115 await makePostBodyRequest({ url: server.url, path: myHistoryPath + '/remove', statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 112 await makePostBodyRequest({ url: server.url, path: myHistoryPath + '/remove', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
116 }) 113 })
117 114
118 it('Should fail with a bad beforeDate parameter', async function () { 115 it('Should fail with a bad beforeDate parameter', async function () {
@@ -122,7 +119,7 @@ describe('Test videos history API validator', function () {
122 token: server.accessToken, 119 token: server.accessToken,
123 path: myHistoryRemove, 120 path: myHistoryRemove,
124 fields: body, 121 fields: body,
125 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 122 expectedStatus: HttpStatusCode.BAD_REQUEST_400
126 }) 123 })
127 }) 124 })
128 125
@@ -133,7 +130,7 @@ describe('Test videos history API validator', function () {
133 token: server.accessToken, 130 token: server.accessToken,
134 path: myHistoryRemove, 131 path: myHistoryRemove,
135 fields: body, 132 fields: body,
136 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 133 expectedStatus: HttpStatusCode.NO_CONTENT_204
137 }) 134 })
138 }) 135 })
139 136
@@ -142,7 +139,7 @@ describe('Test videos history API validator', function () {
142 url: server.url, 139 url: server.url,
143 token: server.accessToken, 140 token: server.accessToken,
144 path: myHistoryRemove, 141 path: myHistoryRemove,
145 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 142 expectedStatus: HttpStatusCode.NO_CONTENT_204
146 }) 143 })
147 }) 144 })
148 }) 145 })
diff --git a/server/tests/api/check-params/videos-overviews.ts b/server/tests/api/check-params/videos-overviews.ts
index 69d7fc471..c2139d74b 100644
--- a/server/tests/api/check-params/videos-overviews.ts
+++ b/server/tests/api/check-params/videos-overviews.ts
@@ -1,29 +1,28 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../../shared/extra-utils' 4import { cleanupTests, createSingleServer, PeerTubeServer } from '@shared/extra-utils'
5import { getVideosOverview } from '@shared/extra-utils/overviews/overviews'
6 5
7describe('Test videos overview', function () { 6describe('Test videos overview', function () {
8 let server: ServerInfo 7 let server: PeerTubeServer
9 8
10 // --------------------------------------------------------------- 9 // ---------------------------------------------------------------
11 10
12 before(async function () { 11 before(async function () {
13 this.timeout(30000) 12 this.timeout(30000)
14 13
15 server = await flushAndRunServer(1) 14 server = await createSingleServer(1)
16 }) 15 })
17 16
18 describe('When getting videos overview', function () { 17 describe('When getting videos overview', function () {
19 18
20 it('Should fail with a bad pagination', async function () { 19 it('Should fail with a bad pagination', async function () {
21 await getVideosOverview(server.url, 0, 400) 20 await server.overviews.getVideos({ page: 0, expectedStatus: 400 })
22 await getVideosOverview(server.url, 100, 400) 21 await server.overviews.getVideos({ page: 100, expectedStatus: 400 })
23 }) 22 })
24 23
25 it('Should succeed with a good pagination', async function () { 24 it('Should succeed with a good pagination', async function () {
26 await getVideosOverview(server.url, 1) 25 await server.overviews.getVideos({ page: 1 })
27 }) 26 })
28 }) 27 })
29 28
diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts
index 4d7a9a23b..e11ca0c82 100644
--- a/server/tests/api/check-params/videos.ts
+++ b/server/tests/api/check-params/videos.ts
@@ -5,39 +5,28 @@ import * as chai from 'chai'
5import { omit } from 'lodash' 5import { omit } from 'lodash'
6import { join } from 'path' 6import { join } from 'path'
7import { randomInt } from '@shared/core-utils' 7import { randomInt } from '@shared/core-utils'
8import { PeerTubeProblemDocument, VideoCreateResult } from '@shared/models'
9import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
10import { 8import {
9 checkBadCountPagination,
10 checkBadSortPagination,
11 checkBadStartPagination,
11 checkUploadVideoParam, 12 checkUploadVideoParam,
12 cleanupTests, 13 cleanupTests,
13 createUser, 14 createSingleServer,
14 flushAndRunServer,
15 getMyUserInformation,
16 getVideo,
17 getVideosList,
18 immutableAssign,
19 makeDeleteRequest, 15 makeDeleteRequest,
20 makeGetRequest, 16 makeGetRequest,
21 makePutBodyRequest, 17 makePutBodyRequest,
22 makeUploadRequest, 18 makeUploadRequest,
23 removeVideo, 19 PeerTubeServer,
24 root, 20 root,
25 ServerInfo, 21 setAccessTokensToServers
26 setAccessTokensToServers, 22} from '@shared/extra-utils'
27 userLogin 23import { HttpStatusCode, PeerTubeProblemDocument, VideoCreateResult, VideoPrivacy } from '@shared/models'
28} from '../../../../shared/extra-utils'
29import {
30 checkBadCountPagination,
31 checkBadSortPagination,
32 checkBadStartPagination
33} from '../../../../shared/extra-utils/requests/check-api-params'
34import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
35 24
36const expect = chai.expect 25const expect = chai.expect
37 26
38describe('Test videos API validator', function () { 27describe('Test videos API validator', function () {
39 const path = '/api/v1/videos/' 28 const path = '/api/v1/videos/'
40 let server: ServerInfo 29 let server: PeerTubeServer
41 let userAccessToken = '' 30 let userAccessToken = ''
42 let accountName: string 31 let accountName: string
43 let channelId: number 32 let channelId: number
@@ -49,20 +38,20 @@ describe('Test videos API validator', function () {
49 before(async function () { 38 before(async function () {
50 this.timeout(30000) 39 this.timeout(30000)
51 40
52 server = await flushAndRunServer(1) 41 server = await createSingleServer(1)
53 42
54 await setAccessTokensToServers([ server ]) 43 await setAccessTokensToServers([ server ])
55 44
56 const username = 'user1' 45 const username = 'user1'
57 const password = 'my super password' 46 const password = 'my super password'
58 await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) 47 await server.users.create({ username: username, password: password })
59 userAccessToken = await userLogin(server, { username, password }) 48 userAccessToken = await server.login.getAccessToken({ username, password })
60 49
61 { 50 {
62 const res = await getMyUserInformation(server.url, server.accessToken) 51 const body = await server.users.getMyInfo()
63 channelId = res.body.videoChannels[0].id 52 channelId = body.videoChannels[0].id
64 channelName = res.body.videoChannels[0].name 53 channelName = body.videoChannels[0].name
65 accountName = res.body.account.name + '@' + res.body.account.host 54 accountName = body.account.name + '@' + body.account.host
66 } 55 }
67 }) 56 })
68 57
@@ -80,11 +69,11 @@ describe('Test videos API validator', function () {
80 }) 69 })
81 70
82 it('Should fail with a bad skipVideos query', async function () { 71 it('Should fail with a bad skipVideos query', async function () {
83 await makeGetRequest({ url: server.url, path, statusCodeExpected: HttpStatusCode.OK_200, query: { skipCount: 'toto' } }) 72 await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.OK_200, query: { skipCount: 'toto' } })
84 }) 73 })
85 74
86 it('Should success with the correct parameters', async function () { 75 it('Should success with the correct parameters', async function () {
87 await makeGetRequest({ url: server.url, path, statusCodeExpected: HttpStatusCode.OK_200, query: { skipCount: false } }) 76 await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.OK_200, query: { skipCount: false } })
88 }) 77 })
89 }) 78 })
90 79
@@ -94,7 +83,7 @@ describe('Test videos API validator', function () {
94 await makeGetRequest({ 83 await makeGetRequest({
95 url: server.url, 84 url: server.url,
96 path: join(path, 'search'), 85 path: join(path, 'search'),
97 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 86 expectedStatus: HttpStatusCode.BAD_REQUEST_400
98 }) 87 })
99 }) 88 })
100 89
@@ -111,7 +100,7 @@ describe('Test videos API validator', function () {
111 }) 100 })
112 101
113 it('Should success with the correct parameters', async function () { 102 it('Should success with the correct parameters', async function () {
114 await makeGetRequest({ url: server.url, path, statusCodeExpected: HttpStatusCode.OK_200 }) 103 await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.OK_200 })
115 }) 104 })
116 }) 105 })
117 106
@@ -131,7 +120,7 @@ describe('Test videos API validator', function () {
131 }) 120 })
132 121
133 it('Should success with the correct parameters', async function () { 122 it('Should success with the correct parameters', async function () {
134 await makeGetRequest({ url: server.url, token: server.accessToken, path, statusCodeExpected: HttpStatusCode.OK_200 }) 123 await makeGetRequest({ url: server.url, token: server.accessToken, path, expectedStatus: HttpStatusCode.OK_200 })
135 }) 124 })
136 }) 125 })
137 126
@@ -155,7 +144,7 @@ describe('Test videos API validator', function () {
155 }) 144 })
156 145
157 it('Should success with the correct parameters', async function () { 146 it('Should success with the correct parameters', async function () {
158 await makeGetRequest({ url: server.url, path, statusCodeExpected: HttpStatusCode.OK_200 }) 147 await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.OK_200 })
159 }) 148 })
160 }) 149 })
161 150
@@ -179,7 +168,7 @@ describe('Test videos API validator', function () {
179 }) 168 })
180 169
181 it('Should success with the correct parameters', async function () { 170 it('Should success with the correct parameters', async function () {
182 await makeGetRequest({ url: server.url, path, statusCodeExpected: HttpStatusCode.OK_200 }) 171 await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.OK_200 })
183 }) 172 })
184 }) 173 })
185 174
@@ -214,70 +203,70 @@ describe('Test videos API validator', function () {
214 it('Should fail with nothing', async function () { 203 it('Should fail with nothing', async function () {
215 const fields = {} 204 const fields = {}
216 const attaches = {} 205 const attaches = {}
217 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 206 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
218 }) 207 })
219 208
220 it('Should fail without name', async function () { 209 it('Should fail without name', async function () {
221 const fields = omit(baseCorrectParams, 'name') 210 const fields = omit(baseCorrectParams, 'name')
222 const attaches = baseCorrectAttaches 211 const attaches = baseCorrectAttaches
223 212
224 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 213 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
225 }) 214 })
226 215
227 it('Should fail with a long name', async function () { 216 it('Should fail with a long name', async function () {
228 const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(65) }) 217 const fields = { ...baseCorrectParams, name: 'super'.repeat(65) }
229 const attaches = baseCorrectAttaches 218 const attaches = baseCorrectAttaches
230 219
231 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 220 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
232 }) 221 })
233 222
234 it('Should fail with a bad category', async function () { 223 it('Should fail with a bad category', async function () {
235 const fields = immutableAssign(baseCorrectParams, { category: 125 }) 224 const fields = { ...baseCorrectParams, category: 125 }
236 const attaches = baseCorrectAttaches 225 const attaches = baseCorrectAttaches
237 226
238 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 227 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
239 }) 228 })
240 229
241 it('Should fail with a bad licence', async function () { 230 it('Should fail with a bad licence', async function () {
242 const fields = immutableAssign(baseCorrectParams, { licence: 125 }) 231 const fields = { ...baseCorrectParams, licence: 125 }
243 const attaches = baseCorrectAttaches 232 const attaches = baseCorrectAttaches
244 233
245 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 234 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
246 }) 235 })
247 236
248 it('Should fail with a bad language', async function () { 237 it('Should fail with a bad language', async function () {
249 const fields = immutableAssign(baseCorrectParams, { language: 'a'.repeat(15) }) 238 const fields = { ...baseCorrectParams, language: 'a'.repeat(15) }
250 const attaches = baseCorrectAttaches 239 const attaches = baseCorrectAttaches
251 240
252 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 241 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
253 }) 242 })
254 243
255 it('Should fail with a long description', async function () { 244 it('Should fail with a long description', async function () {
256 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) }) 245 const fields = { ...baseCorrectParams, description: 'super'.repeat(2500) }
257 const attaches = baseCorrectAttaches 246 const attaches = baseCorrectAttaches
258 247
259 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 248 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
260 }) 249 })
261 250
262 it('Should fail with a long support text', async function () { 251 it('Should fail with a long support text', async function () {
263 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) }) 252 const fields = { ...baseCorrectParams, support: 'super'.repeat(201) }
264 const attaches = baseCorrectAttaches 253 const attaches = baseCorrectAttaches
265 254
266 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 255 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
267 }) 256 })
268 257
269 it('Should fail without a channel', async function () { 258 it('Should fail without a channel', async function () {
270 const fields = omit(baseCorrectParams, 'channelId') 259 const fields = omit(baseCorrectParams, 'channelId')
271 const attaches = baseCorrectAttaches 260 const attaches = baseCorrectAttaches
272 261
273 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 262 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
274 }) 263 })
275 264
276 it('Should fail with a bad channel', async function () { 265 it('Should fail with a bad channel', async function () {
277 const fields = immutableAssign(baseCorrectParams, { channelId: 545454 }) 266 const fields = { ...baseCorrectParams, channelId: 545454 }
278 const attaches = baseCorrectAttaches 267 const attaches = baseCorrectAttaches
279 268
280 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 269 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
281 }) 270 })
282 271
283 it('Should fail with another user channel', async function () { 272 it('Should fail with another user channel', async function () {
@@ -285,69 +274,71 @@ describe('Test videos API validator', function () {
285 username: 'fake' + randomInt(0, 1500), 274 username: 'fake' + randomInt(0, 1500),
286 password: 'fake_password' 275 password: 'fake_password'
287 } 276 }
288 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 277 await server.users.create({ username: user.username, password: user.password })
289 278
290 const accessTokenUser = await userLogin(server, user) 279 const accessTokenUser = await server.login.getAccessToken(user)
291 const res = await getMyUserInformation(server.url, accessTokenUser) 280 const { videoChannels } = await server.users.getMyInfo({ token: accessTokenUser })
292 const customChannelId = res.body.videoChannels[0].id 281 const customChannelId = videoChannels[0].id
293 282
294 const fields = immutableAssign(baseCorrectParams, { channelId: customChannelId }) 283 const fields = { ...baseCorrectParams, channelId: customChannelId }
295 const attaches = baseCorrectAttaches 284 const attaches = baseCorrectAttaches
296 285
297 await checkUploadVideoParam(server.url, userAccessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 286 await checkUploadVideoParam(server, userAccessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
298 }) 287 })
299 288
300 it('Should fail with too many tags', async function () { 289 it('Should fail with too many tags', async function () {
301 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }) 290 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }
302 const attaches = baseCorrectAttaches 291 const attaches = baseCorrectAttaches
303 292
304 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 293 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
305 }) 294 })
306 295
307 it('Should fail with a tag length too low', async function () { 296 it('Should fail with a tag length too low', async function () {
308 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 't' ] }) 297 const fields = { ...baseCorrectParams, tags: [ 'tag1', 't' ] }
309 const attaches = baseCorrectAttaches 298 const attaches = baseCorrectAttaches
310 299
311 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 300 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
312 }) 301 })
313 302
314 it('Should fail with a tag length too big', async function () { 303 it('Should fail with a tag length too big', async function () {
315 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }) 304 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }
316 const attaches = baseCorrectAttaches 305 const attaches = baseCorrectAttaches
317 306
318 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 307 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
319 }) 308 })
320 309
321 it('Should fail with a bad schedule update (miss updateAt)', async function () { 310 it('Should fail with a bad schedule update (miss updateAt)', async function () {
322 const fields = immutableAssign(baseCorrectParams, { scheduleUpdate: { privacy: VideoPrivacy.PUBLIC } }) 311 const fields = { ...baseCorrectParams, scheduleUpdate: { privacy: VideoPrivacy.PUBLIC } }
323 const attaches = baseCorrectAttaches 312 const attaches = baseCorrectAttaches
324 313
325 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 314 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
326 }) 315 })
327 316
328 it('Should fail with a bad schedule update (wrong updateAt)', async function () { 317 it('Should fail with a bad schedule update (wrong updateAt)', async function () {
329 const fields = immutableAssign(baseCorrectParams, { 318 const fields = {
319 ...baseCorrectParams,
320
330 scheduleUpdate: { 321 scheduleUpdate: {
331 privacy: VideoPrivacy.PUBLIC, 322 privacy: VideoPrivacy.PUBLIC,
332 updateAt: 'toto' 323 updateAt: 'toto'
333 } 324 }
334 }) 325 }
335 const attaches = baseCorrectAttaches 326 const attaches = baseCorrectAttaches
336 327
337 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 328 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
338 }) 329 })
339 330
340 it('Should fail with a bad originally published at attribute', async function () { 331 it('Should fail with a bad originally published at attribute', async function () {
341 const fields = immutableAssign(baseCorrectParams, { originallyPublishedAt: 'toto' }) 332 const fields = { ...baseCorrectParams, originallyPublishedAt: 'toto' }
342 const attaches = baseCorrectAttaches 333 const attaches = baseCorrectAttaches
343 334
344 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 335 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
345 }) 336 })
346 337
347 it('Should fail without an input file', async function () { 338 it('Should fail without an input file', async function () {
348 const fields = baseCorrectParams 339 const fields = baseCorrectParams
349 const attaches = {} 340 const attaches = {}
350 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 341 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
351 }) 342 })
352 343
353 it('Should fail with an incorrect input file', async function () { 344 it('Should fail with an incorrect input file', async function () {
@@ -355,7 +346,7 @@ describe('Test videos API validator', function () {
355 let attaches = { fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short_fake.webm') } 346 let attaches = { fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short_fake.webm') }
356 347
357 await checkUploadVideoParam( 348 await checkUploadVideoParam(
358 server.url, 349 server,
359 server.accessToken, 350 server.accessToken,
360 { ...fields, ...attaches }, 351 { ...fields, ...attaches },
361 HttpStatusCode.UNPROCESSABLE_ENTITY_422, 352 HttpStatusCode.UNPROCESSABLE_ENTITY_422,
@@ -364,7 +355,7 @@ describe('Test videos API validator', function () {
364 355
365 attaches = { fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mkv') } 356 attaches = { fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mkv') }
366 await checkUploadVideoParam( 357 await checkUploadVideoParam(
367 server.url, 358 server,
368 server.accessToken, 359 server.accessToken,
369 { ...fields, ...attaches }, 360 { ...fields, ...attaches },
370 HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415, 361 HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415,
@@ -379,7 +370,7 @@ describe('Test videos API validator', function () {
379 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4') 370 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4')
380 } 371 }
381 372
382 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 373 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
383 }) 374 })
384 375
385 it('Should fail with a big thumbnail file', async function () { 376 it('Should fail with a big thumbnail file', async function () {
@@ -389,7 +380,7 @@ describe('Test videos API validator', function () {
389 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4') 380 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4')
390 } 381 }
391 382
392 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 383 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
393 }) 384 })
394 385
395 it('Should fail with an incorrect preview file', async function () { 386 it('Should fail with an incorrect preview file', async function () {
@@ -399,7 +390,7 @@ describe('Test videos API validator', function () {
399 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4') 390 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4')
400 } 391 }
401 392
402 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 393 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
403 }) 394 })
404 395
405 it('Should fail with a big preview file', async function () { 396 it('Should fail with a big preview file', async function () {
@@ -409,17 +400,17 @@ describe('Test videos API validator', function () {
409 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4') 400 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4')
410 } 401 }
411 402
412 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 403 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
413 }) 404 })
414 405
415 it('Should report the appropriate error', async function () { 406 it('Should report the appropriate error', async function () {
416 const fields = immutableAssign(baseCorrectParams, { language: 'a'.repeat(15) }) 407 const fields = { ...baseCorrectParams, language: 'a'.repeat(15) }
417 const attaches = baseCorrectAttaches 408 const attaches = baseCorrectAttaches
418 409
419 const attributes = { ...fields, ...attaches } 410 const attributes = { ...fields, ...attaches }
420 const res = await checkUploadVideoParam(server.url, server.accessToken, attributes, HttpStatusCode.BAD_REQUEST_400, mode) 411 const body = await checkUploadVideoParam(server, server.accessToken, attributes, HttpStatusCode.BAD_REQUEST_400, mode)
421 412
422 const error = res.body as PeerTubeProblemDocument 413 const error = body as unknown as PeerTubeProblemDocument
423 414
424 if (mode === 'legacy') { 415 if (mode === 'legacy') {
425 expect(error.docs).to.equal('https://docs.joinpeertube.org/api-rest-reference.html#operation/uploadLegacy') 416 expect(error.docs).to.equal('https://docs.joinpeertube.org/api-rest-reference.html#operation/uploadLegacy')
@@ -444,23 +435,27 @@ describe('Test videos API validator', function () {
444 435
445 { 436 {
446 const attaches = baseCorrectAttaches 437 const attaches = baseCorrectAttaches
447 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.OK_200, mode) 438 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.OK_200, mode)
448 } 439 }
449 440
450 { 441 {
451 const attaches = immutableAssign(baseCorrectAttaches, { 442 const attaches = {
443 ...baseCorrectAttaches,
444
452 videofile: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4') 445 videofile: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4')
453 }) 446 }
454 447
455 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.OK_200, mode) 448 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.OK_200, mode)
456 } 449 }
457 450
458 { 451 {
459 const attaches = immutableAssign(baseCorrectAttaches, { 452 const attaches = {
453 ...baseCorrectAttaches,
454
460 videofile: join(root(), 'server', 'tests', 'fixtures', 'video_short.ogv') 455 videofile: join(root(), 'server', 'tests', 'fixtures', 'video_short.ogv')
461 }) 456 }
462 457
463 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.OK_200, mode) 458 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.OK_200, mode)
464 } 459 }
465 }) 460 })
466 } 461 }
@@ -489,8 +484,8 @@ describe('Test videos API validator', function () {
489 } 484 }
490 485
491 before(async function () { 486 before(async function () {
492 const res = await getVideosList(server.url) 487 const { data } = await server.videos.list()
493 video = res.body.data[0] 488 video = data[0]
494 }) 489 })
495 490
496 it('Should fail with nothing', async function () { 491 it('Should fail with nothing', async function () {
@@ -511,84 +506,84 @@ describe('Test videos API validator', function () {
511 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06', 506 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06',
512 token: server.accessToken, 507 token: server.accessToken,
513 fields, 508 fields,
514 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 509 expectedStatus: HttpStatusCode.NOT_FOUND_404
515 }) 510 })
516 }) 511 })
517 512
518 it('Should fail with a long name', async function () { 513 it('Should fail with a long name', async function () {
519 const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(65) }) 514 const fields = { ...baseCorrectParams, name: 'super'.repeat(65) }
520 515
521 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 516 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
522 }) 517 })
523 518
524 it('Should fail with a bad category', async function () { 519 it('Should fail with a bad category', async function () {
525 const fields = immutableAssign(baseCorrectParams, { category: 125 }) 520 const fields = { ...baseCorrectParams, category: 125 }
526 521
527 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 522 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
528 }) 523 })
529 524
530 it('Should fail with a bad licence', async function () { 525 it('Should fail with a bad licence', async function () {
531 const fields = immutableAssign(baseCorrectParams, { licence: 125 }) 526 const fields = { ...baseCorrectParams, licence: 125 }
532 527
533 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 528 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
534 }) 529 })
535 530
536 it('Should fail with a bad language', async function () { 531 it('Should fail with a bad language', async function () {
537 const fields = immutableAssign(baseCorrectParams, { language: 'a'.repeat(15) }) 532 const fields = { ...baseCorrectParams, language: 'a'.repeat(15) }
538 533
539 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 534 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
540 }) 535 })
541 536
542 it('Should fail with a long description', async function () { 537 it('Should fail with a long description', async function () {
543 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) }) 538 const fields = { ...baseCorrectParams, description: 'super'.repeat(2500) }
544 539
545 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 540 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
546 }) 541 })
547 542
548 it('Should fail with a long support text', async function () { 543 it('Should fail with a long support text', async function () {
549 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) }) 544 const fields = { ...baseCorrectParams, support: 'super'.repeat(201) }
550 545
551 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 546 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
552 }) 547 })
553 548
554 it('Should fail with a bad channel', async function () { 549 it('Should fail with a bad channel', async function () {
555 const fields = immutableAssign(baseCorrectParams, { channelId: 545454 }) 550 const fields = { ...baseCorrectParams, channelId: 545454 }
556 551
557 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 552 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
558 }) 553 })
559 554
560 it('Should fail with too many tags', async function () { 555 it('Should fail with too many tags', async function () {
561 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }) 556 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }
562 557
563 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 558 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
564 }) 559 })
565 560
566 it('Should fail with a tag length too low', async function () { 561 it('Should fail with a tag length too low', async function () {
567 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 't' ] }) 562 const fields = { ...baseCorrectParams, tags: [ 'tag1', 't' ] }
568 563
569 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 564 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
570 }) 565 })
571 566
572 it('Should fail with a tag length too big', async function () { 567 it('Should fail with a tag length too big', async function () {
573 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }) 568 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }
574 569
575 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 570 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
576 }) 571 })
577 572
578 it('Should fail with a bad schedule update (miss updateAt)', async function () { 573 it('Should fail with a bad schedule update (miss updateAt)', async function () {
579 const fields = immutableAssign(baseCorrectParams, { scheduleUpdate: { privacy: VideoPrivacy.PUBLIC } }) 574 const fields = { ...baseCorrectParams, scheduleUpdate: { privacy: VideoPrivacy.PUBLIC } }
580 575
581 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 576 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
582 }) 577 })
583 578
584 it('Should fail with a bad schedule update (wrong updateAt)', async function () { 579 it('Should fail with a bad schedule update (wrong updateAt)', async function () {
585 const fields = immutableAssign(baseCorrectParams, { scheduleUpdate: { updateAt: 'toto', privacy: VideoPrivacy.PUBLIC } }) 580 const fields = { ...baseCorrectParams, scheduleUpdate: { updateAt: 'toto', privacy: VideoPrivacy.PUBLIC } }
586 581
587 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 582 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
588 }) 583 })
589 584
590 it('Should fail with a bad originally published at param', async function () { 585 it('Should fail with a bad originally published at param', async function () {
591 const fields = immutableAssign(baseCorrectParams, { originallyPublishedAt: 'toto' }) 586 const fields = { ...baseCorrectParams, originallyPublishedAt: 'toto' }
592 587
593 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 588 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
594 }) 589 })
@@ -665,14 +660,14 @@ describe('Test videos API validator', function () {
665 path: path + video.shortUUID, 660 path: path + video.shortUUID,
666 token: userAccessToken, 661 token: userAccessToken,
667 fields, 662 fields,
668 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 663 expectedStatus: HttpStatusCode.FORBIDDEN_403
669 }) 664 })
670 }) 665 })
671 666
672 it('Should fail with a video of another server') 667 it('Should fail with a video of another server')
673 668
674 it('Shoud report the appropriate error', async function () { 669 it('Shoud report the appropriate error', async function () {
675 const fields = immutableAssign(baseCorrectParams, { licence: 125 }) 670 const fields = { ...baseCorrectParams, licence: 125 }
676 671
677 const res = await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 672 const res = await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
678 const error = res.body as PeerTubeProblemDocument 673 const error = res.body as PeerTubeProblemDocument
@@ -697,7 +692,7 @@ describe('Test videos API validator', function () {
697 path: path + video.shortUUID, 692 path: path + video.shortUUID,
698 token: server.accessToken, 693 token: server.accessToken,
699 fields, 694 fields,
700 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 695 expectedStatus: HttpStatusCode.NO_CONTENT_204
701 }) 696 })
702 }) 697 })
703 }) 698 })
@@ -707,7 +702,7 @@ describe('Test videos API validator', function () {
707 const res = await makeGetRequest({ 702 const res = await makeGetRequest({
708 url: server.url, 703 url: server.url,
709 path, 704 path,
710 statusCodeExpected: HttpStatusCode.OK_200 705 expectedStatus: HttpStatusCode.OK_200
711 }) 706 })
712 707
713 expect(res.body.data).to.be.an('array') 708 expect(res.body.data).to.be.an('array')
@@ -715,16 +710,16 @@ describe('Test videos API validator', function () {
715 }) 710 })
716 711
717 it('Should fail without a correct uuid', async function () { 712 it('Should fail without a correct uuid', async function () {
718 await getVideo(server.url, 'coucou', HttpStatusCode.BAD_REQUEST_400) 713 await server.videos.get({ id: 'coucou', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
719 }) 714 })
720 715
721 it('Should return 404 with an incorrect video', async function () { 716 it('Should return 404 with an incorrect video', async function () {
722 await getVideo(server.url, '4da6fde3-88f7-4d16-b119-108df5630b06', HttpStatusCode.NOT_FOUND_404) 717 await server.videos.get({ id: '4da6fde3-88f7-4d16-b119-108df5630b06', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
723 }) 718 })
724 719
725 it('Shoud report the appropriate error', async function () { 720 it('Shoud report the appropriate error', async function () {
726 const res = await getVideo(server.url, 'hi', HttpStatusCode.BAD_REQUEST_400) 721 const body = await server.videos.get({ id: 'hi', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
727 const error = res.body as PeerTubeProblemDocument 722 const error = body as unknown as PeerTubeProblemDocument
728 723
729 expect(error.docs).to.equal('https://docs.joinpeertube.org/api-rest-reference.html#operation/getVideo') 724 expect(error.docs).to.equal('https://docs.joinpeertube.org/api-rest-reference.html#operation/getVideo')
730 725
@@ -739,16 +734,16 @@ describe('Test videos API validator', function () {
739 }) 734 })
740 735
741 it('Should succeed with the correct parameters', async function () { 736 it('Should succeed with the correct parameters', async function () {
742 await getVideo(server.url, video.shortUUID) 737 await server.videos.get({ id: video.shortUUID })
743 }) 738 })
744 }) 739 })
745 740
746 describe('When rating a video', function () { 741 describe('When rating a video', function () {
747 let videoId 742 let videoId: number
748 743
749 before(async function () { 744 before(async function () {
750 const res = await getVideosList(server.url) 745 const { data } = await server.videos.list()
751 videoId = res.body.data[0].id 746 videoId = data[0].id
752 }) 747 })
753 748
754 it('Should fail without a valid uuid', async function () { 749 it('Should fail without a valid uuid', async function () {
@@ -767,7 +762,7 @@ describe('Test videos API validator', function () {
767 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06/rate', 762 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06/rate',
768 token: server.accessToken, 763 token: server.accessToken,
769 fields, 764 fields,
770 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 765 expectedStatus: HttpStatusCode.NOT_FOUND_404
771 }) 766 })
772 }) 767 })
773 768
@@ -787,7 +782,7 @@ describe('Test videos API validator', function () {
787 path: path + videoId + '/rate', 782 path: path + videoId + '/rate',
788 token: server.accessToken, 783 token: server.accessToken,
789 fields, 784 fields,
790 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 785 expectedStatus: HttpStatusCode.NO_CONTENT_204
791 }) 786 })
792 }) 787 })
793 }) 788 })
@@ -797,27 +792,27 @@ describe('Test videos API validator', function () {
797 await makeDeleteRequest({ 792 await makeDeleteRequest({
798 url: server.url, 793 url: server.url,
799 path, 794 path,
800 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 795 expectedStatus: HttpStatusCode.BAD_REQUEST_400
801 }) 796 })
802 }) 797 })
803 798
804 it('Should fail without a correct uuid', async function () { 799 it('Should fail without a correct uuid', async function () {
805 await removeVideo(server.url, server.accessToken, 'hello', HttpStatusCode.BAD_REQUEST_400) 800 await server.videos.remove({ id: 'hello', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
806 }) 801 })
807 802
808 it('Should fail with a video which does not exist', async function () { 803 it('Should fail with a video which does not exist', async function () {
809 await removeVideo(server.url, server.accessToken, '4da6fde3-88f7-4d16-b119-108df5630b06', HttpStatusCode.NOT_FOUND_404) 804 await server.videos.remove({ id: '4da6fde3-88f7-4d16-b119-108df5630b06', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
810 }) 805 })
811 806
812 it('Should fail with a video of another user without the appropriate right', async function () { 807 it('Should fail with a video of another user without the appropriate right', async function () {
813 await removeVideo(server.url, userAccessToken, video.uuid, HttpStatusCode.FORBIDDEN_403) 808 await server.videos.remove({ token: userAccessToken, id: video.uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
814 }) 809 })
815 810
816 it('Should fail with a video of another server') 811 it('Should fail with a video of another server')
817 812
818 it('Shoud report the appropriate error', async function () { 813 it('Shoud report the appropriate error', async function () {
819 const res = await removeVideo(server.url, server.accessToken, 'hello', HttpStatusCode.BAD_REQUEST_400) 814 const body = await server.videos.remove({ id: 'hello', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
820 const error = res.body as PeerTubeProblemDocument 815 const error = body as PeerTubeProblemDocument
821 816
822 expect(error.docs).to.equal('https://docs.joinpeertube.org/api-rest-reference.html#operation/delVideo') 817 expect(error.docs).to.equal('https://docs.joinpeertube.org/api-rest-reference.html#operation/delVideo')
823 818
@@ -832,7 +827,7 @@ describe('Test videos API validator', function () {
832 }) 827 })
833 828
834 it('Should succeed with the correct parameters', async function () { 829 it('Should succeed with the correct parameters', async function () {
835 await removeVideo(server.url, server.accessToken, video.uuid) 830 await server.videos.remove({ id: video.uuid })
836 }) 831 })
837 }) 832 })
838 833
diff --git a/server/tests/api/live/live-constraints.ts b/server/tests/api/live/live-constraints.ts
index cc635de33..4acde3cc5 100644
--- a/server/tests/api/live/live-constraints.ts
+++ b/server/tests/api/live/live-constraints.ts
@@ -2,31 +2,24 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoDetails, VideoPrivacy } from '@shared/models' 5import { VideoPrivacy } from '@shared/models'
6import { 6import {
7 checkLiveCleanup, 7 checkLiveCleanupAfterSave,
8 cleanupTests, 8 cleanupTests,
9 createLive, 9 ConfigCommand,
10 createMultipleServers,
10 doubleFollow, 11 doubleFollow,
11 flushAndRunMultipleServers, 12 PeerTubeServer,
12 generateUser,
13 getCustomConfigResolutions,
14 getVideo,
15 runAndTestFfmpegStreamError,
16 ServerInfo,
17 setAccessTokensToServers, 13 setAccessTokensToServers,
18 setDefaultVideoChannel, 14 setDefaultVideoChannel,
19 updateCustomSubConfig,
20 updateUser,
21 wait, 15 wait,
22 waitJobs, 16 waitJobs
23 waitUntilLivePublished
24} from '../../../../shared/extra-utils' 17} from '../../../../shared/extra-utils'
25 18
26const expect = chai.expect 19const expect = chai.expect
27 20
28describe('Test live constraints', function () { 21describe('Test live constraints', function () {
29 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
30 let userId: number 23 let userId: number
31 let userAccessToken: string 24 let userAccessToken: string
32 let userChannelId: number 25 let userChannelId: number
@@ -39,32 +32,28 @@ describe('Test live constraints', function () {
39 saveReplay 32 saveReplay
40 } 33 }
41 34
42 const res = await createLive(servers[0].url, userAccessToken, liveAttributes) 35 const { uuid } = await servers[0].live.create({ token: userAccessToken, fields: liveAttributes })
43 return res.body.video.uuid as string 36 return uuid
44 } 37 }
45 38
46 async function checkSaveReplay (videoId: string, resolutions = [ 720 ]) { 39 async function checkSaveReplay (videoId: string, resolutions = [ 720 ]) {
47 for (const server of servers) { 40 for (const server of servers) {
48 const res = await getVideo(server.url, videoId) 41 const video = await server.videos.get({ id: videoId })
49
50 const video: VideoDetails = res.body
51 expect(video.isLive).to.be.false 42 expect(video.isLive).to.be.false
52 expect(video.duration).to.be.greaterThan(0) 43 expect(video.duration).to.be.greaterThan(0)
53 } 44 }
54 45
55 await checkLiveCleanup(servers[0], videoId, resolutions) 46 await checkLiveCleanupAfterSave(servers[0], videoId, resolutions)
56 } 47 }
57 48
58 async function waitUntilLivePublishedOnAllServers (videoId: string) { 49 async function waitUntilLivePublishedOnAllServers (videoId: string) {
59 for (const server of servers) { 50 for (const server of servers) {
60 await waitUntilLivePublished(server.url, server.accessToken, videoId) 51 await server.live.waitUntilPublished({ videoId })
61 } 52 }
62 } 53 }
63 54
64 function updateQuota (options: { total: number, daily: number }) { 55 function updateQuota (options: { total: number, daily: number }) {
65 return updateUser({ 56 return servers[0].users.update({
66 url: servers[0].url,
67 accessToken: servers[0].accessToken,
68 userId, 57 userId,
69 videoQuota: options.total, 58 videoQuota: options.total,
70 videoQuotaDaily: options.daily 59 videoQuotaDaily: options.daily
@@ -74,24 +63,26 @@ describe('Test live constraints', function () {
74 before(async function () { 63 before(async function () {
75 this.timeout(120000) 64 this.timeout(120000)
76 65
77 servers = await flushAndRunMultipleServers(2) 66 servers = await createMultipleServers(2)
78 67
79 // Get the access tokens 68 // Get the access tokens
80 await setAccessTokensToServers(servers) 69 await setAccessTokensToServers(servers)
81 await setDefaultVideoChannel(servers) 70 await setDefaultVideoChannel(servers)
82 71
83 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 72 await servers[0].config.updateCustomSubConfig({
84 live: { 73 newConfig: {
85 enabled: true, 74 live: {
86 allowReplay: true, 75 enabled: true,
87 transcoding: { 76 allowReplay: true,
88 enabled: false 77 transcoding: {
78 enabled: false
79 }
89 } 80 }
90 } 81 }
91 }) 82 })
92 83
93 { 84 {
94 const res = await generateUser(servers[0], 'user1') 85 const res = await servers[0].users.generate('user1')
95 userId = res.userId 86 userId = res.userId
96 userChannelId = res.userChannelId 87 userChannelId = res.userChannelId
97 userAccessToken = res.token 88 userAccessToken = res.token
@@ -107,7 +98,7 @@ describe('Test live constraints', function () {
107 this.timeout(60000) 98 this.timeout(60000)
108 99
109 const userVideoLiveoId = await createLiveWrapper(false) 100 const userVideoLiveoId = await createLiveWrapper(false)
110 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, false) 101 await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: false })
111 }) 102 })
112 103
113 it('Should have size limit depending on user global quota if save replay is enabled', async function () { 104 it('Should have size limit depending on user global quota if save replay is enabled', async function () {
@@ -117,7 +108,7 @@ describe('Test live constraints', function () {
117 await wait(5000) 108 await wait(5000)
118 109
119 const userVideoLiveoId = await createLiveWrapper(true) 110 const userVideoLiveoId = await createLiveWrapper(true)
120 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) 111 await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: true })
121 112
122 await waitUntilLivePublishedOnAllServers(userVideoLiveoId) 113 await waitUntilLivePublishedOnAllServers(userVideoLiveoId)
123 await waitJobs(servers) 114 await waitJobs(servers)
@@ -134,7 +125,7 @@ describe('Test live constraints', function () {
134 await updateQuota({ total: -1, daily: 1 }) 125 await updateQuota({ total: -1, daily: 1 })
135 126
136 const userVideoLiveoId = await createLiveWrapper(true) 127 const userVideoLiveoId = await createLiveWrapper(true)
137 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) 128 await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: true })
138 129
139 await waitUntilLivePublishedOnAllServers(userVideoLiveoId) 130 await waitUntilLivePublishedOnAllServers(userVideoLiveoId)
140 await waitJobs(servers) 131 await waitJobs(servers)
@@ -151,26 +142,28 @@ describe('Test live constraints', function () {
151 await updateQuota({ total: 10 * 1000 * 1000, daily: -1 }) 142 await updateQuota({ total: 10 * 1000 * 1000, daily: -1 })
152 143
153 const userVideoLiveoId = await createLiveWrapper(true) 144 const userVideoLiveoId = await createLiveWrapper(true)
154 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, false) 145 await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: false })
155 }) 146 })
156 147
157 it('Should have max duration limit', async function () { 148 it('Should have max duration limit', async function () {
158 this.timeout(60000) 149 this.timeout(60000)
159 150
160 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 151 await servers[0].config.updateCustomSubConfig({
161 live: { 152 newConfig: {
162 enabled: true, 153 live: {
163 allowReplay: true,
164 maxDuration: 1,
165 transcoding: {
166 enabled: true, 154 enabled: true,
167 resolutions: getCustomConfigResolutions(true) 155 allowReplay: true,
156 maxDuration: 1,
157 transcoding: {
158 enabled: true,
159 resolutions: ConfigCommand.getCustomConfigResolutions(true)
160 }
168 } 161 }
169 } 162 }
170 }) 163 })
171 164
172 const userVideoLiveoId = await createLiveWrapper(true) 165 const userVideoLiveoId = await createLiveWrapper(true)
173 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) 166 await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: true })
174 167
175 await waitUntilLivePublishedOnAllServers(userVideoLiveoId) 168 await waitUntilLivePublishedOnAllServers(userVideoLiveoId)
176 await waitJobs(servers) 169 await waitJobs(servers)
diff --git a/server/tests/api/live/live-permanent.ts b/server/tests/api/live/live-permanent.ts
index 71b7d28a8..f07d4cfec 100644
--- a/server/tests/api/live/live-permanent.ts
+++ b/server/tests/api/live/live-permanent.ts
@@ -2,59 +2,50 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { LiveVideoCreate, VideoDetails, VideoPrivacy, VideoState } from '@shared/models' 5import { LiveVideoCreate, VideoPrivacy, VideoState } from '@shared/models'
6import { 6import {
7 cleanupTests, 7 cleanupTests,
8 createLive, 8 ConfigCommand,
9 createMultipleServers,
9 doubleFollow, 10 doubleFollow,
10 flushAndRunMultipleServers, 11 PeerTubeServer,
11 getCustomConfigResolutions,
12 getLive,
13 getPlaylistsCount,
14 getVideo,
15 sendRTMPStreamInVideo,
16 ServerInfo,
17 setAccessTokensToServers, 12 setAccessTokensToServers,
18 setDefaultVideoChannel, 13 setDefaultVideoChannel,
19 stopFfmpeg, 14 stopFfmpeg,
20 updateCustomSubConfig,
21 updateLive,
22 wait, 15 wait,
23 waitJobs, 16 waitJobs
24 waitUntilLivePublished,
25 waitUntilLiveWaiting
26} from '../../../../shared/extra-utils' 17} from '../../../../shared/extra-utils'
27 18
28const expect = chai.expect 19const expect = chai.expect
29 20
30describe('Permanent live', function () { 21describe('Permanent live', function () {
31 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
32 let videoUUID: string 23 let videoUUID: string
33 24
34 async function createLiveWrapper (permanentLive: boolean) { 25 async function createLiveWrapper (permanentLive: boolean) {
35 const attributes: LiveVideoCreate = { 26 const attributes: LiveVideoCreate = {
36 channelId: servers[0].videoChannel.id, 27 channelId: servers[0].store.channel.id,
37 privacy: VideoPrivacy.PUBLIC, 28 privacy: VideoPrivacy.PUBLIC,
38 name: 'my super live', 29 name: 'my super live',
39 saveReplay: false, 30 saveReplay: false,
40 permanentLive 31 permanentLive
41 } 32 }
42 33
43 const res = await createLive(servers[0].url, servers[0].accessToken, attributes) 34 const { uuid } = await servers[0].live.create({ fields: attributes })
44 return res.body.video.uuid 35 return uuid
45 } 36 }
46 37
47 async function checkVideoState (videoId: string, state: VideoState) { 38 async function checkVideoState (videoId: string, state: VideoState) {
48 for (const server of servers) { 39 for (const server of servers) {
49 const res = await getVideo(server.url, videoId) 40 const video = await server.videos.get({ id: videoId })
50 expect((res.body as VideoDetails).state.id).to.equal(state) 41 expect(video.state.id).to.equal(state)
51 } 42 }
52 } 43 }
53 44
54 before(async function () { 45 before(async function () {
55 this.timeout(120000) 46 this.timeout(120000)
56 47
57 servers = await flushAndRunMultipleServers(2) 48 servers = await createMultipleServers(2)
58 49
59 // Get the access tokens 50 // Get the access tokens
60 await setAccessTokensToServers(servers) 51 await setAccessTokensToServers(servers)
@@ -63,14 +54,16 @@ describe('Permanent live', function () {
63 // Server 1 and server 2 follow each other 54 // Server 1 and server 2 follow each other
64 await doubleFollow(servers[0], servers[1]) 55 await doubleFollow(servers[0], servers[1])
65 56
66 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 57 await servers[0].config.updateCustomSubConfig({
67 live: { 58 newConfig: {
68 enabled: true, 59 live: {
69 allowReplay: true,
70 maxDuration: -1,
71 transcoding: {
72 enabled: true, 60 enabled: true,
73 resolutions: getCustomConfigResolutions(true) 61 allowReplay: true,
62 maxDuration: -1,
63 transcoding: {
64 enabled: true,
65 resolutions: ConfigCommand.getCustomConfigResolutions(true)
66 }
74 } 67 }
75 } 68 }
76 }) 69 })
@@ -82,15 +75,15 @@ describe('Permanent live', function () {
82 const videoUUID = await createLiveWrapper(false) 75 const videoUUID = await createLiveWrapper(false)
83 76
84 { 77 {
85 const res = await getLive(servers[0].url, servers[0].accessToken, videoUUID) 78 const live = await servers[0].live.get({ videoId: videoUUID })
86 expect(res.body.permanentLive).to.be.false 79 expect(live.permanentLive).to.be.false
87 } 80 }
88 81
89 await updateLive(servers[0].url, servers[0].accessToken, videoUUID, { permanentLive: true }) 82 await servers[0].live.update({ videoId: videoUUID, fields: { permanentLive: true } })
90 83
91 { 84 {
92 const res = await getLive(servers[0].url, servers[0].accessToken, videoUUID) 85 const live = await servers[0].live.get({ videoId: videoUUID })
93 expect(res.body.permanentLive).to.be.true 86 expect(live.permanentLive).to.be.true
94 } 87 }
95 }) 88 })
96 89
@@ -99,8 +92,8 @@ describe('Permanent live', function () {
99 92
100 videoUUID = await createLiveWrapper(true) 93 videoUUID = await createLiveWrapper(true)
101 94
102 const res = await getLive(servers[0].url, servers[0].accessToken, videoUUID) 95 const live = await servers[0].live.get({ videoId: videoUUID })
103 expect(res.body.permanentLive).to.be.true 96 expect(live.permanentLive).to.be.true
104 97
105 await waitJobs(servers) 98 await waitJobs(servers)
106 }) 99 })
@@ -108,16 +101,16 @@ describe('Permanent live', function () {
108 it('Should stream into this permanent live', async function () { 101 it('Should stream into this permanent live', async function () {
109 this.timeout(120000) 102 this.timeout(120000)
110 103
111 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, videoUUID) 104 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: videoUUID })
112 105
113 for (const server of servers) { 106 for (const server of servers) {
114 await waitUntilLivePublished(server.url, server.accessToken, videoUUID) 107 await server.live.waitUntilPublished({ videoId: videoUUID })
115 } 108 }
116 109
117 await checkVideoState(videoUUID, VideoState.PUBLISHED) 110 await checkVideoState(videoUUID, VideoState.PUBLISHED)
118 111
119 await stopFfmpeg(command) 112 await stopFfmpeg(ffmpegCommand)
120 await waitUntilLiveWaiting(servers[0].url, servers[0].accessToken, videoUUID) 113 await servers[0].live.waitUntilWaiting({ videoId: videoUUID })
121 114
122 await waitJobs(servers) 115 await waitJobs(servers)
123 }) 116 })
@@ -129,9 +122,7 @@ describe('Permanent live', function () {
129 await waitJobs(servers) 122 await waitJobs(servers)
130 123
131 for (const server of servers) { 124 for (const server of servers) {
132 const res = await getVideo(server.url, videoUUID) 125 const videoDetails = await server.videos.get({ id: videoUUID })
133
134 const videoDetails = res.body as VideoDetails
135 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1) 126 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
136 } 127 }
137 }) 128 })
@@ -145,31 +136,33 @@ describe('Permanent live', function () {
145 it('Should be able to stream again in the permanent live', async function () { 136 it('Should be able to stream again in the permanent live', async function () {
146 this.timeout(20000) 137 this.timeout(20000)
147 138
148 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 139 await servers[0].config.updateCustomSubConfig({
149 live: { 140 newConfig: {
150 enabled: true, 141 live: {
151 allowReplay: true,
152 maxDuration: -1,
153 transcoding: {
154 enabled: true, 142 enabled: true,
155 resolutions: getCustomConfigResolutions(false) 143 allowReplay: true,
144 maxDuration: -1,
145 transcoding: {
146 enabled: true,
147 resolutions: ConfigCommand.getCustomConfigResolutions(false)
148 }
156 } 149 }
157 } 150 }
158 }) 151 })
159 152
160 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, videoUUID) 153 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: videoUUID })
161 154
162 for (const server of servers) { 155 for (const server of servers) {
163 await waitUntilLivePublished(server.url, server.accessToken, videoUUID) 156 await server.live.waitUntilPublished({ videoId: videoUUID })
164 } 157 }
165 158
166 await checkVideoState(videoUUID, VideoState.PUBLISHED) 159 await checkVideoState(videoUUID, VideoState.PUBLISHED)
167 160
168 const count = await getPlaylistsCount(servers[0], videoUUID) 161 const count = await servers[0].live.countPlaylists({ videoUUID })
169 // master playlist and 720p playlist 162 // master playlist and 720p playlist
170 expect(count).to.equal(2) 163 expect(count).to.equal(2)
171 164
172 await stopFfmpeg(command) 165 await stopFfmpeg(ffmpegCommand)
173 }) 166 })
174 167
175 after(async function () { 168 after(async function () {
diff --git a/server/tests/api/live/live-save-replay.ts b/server/tests/api/live/live-save-replay.ts
index 3d4736c8f..8f1fb78a5 100644
--- a/server/tests/api/live/live-save-replay.ts
+++ b/server/tests/api/live/live-save-replay.ts
@@ -3,97 +3,85 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { FfmpegCommand } from 'fluent-ffmpeg' 5import { FfmpegCommand } from 'fluent-ffmpeg'
6import { LiveVideoCreate, VideoDetails, VideoPrivacy, VideoState } from '@shared/models'
7import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
8import { 6import {
9 addVideoToBlacklist, 7 checkLiveCleanupAfterSave,
10 checkLiveCleanup,
11 cleanupTests, 8 cleanupTests,
12 createLive, 9 ConfigCommand,
10 createMultipleServers,
13 doubleFollow, 11 doubleFollow,
14 flushAndRunMultipleServers, 12 PeerTubeServer,
15 getCustomConfigResolutions,
16 getVideo,
17 getVideosList,
18 removeVideo,
19 sendRTMPStreamInVideo,
20 ServerInfo,
21 setAccessTokensToServers, 13 setAccessTokensToServers,
22 setDefaultVideoChannel, 14 setDefaultVideoChannel,
23 stopFfmpeg, 15 stopFfmpeg,
24 testFfmpegStreamError, 16 testFfmpegStreamError,
25 updateCustomSubConfig,
26 updateVideo,
27 wait, 17 wait,
28 waitJobs, 18 waitJobs
29 waitUntilLiveEnded, 19} from '@shared/extra-utils'
30 waitUntilLivePublished, 20import { HttpStatusCode, LiveVideoCreate, VideoPrivacy, VideoState } from '@shared/models'
31 waitUntilLiveSaved
32} from '../../../../shared/extra-utils'
33 21
34const expect = chai.expect 22const expect = chai.expect
35 23
36describe('Save replay setting', function () { 24describe('Save replay setting', function () {
37 let servers: ServerInfo[] = [] 25 let servers: PeerTubeServer[] = []
38 let liveVideoUUID: string 26 let liveVideoUUID: string
39 let ffmpegCommand: FfmpegCommand 27 let ffmpegCommand: FfmpegCommand
40 28
41 async function createLiveWrapper (saveReplay: boolean) { 29 async function createLiveWrapper (saveReplay: boolean) {
42 if (liveVideoUUID) { 30 if (liveVideoUUID) {
43 try { 31 try {
44 await removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 32 await servers[0].videos.remove({ id: liveVideoUUID })
45 await waitJobs(servers) 33 await waitJobs(servers)
46 } catch {} 34 } catch {}
47 } 35 }
48 36
49 const attributes: LiveVideoCreate = { 37 const attributes: LiveVideoCreate = {
50 channelId: servers[0].videoChannel.id, 38 channelId: servers[0].store.channel.id,
51 privacy: VideoPrivacy.PUBLIC, 39 privacy: VideoPrivacy.PUBLIC,
52 name: 'my super live', 40 name: 'my super live',
53 saveReplay 41 saveReplay
54 } 42 }
55 43
56 const res = await createLive(servers[0].url, servers[0].accessToken, attributes) 44 const { uuid } = await servers[0].live.create({ fields: attributes })
57 return res.body.video.uuid 45 return uuid
58 } 46 }
59 47
60 async function checkVideosExist (videoId: string, existsInList: boolean, getStatus?: number) { 48 async function checkVideosExist (videoId: string, existsInList: boolean, expectedStatus?: number) {
61 for (const server of servers) { 49 for (const server of servers) {
62 const length = existsInList ? 1 : 0 50 const length = existsInList ? 1 : 0
63 51
64 const resVideos = await getVideosList(server.url) 52 const { data, total } = await server.videos.list()
65 expect(resVideos.body.data).to.have.lengthOf(length) 53 expect(data).to.have.lengthOf(length)
66 expect(resVideos.body.total).to.equal(length) 54 expect(total).to.equal(length)
67 55
68 if (getStatus) { 56 if (expectedStatus) {
69 await getVideo(server.url, videoId, getStatus) 57 await server.videos.get({ id: videoId, expectedStatus })
70 } 58 }
71 } 59 }
72 } 60 }
73 61
74 async function checkVideoState (videoId: string, state: VideoState) { 62 async function checkVideoState (videoId: string, state: VideoState) {
75 for (const server of servers) { 63 for (const server of servers) {
76 const res = await getVideo(server.url, videoId) 64 const video = await server.videos.get({ id: videoId })
77 expect((res.body as VideoDetails).state.id).to.equal(state) 65 expect(video.state.id).to.equal(state)
78 } 66 }
79 } 67 }
80 68
81 async function waitUntilLivePublishedOnAllServers (videoId: string) { 69 async function waitUntilLivePublishedOnAllServers (videoId: string) {
82 for (const server of servers) { 70 for (const server of servers) {
83 await waitUntilLivePublished(server.url, server.accessToken, videoId) 71 await server.live.waitUntilPublished({ videoId })
84 } 72 }
85 } 73 }
86 74
87 async function waitUntilLiveSavedOnAllServers (videoId: string) { 75 async function waitUntilLiveSavedOnAllServers (videoId: string) {
88 for (const server of servers) { 76 for (const server of servers) {
89 await waitUntilLiveSaved(server.url, server.accessToken, videoId) 77 await server.live.waitUntilSaved({ videoId })
90 } 78 }
91 } 79 }
92 80
93 before(async function () { 81 before(async function () {
94 this.timeout(120000) 82 this.timeout(120000)
95 83
96 servers = await flushAndRunMultipleServers(2) 84 servers = await createMultipleServers(2)
97 85
98 // Get the access tokens 86 // Get the access tokens
99 await setAccessTokensToServers(servers) 87 await setAccessTokensToServers(servers)
@@ -102,14 +90,16 @@ describe('Save replay setting', function () {
102 // Server 1 and server 2 follow each other 90 // Server 1 and server 2 follow each other
103 await doubleFollow(servers[0], servers[1]) 91 await doubleFollow(servers[0], servers[1])
104 92
105 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 93 await servers[0].config.updateCustomSubConfig({
106 live: { 94 newConfig: {
107 enabled: true, 95 live: {
108 allowReplay: true, 96 enabled: true,
109 maxDuration: -1, 97 allowReplay: true,
110 transcoding: { 98 maxDuration: -1,
111 enabled: false, 99 transcoding: {
112 resolutions: getCustomConfigResolutions(true) 100 enabled: false,
101 resolutions: ConfigCommand.getCustomConfigResolutions(true)
102 }
113 } 103 }
114 } 104 }
115 }) 105 })
@@ -135,7 +125,7 @@ describe('Save replay setting', function () {
135 it('Should correctly have updated the live and federated it when streaming in the live', async function () { 125 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
136 this.timeout(30000) 126 this.timeout(30000)
137 127
138 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 128 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
139 129
140 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 130 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
141 131
@@ -151,7 +141,7 @@ describe('Save replay setting', function () {
151 await stopFfmpeg(ffmpegCommand) 141 await stopFfmpeg(ffmpegCommand)
152 142
153 for (const server of servers) { 143 for (const server of servers) {
154 await waitUntilLiveEnded(server.url, server.accessToken, liveVideoUUID) 144 await server.live.waitUntilEnded({ videoId: liveVideoUUID })
155 } 145 }
156 await waitJobs(servers) 146 await waitJobs(servers)
157 147
@@ -160,7 +150,7 @@ describe('Save replay setting', function () {
160 await checkVideoState(liveVideoUUID, VideoState.LIVE_ENDED) 150 await checkVideoState(liveVideoUUID, VideoState.LIVE_ENDED)
161 151
162 // No resolutions saved since we did not save replay 152 // No resolutions saved since we did not save replay
163 await checkLiveCleanup(servers[0], liveVideoUUID, []) 153 await checkLiveCleanupAfterSave(servers[0], liveVideoUUID, [])
164 }) 154 })
165 155
166 it('Should correctly terminate the stream on blacklist and delete the live', async function () { 156 it('Should correctly terminate the stream on blacklist and delete the live', async function () {
@@ -168,7 +158,7 @@ describe('Save replay setting', function () {
168 158
169 liveVideoUUID = await createLiveWrapper(false) 159 liveVideoUUID = await createLiveWrapper(false)
170 160
171 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 161 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
172 162
173 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 163 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
174 164
@@ -176,7 +166,7 @@ describe('Save replay setting', function () {
176 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200) 166 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
177 167
178 await Promise.all([ 168 await Promise.all([
179 addVideoToBlacklist(servers[0].url, servers[0].accessToken, liveVideoUUID, 'bad live', true), 169 servers[0].blacklist.add({ videoId: liveVideoUUID, reason: 'bad live', unfederate: true }),
180 testFfmpegStreamError(ffmpegCommand, true) 170 testFfmpegStreamError(ffmpegCommand, true)
181 ]) 171 ])
182 172
@@ -184,12 +174,12 @@ describe('Save replay setting', function () {
184 174
185 await checkVideosExist(liveVideoUUID, false) 175 await checkVideosExist(liveVideoUUID, false)
186 176
187 await getVideo(servers[0].url, liveVideoUUID, HttpStatusCode.UNAUTHORIZED_401) 177 await servers[0].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
188 await getVideo(servers[1].url, liveVideoUUID, HttpStatusCode.NOT_FOUND_404) 178 await servers[1].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
189 179
190 await wait(5000) 180 await wait(5000)
191 await waitJobs(servers) 181 await waitJobs(servers)
192 await checkLiveCleanup(servers[0], liveVideoUUID, []) 182 await checkLiveCleanupAfterSave(servers[0], liveVideoUUID, [])
193 }) 183 })
194 184
195 it('Should correctly terminate the stream on delete and delete the video', async function () { 185 it('Should correctly terminate the stream on delete and delete the video', async function () {
@@ -197,7 +187,7 @@ describe('Save replay setting', function () {
197 187
198 liveVideoUUID = await createLiveWrapper(false) 188 liveVideoUUID = await createLiveWrapper(false)
199 189
200 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 190 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
201 191
202 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 192 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
203 193
@@ -206,14 +196,14 @@ describe('Save replay setting', function () {
206 196
207 await Promise.all([ 197 await Promise.all([
208 testFfmpegStreamError(ffmpegCommand, true), 198 testFfmpegStreamError(ffmpegCommand, true),
209 removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 199 servers[0].videos.remove({ id: liveVideoUUID })
210 ]) 200 ])
211 201
212 await wait(5000) 202 await wait(5000)
213 await waitJobs(servers) 203 await waitJobs(servers)
214 204
215 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404) 205 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
216 await checkLiveCleanup(servers[0], liveVideoUUID, []) 206 await checkLiveCleanupAfterSave(servers[0], liveVideoUUID, [])
217 }) 207 })
218 }) 208 })
219 209
@@ -233,7 +223,7 @@ describe('Save replay setting', function () {
233 it('Should correctly have updated the live and federated it when streaming in the live', async function () { 223 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
234 this.timeout(20000) 224 this.timeout(20000)
235 225
236 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 226 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
237 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 227 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
238 228
239 await waitJobs(servers) 229 await waitJobs(servers)
@@ -258,18 +248,18 @@ describe('Save replay setting', function () {
258 it('Should update the saved live and correctly federate the updated attributes', async function () { 248 it('Should update the saved live and correctly federate the updated attributes', async function () {
259 this.timeout(30000) 249 this.timeout(30000)
260 250
261 await updateVideo(servers[0].url, servers[0].accessToken, liveVideoUUID, { name: 'video updated' }) 251 await servers[0].videos.update({ id: liveVideoUUID, attributes: { name: 'video updated' } })
262 await waitJobs(servers) 252 await waitJobs(servers)
263 253
264 for (const server of servers) { 254 for (const server of servers) {
265 const res = await getVideo(server.url, liveVideoUUID) 255 const video = await server.videos.get({ id: liveVideoUUID })
266 expect(res.body.name).to.equal('video updated') 256 expect(video.name).to.equal('video updated')
267 expect(res.body.isLive).to.be.false 257 expect(video.isLive).to.be.false
268 } 258 }
269 }) 259 })
270 260
271 it('Should have cleaned up the live files', async function () { 261 it('Should have cleaned up the live files', async function () {
272 await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ]) 262 await checkLiveCleanupAfterSave(servers[0], liveVideoUUID, [ 720 ])
273 }) 263 })
274 264
275 it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () { 265 it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () {
@@ -277,14 +267,14 @@ describe('Save replay setting', function () {
277 267
278 liveVideoUUID = await createLiveWrapper(true) 268 liveVideoUUID = await createLiveWrapper(true)
279 269
280 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 270 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
281 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 271 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
282 272
283 await waitJobs(servers) 273 await waitJobs(servers)
284 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200) 274 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
285 275
286 await Promise.all([ 276 await Promise.all([
287 addVideoToBlacklist(servers[0].url, servers[0].accessToken, liveVideoUUID, 'bad live', true), 277 servers[0].blacklist.add({ videoId: liveVideoUUID, reason: 'bad live', unfederate: true }),
288 testFfmpegStreamError(ffmpegCommand, true) 278 testFfmpegStreamError(ffmpegCommand, true)
289 ]) 279 ])
290 280
@@ -292,12 +282,12 @@ describe('Save replay setting', function () {
292 282
293 await checkVideosExist(liveVideoUUID, false) 283 await checkVideosExist(liveVideoUUID, false)
294 284
295 await getVideo(servers[0].url, liveVideoUUID, HttpStatusCode.UNAUTHORIZED_401) 285 await servers[0].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
296 await getVideo(servers[1].url, liveVideoUUID, HttpStatusCode.NOT_FOUND_404) 286 await servers[1].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
297 287
298 await wait(5000) 288 await wait(5000)
299 await waitJobs(servers) 289 await waitJobs(servers)
300 await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ]) 290 await checkLiveCleanupAfterSave(servers[0], liveVideoUUID, [ 720 ])
301 }) 291 })
302 292
303 it('Should correctly terminate the stream on delete and delete the video', async function () { 293 it('Should correctly terminate the stream on delete and delete the video', async function () {
@@ -305,14 +295,14 @@ describe('Save replay setting', function () {
305 295
306 liveVideoUUID = await createLiveWrapper(true) 296 liveVideoUUID = await createLiveWrapper(true)
307 297
308 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 298 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
309 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 299 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
310 300
311 await waitJobs(servers) 301 await waitJobs(servers)
312 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200) 302 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
313 303
314 await Promise.all([ 304 await Promise.all([
315 removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID), 305 servers[0].videos.remove({ id: liveVideoUUID }),
316 testFfmpegStreamError(ffmpegCommand, true) 306 testFfmpegStreamError(ffmpegCommand, true)
317 ]) 307 ])
318 308
@@ -320,7 +310,7 @@ describe('Save replay setting', function () {
320 await waitJobs(servers) 310 await waitJobs(servers)
321 311
322 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404) 312 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
323 await checkLiveCleanup(servers[0], liveVideoUUID, []) 313 await checkLiveCleanupAfterSave(servers[0], liveVideoUUID, [])
324 }) 314 })
325 }) 315 })
326 316
diff --git a/server/tests/api/live/live-socket-messages.ts b/server/tests/api/live/live-socket-messages.ts
index e00909ade..2a1f9f108 100644
--- a/server/tests/api/live/live-socket-messages.ts
+++ b/server/tests/api/live/live-socket-messages.ts
@@ -2,47 +2,42 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { getLiveNotificationSocket } from '@shared/extra-utils/socket/socket-io'
6import { VideoPrivacy, VideoState } from '@shared/models' 5import { VideoPrivacy, VideoState } from '@shared/models'
7import { 6import {
8 cleanupTests, 7 cleanupTests,
9 createLive, 8 createMultipleServers,
10 doubleFollow, 9 doubleFollow,
11 flushAndRunMultipleServers, 10 PeerTubeServer,
12 getVideoIdFromUUID,
13 sendRTMPStreamInVideo,
14 ServerInfo,
15 setAccessTokensToServers, 11 setAccessTokensToServers,
16 setDefaultVideoChannel, 12 setDefaultVideoChannel,
17 stopFfmpeg, 13 stopFfmpeg,
18 updateCustomSubConfig,
19 viewVideo,
20 wait, 14 wait,
21 waitJobs, 15 waitJobs,
22 waitUntilLiveEnded,
23 waitUntilLivePublishedOnAllServers 16 waitUntilLivePublishedOnAllServers
24} from '../../../../shared/extra-utils' 17} from '../../../../shared/extra-utils'
25 18
26const expect = chai.expect 19const expect = chai.expect
27 20
28describe('Test live', function () { 21describe('Test live', function () {
29 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
30 23
31 before(async function () { 24 before(async function () {
32 this.timeout(120000) 25 this.timeout(120000)
33 26
34 servers = await flushAndRunMultipleServers(2) 27 servers = await createMultipleServers(2)
35 28
36 // Get the access tokens 29 // Get the access tokens
37 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
38 await setDefaultVideoChannel(servers) 31 await setDefaultVideoChannel(servers)
39 32
40 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 33 await servers[0].config.updateCustomSubConfig({
41 live: { 34 newConfig: {
42 enabled: true, 35 live: {
43 allowReplay: true, 36 enabled: true,
44 transcoding: { 37 allowReplay: true,
45 enabled: false 38 transcoding: {
39 enabled: false
40 }
46 } 41 }
47 } 42 }
48 }) 43 })
@@ -56,12 +51,12 @@ describe('Test live', function () {
56 async function createLiveWrapper () { 51 async function createLiveWrapper () {
57 const liveAttributes = { 52 const liveAttributes = {
58 name: 'live video', 53 name: 'live video',
59 channelId: servers[0].videoChannel.id, 54 channelId: servers[0].store.channel.id,
60 privacy: VideoPrivacy.PUBLIC 55 privacy: VideoPrivacy.PUBLIC
61 } 56 }
62 57
63 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 58 const { uuid } = await servers[0].live.create({ fields: liveAttributes })
64 return res.body.video.uuid 59 return uuid
65 } 60 }
66 61
67 it('Should correctly send a message when the live starts and ends', async function () { 62 it('Should correctly send a message when the live starts and ends', async function () {
@@ -74,22 +69,22 @@ describe('Test live', function () {
74 await waitJobs(servers) 69 await waitJobs(servers)
75 70
76 { 71 {
77 const videoId = await getVideoIdFromUUID(servers[0].url, liveVideoUUID) 72 const videoId = await servers[0].videos.getId({ uuid: liveVideoUUID })
78 73
79 const localSocket = getLiveNotificationSocket(servers[0].url) 74 const localSocket = servers[0].socketIO.getLiveNotificationSocket()
80 localSocket.on('state-change', data => localStateChanges.push(data.state)) 75 localSocket.on('state-change', data => localStateChanges.push(data.state))
81 localSocket.emit('subscribe', { videoId }) 76 localSocket.emit('subscribe', { videoId })
82 } 77 }
83 78
84 { 79 {
85 const videoId = await getVideoIdFromUUID(servers[1].url, liveVideoUUID) 80 const videoId = await servers[1].videos.getId({ uuid: liveVideoUUID })
86 81
87 const remoteSocket = getLiveNotificationSocket(servers[1].url) 82 const remoteSocket = servers[1].socketIO.getLiveNotificationSocket()
88 remoteSocket.on('state-change', data => remoteStateChanges.push(data.state)) 83 remoteSocket.on('state-change', data => remoteStateChanges.push(data.state))
89 remoteSocket.emit('subscribe', { videoId }) 84 remoteSocket.emit('subscribe', { videoId })
90 } 85 }
91 86
92 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 87 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
93 88
94 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID) 89 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
95 await waitJobs(servers) 90 await waitJobs(servers)
@@ -99,10 +94,10 @@ describe('Test live', function () {
99 expect(stateChanges[stateChanges.length - 1]).to.equal(VideoState.PUBLISHED) 94 expect(stateChanges[stateChanges.length - 1]).to.equal(VideoState.PUBLISHED)
100 } 95 }
101 96
102 await stopFfmpeg(command) 97 await stopFfmpeg(ffmpegCommand)
103 98
104 for (const server of servers) { 99 for (const server of servers) {
105 await waitUntilLiveEnded(server.url, server.accessToken, liveVideoUUID) 100 await server.live.waitUntilEnded({ videoId: liveVideoUUID })
106 } 101 }
107 await waitJobs(servers) 102 await waitJobs(servers)
108 103
@@ -122,22 +117,22 @@ describe('Test live', function () {
122 await waitJobs(servers) 117 await waitJobs(servers)
123 118
124 { 119 {
125 const videoId = await getVideoIdFromUUID(servers[0].url, liveVideoUUID) 120 const videoId = await servers[0].videos.getId({ uuid: liveVideoUUID })
126 121
127 const localSocket = getLiveNotificationSocket(servers[0].url) 122 const localSocket = servers[0].socketIO.getLiveNotificationSocket()
128 localSocket.on('views-change', data => { localLastVideoViews = data.views }) 123 localSocket.on('views-change', data => { localLastVideoViews = data.views })
129 localSocket.emit('subscribe', { videoId }) 124 localSocket.emit('subscribe', { videoId })
130 } 125 }
131 126
132 { 127 {
133 const videoId = await getVideoIdFromUUID(servers[1].url, liveVideoUUID) 128 const videoId = await servers[1].videos.getId({ uuid: liveVideoUUID })
134 129
135 const remoteSocket = getLiveNotificationSocket(servers[1].url) 130 const remoteSocket = servers[1].socketIO.getLiveNotificationSocket()
136 remoteSocket.on('views-change', data => { remoteLastVideoViews = data.views }) 131 remoteSocket.on('views-change', data => { remoteLastVideoViews = data.views })
137 remoteSocket.emit('subscribe', { videoId }) 132 remoteSocket.emit('subscribe', { videoId })
138 } 133 }
139 134
140 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 135 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
141 136
142 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID) 137 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
143 await waitJobs(servers) 138 await waitJobs(servers)
@@ -145,8 +140,8 @@ describe('Test live', function () {
145 expect(localLastVideoViews).to.equal(0) 140 expect(localLastVideoViews).to.equal(0)
146 expect(remoteLastVideoViews).to.equal(0) 141 expect(remoteLastVideoViews).to.equal(0)
147 142
148 await viewVideo(servers[0].url, liveVideoUUID) 143 await servers[0].videos.view({ id: liveVideoUUID })
149 await viewVideo(servers[1].url, liveVideoUUID) 144 await servers[1].videos.view({ id: liveVideoUUID })
150 145
151 await waitJobs(servers) 146 await waitJobs(servers)
152 await wait(5000) 147 await wait(5000)
@@ -155,7 +150,7 @@ describe('Test live', function () {
155 expect(localLastVideoViews).to.equal(2) 150 expect(localLastVideoViews).to.equal(2)
156 expect(remoteLastVideoViews).to.equal(2) 151 expect(remoteLastVideoViews).to.equal(2)
157 152
158 await stopFfmpeg(command) 153 await stopFfmpeg(ffmpegCommand)
159 }) 154 })
160 155
161 it('Should not receive a notification after unsubscribe', async function () { 156 it('Should not receive a notification after unsubscribe', async function () {
@@ -166,13 +161,13 @@ describe('Test live', function () {
166 const liveVideoUUID = await createLiveWrapper() 161 const liveVideoUUID = await createLiveWrapper()
167 await waitJobs(servers) 162 await waitJobs(servers)
168 163
169 const videoId = await getVideoIdFromUUID(servers[0].url, liveVideoUUID) 164 const videoId = await servers[0].videos.getId({ uuid: liveVideoUUID })
170 165
171 const socket = getLiveNotificationSocket(servers[0].url) 166 const socket = servers[0].socketIO.getLiveNotificationSocket()
172 socket.on('state-change', data => stateChanges.push(data.state)) 167 socket.on('state-change', data => stateChanges.push(data.state))
173 socket.emit('subscribe', { videoId }) 168 socket.emit('subscribe', { videoId })
174 169
175 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 170 const command = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
176 171
177 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID) 172 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
178 await waitJobs(servers) 173 await waitJobs(servers)
diff --git a/server/tests/api/live/live-views.ts b/server/tests/api/live/live-views.ts
index a44d21ffa..5e3a79c64 100644
--- a/server/tests/api/live/live-views.ts
+++ b/server/tests/api/live/live-views.ts
@@ -3,20 +3,15 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { FfmpegCommand } from 'fluent-ffmpeg' 5import { FfmpegCommand } from 'fluent-ffmpeg'
6import { VideoDetails, VideoPrivacy } from '@shared/models' 6import { VideoPrivacy } from '@shared/models'
7import { 7import {
8 cleanupTests, 8 cleanupTests,
9 createLive, 9 createMultipleServers,
10 doubleFollow, 10 doubleFollow,
11 flushAndRunMultipleServers, 11 PeerTubeServer,
12 getVideo,
13 sendRTMPStreamInVideo,
14 ServerInfo,
15 setAccessTokensToServers, 12 setAccessTokensToServers,
16 setDefaultVideoChannel, 13 setDefaultVideoChannel,
17 stopFfmpeg, 14 stopFfmpeg,
18 updateCustomSubConfig,
19 viewVideo,
20 wait, 15 wait,
21 waitJobs, 16 waitJobs,
22 waitUntilLivePublishedOnAllServers 17 waitUntilLivePublishedOnAllServers
@@ -25,23 +20,25 @@ import {
25const expect = chai.expect 20const expect = chai.expect
26 21
27describe('Test live', function () { 22describe('Test live', function () {
28 let servers: ServerInfo[] = [] 23 let servers: PeerTubeServer[] = []
29 24
30 before(async function () { 25 before(async function () {
31 this.timeout(120000) 26 this.timeout(120000)
32 27
33 servers = await flushAndRunMultipleServers(2) 28 servers = await createMultipleServers(2)
34 29
35 // Get the access tokens 30 // Get the access tokens
36 await setAccessTokensToServers(servers) 31 await setAccessTokensToServers(servers)
37 await setDefaultVideoChannel(servers) 32 await setDefaultVideoChannel(servers)
38 33
39 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 34 await servers[0].config.updateCustomSubConfig({
40 live: { 35 newConfig: {
41 enabled: true, 36 live: {
42 allowReplay: true, 37 enabled: true,
43 transcoding: { 38 allowReplay: true,
44 enabled: false 39 transcoding: {
40 enabled: false
41 }
45 } 42 }
46 } 43 }
47 }) 44 })
@@ -56,9 +53,7 @@ describe('Test live', function () {
56 53
57 async function countViews (expected: number) { 54 async function countViews (expected: number) {
58 for (const server of servers) { 55 for (const server of servers) {
59 const res = await getVideo(server.url, liveVideoId) 56 const video = await server.videos.get({ id: liveVideoId })
60 const video: VideoDetails = res.body
61
62 expect(video.views).to.equal(expected) 57 expect(video.views).to.equal(expected)
63 } 58 }
64 } 59 }
@@ -68,14 +63,14 @@ describe('Test live', function () {
68 63
69 const liveAttributes = { 64 const liveAttributes = {
70 name: 'live video', 65 name: 'live video',
71 channelId: servers[0].videoChannel.id, 66 channelId: servers[0].store.channel.id,
72 privacy: VideoPrivacy.PUBLIC 67 privacy: VideoPrivacy.PUBLIC
73 } 68 }
74 69
75 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 70 const live = await servers[0].live.create({ fields: liveAttributes })
76 liveVideoId = res.body.video.uuid 71 liveVideoId = live.uuid
77 72
78 command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId) 73 command = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoId })
79 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 74 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
80 await waitJobs(servers) 75 await waitJobs(servers)
81 }) 76 })
@@ -87,8 +82,8 @@ describe('Test live', function () {
87 it('Should view a live twice and display 1 view', async function () { 82 it('Should view a live twice and display 1 view', async function () {
88 this.timeout(30000) 83 this.timeout(30000)
89 84
90 await viewVideo(servers[0].url, liveVideoId) 85 await servers[0].videos.view({ id: liveVideoId })
91 await viewVideo(servers[0].url, liveVideoId) 86 await servers[0].videos.view({ id: liveVideoId })
92 87
93 await wait(7000) 88 await wait(7000)
94 89
@@ -109,9 +104,9 @@ describe('Test live', function () {
109 it('Should view a live on a remote and on local and display 2 views', async function () { 104 it('Should view a live on a remote and on local and display 2 views', async function () {
110 this.timeout(30000) 105 this.timeout(30000)
111 106
112 await viewVideo(servers[0].url, liveVideoId) 107 await servers[0].videos.view({ id: liveVideoId })
113 await viewVideo(servers[1].url, liveVideoId) 108 await servers[1].videos.view({ id: liveVideoId })
114 await viewVideo(servers[1].url, liveVideoId) 109 await servers[1].videos.view({ id: liveVideoId })
115 110
116 await wait(7000) 111 await wait(7000)
117 await waitJobs(servers) 112 await waitJobs(servers)
diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts
index 50397924e..d555cff19 100644
--- a/server/tests/api/live/live.ts
+++ b/server/tests/api/live/live.ts
@@ -2,75 +2,70 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { join } from 'path' 5import { basename, join } from 'path'
6import { ffprobePromise, getVideoStreamFromFile } from '@server/helpers/ffprobe-utils' 6import { ffprobePromise, getVideoStreamFromFile } from '@server/helpers/ffprobe-utils'
7import { LiveVideo, LiveVideoCreate, Video, VideoDetails, VideoPrivacy, VideoState, VideoStreamingPlaylistType } from '@shared/models'
8import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
9import { 7import {
10 addVideoToBlacklist, 8 checkLiveCleanupAfterSave,
11 buildServerDirectory,
12 checkLiveCleanup,
13 checkLiveSegmentHash, 9 checkLiveSegmentHash,
14 checkResolutionsInMasterPlaylist, 10 checkResolutionsInMasterPlaylist,
15 cleanupTests, 11 cleanupTests,
16 createLive, 12 createMultipleServers,
17 doubleFollow, 13 doubleFollow,
18 flushAndRunMultipleServers,
19 getLive,
20 getMyVideosWithFilter,
21 getPlaylist,
22 getVideo,
23 getVideosList,
24 getVideosWithFilters,
25 killallServers, 14 killallServers,
15 LiveCommand,
26 makeRawRequest, 16 makeRawRequest,
27 removeVideo, 17 PeerTubeServer,
28 reRunServer,
29 sendRTMPStream, 18 sendRTMPStream,
30 sendRTMPStreamInVideo,
31 ServerInfo,
32 setAccessTokensToServers, 19 setAccessTokensToServers,
33 setDefaultVideoChannel, 20 setDefaultVideoChannel,
34 stopFfmpeg, 21 stopFfmpeg,
35 testFfmpegStreamError, 22 testFfmpegStreamError,
36 testImage, 23 testImage,
37 updateCustomSubConfig,
38 updateLive,
39 uploadVideoAndGetId,
40 wait, 24 wait,
41 waitJobs, 25 waitJobs,
42 waitUntilLiveEnded, 26 waitUntilLivePublishedOnAllServers
43 waitUntilLivePublished, 27} from '@shared/extra-utils'
44 waitUntilLivePublishedOnAllServers, 28import {
45 waitUntilLiveSegmentGeneration 29 HttpStatusCode,
46} from '../../../../shared/extra-utils' 30 LiveVideo,
31 LiveVideoCreate,
32 VideoDetails,
33 VideoPrivacy,
34 VideoState,
35 VideoStreamingPlaylistType
36} from '@shared/models'
47 37
48const expect = chai.expect 38const expect = chai.expect
49 39
50describe('Test live', function () { 40describe('Test live', function () {
51 let servers: ServerInfo[] = [] 41 let servers: PeerTubeServer[] = []
42 let commands: LiveCommand[]
52 43
53 before(async function () { 44 before(async function () {
54 this.timeout(120000) 45 this.timeout(120000)
55 46
56 servers = await flushAndRunMultipleServers(2) 47 servers = await createMultipleServers(2)
57 48
58 // Get the access tokens 49 // Get the access tokens
59 await setAccessTokensToServers(servers) 50 await setAccessTokensToServers(servers)
60 await setDefaultVideoChannel(servers) 51 await setDefaultVideoChannel(servers)
61 52
62 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 53 await servers[0].config.updateCustomSubConfig({
63 live: { 54 newConfig: {
64 enabled: true, 55 live: {
65 allowReplay: true, 56 enabled: true,
66 transcoding: { 57 allowReplay: true,
67 enabled: false 58 transcoding: {
59 enabled: false
60 }
68 } 61 }
69 } 62 }
70 }) 63 })
71 64
72 // Server 1 and server 2 follow each other 65 // Server 1 and server 2 follow each other
73 await doubleFollow(servers[0], servers[1]) 66 await doubleFollow(servers[0], servers[1])
67
68 commands = servers.map(s => s.live)
74 }) 69 })
75 70
76 describe('Live creation, update and delete', function () { 71 describe('Live creation, update and delete', function () {
@@ -85,7 +80,7 @@ describe('Test live', function () {
85 language: 'fr', 80 language: 'fr',
86 description: 'super live description', 81 description: 'super live description',
87 support: 'support field', 82 support: 'support field',
88 channelId: servers[0].videoChannel.id, 83 channelId: servers[0].store.channel.id,
89 nsfw: false, 84 nsfw: false,
90 waitTranscoding: false, 85 waitTranscoding: false,
91 name: 'my super live', 86 name: 'my super live',
@@ -98,14 +93,13 @@ describe('Test live', function () {
98 thumbnailfile: 'video_short1.webm.jpg' 93 thumbnailfile: 'video_short1.webm.jpg'
99 } 94 }
100 95
101 const res = await createLive(servers[0].url, servers[0].accessToken, attributes) 96 const live = await commands[0].create({ fields: attributes })
102 liveVideoUUID = res.body.video.uuid 97 liveVideoUUID = live.uuid
103 98
104 await waitJobs(servers) 99 await waitJobs(servers)
105 100
106 for (const server of servers) { 101 for (const server of servers) {
107 const resVideo = await getVideo(server.url, liveVideoUUID) 102 const video = await server.videos.get({ id: liveVideoUUID })
108 const video: VideoDetails = resVideo.body
109 103
110 expect(video.category.id).to.equal(1) 104 expect(video.category.id).to.equal(1)
111 expect(video.licence.id).to.equal(2) 105 expect(video.licence.id).to.equal(2)
@@ -113,8 +107,8 @@ describe('Test live', function () {
113 expect(video.description).to.equal('super live description') 107 expect(video.description).to.equal('super live description')
114 expect(video.support).to.equal('support field') 108 expect(video.support).to.equal('support field')
115 109
116 expect(video.channel.name).to.equal(servers[0].videoChannel.name) 110 expect(video.channel.name).to.equal(servers[0].store.channel.name)
117 expect(video.channel.host).to.equal(servers[0].videoChannel.host) 111 expect(video.channel.host).to.equal(servers[0].store.channel.host)
118 112
119 expect(video.isLive).to.be.true 113 expect(video.isLive).to.be.true
120 114
@@ -129,8 +123,7 @@ describe('Test live', function () {
129 await testImage(server.url, 'video_short1-preview.webm', video.previewPath) 123 await testImage(server.url, 'video_short1-preview.webm', video.previewPath)
130 await testImage(server.url, 'video_short1.webm', video.thumbnailPath) 124 await testImage(server.url, 'video_short1.webm', video.thumbnailPath)
131 125
132 const resLive = await getLive(server.url, server.accessToken, liveVideoUUID) 126 const live = await server.live.get({ videoId: liveVideoUUID })
133 const live: LiveVideo = resLive.body
134 127
135 if (server.url === servers[0].url) { 128 if (server.url === servers[0].url) {
136 expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':' + servers[0].rtmpPort + '/live') 129 expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':' + servers[0].rtmpPort + '/live')
@@ -149,20 +142,18 @@ describe('Test live', function () {
149 142
150 const attributes: LiveVideoCreate = { 143 const attributes: LiveVideoCreate = {
151 name: 'default live thumbnail', 144 name: 'default live thumbnail',
152 channelId: servers[0].videoChannel.id, 145 channelId: servers[0].store.channel.id,
153 privacy: VideoPrivacy.UNLISTED, 146 privacy: VideoPrivacy.UNLISTED,
154 nsfw: true 147 nsfw: true
155 } 148 }
156 149
157 const res = await createLive(servers[0].url, servers[0].accessToken, attributes) 150 const live = await commands[0].create({ fields: attributes })
158 const videoId = res.body.video.uuid 151 const videoId = live.uuid
159 152
160 await waitJobs(servers) 153 await waitJobs(servers)
161 154
162 for (const server of servers) { 155 for (const server of servers) {
163 const resVideo = await getVideo(server.url, videoId) 156 const video = await server.videos.get({ id: videoId })
164 const video: VideoDetails = resVideo.body
165
166 expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED) 157 expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED)
167 expect(video.nsfw).to.be.true 158 expect(video.nsfw).to.be.true
168 159
@@ -173,28 +164,27 @@ describe('Test live', function () {
173 164
174 it('Should not have the live listed since nobody streams into', async function () { 165 it('Should not have the live listed since nobody streams into', async function () {
175 for (const server of servers) { 166 for (const server of servers) {
176 const res = await getVideosList(server.url) 167 const { total, data } = await server.videos.list()
177 168
178 expect(res.body.total).to.equal(0) 169 expect(total).to.equal(0)
179 expect(res.body.data).to.have.lengthOf(0) 170 expect(data).to.have.lengthOf(0)
180 } 171 }
181 }) 172 })
182 173
183 it('Should not be able to update a live of another server', async function () { 174 it('Should not be able to update a live of another server', async function () {
184 await updateLive(servers[1].url, servers[1].accessToken, liveVideoUUID, { saveReplay: false }, HttpStatusCode.FORBIDDEN_403) 175 await commands[1].update({ videoId: liveVideoUUID, fields: { saveReplay: false }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
185 }) 176 })
186 177
187 it('Should update the live', async function () { 178 it('Should update the live', async function () {
188 this.timeout(10000) 179 this.timeout(10000)
189 180
190 await updateLive(servers[0].url, servers[0].accessToken, liveVideoUUID, { saveReplay: false }) 181 await commands[0].update({ videoId: liveVideoUUID, fields: { saveReplay: false } })
191 await waitJobs(servers) 182 await waitJobs(servers)
192 }) 183 })
193 184
194 it('Have the live updated', async function () { 185 it('Have the live updated', async function () {
195 for (const server of servers) { 186 for (const server of servers) {
196 const res = await getLive(server.url, server.accessToken, liveVideoUUID) 187 const live = await server.live.get({ videoId: liveVideoUUID })
197 const live: LiveVideo = res.body
198 188
199 if (server.url === servers[0].url) { 189 if (server.url === servers[0].url) {
200 expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':' + servers[0].rtmpPort + '/live') 190 expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':' + servers[0].rtmpPort + '/live')
@@ -211,77 +201,75 @@ describe('Test live', function () {
211 it('Delete the live', async function () { 201 it('Delete the live', async function () {
212 this.timeout(10000) 202 this.timeout(10000)
213 203
214 await removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 204 await servers[0].videos.remove({ id: liveVideoUUID })
215 await waitJobs(servers) 205 await waitJobs(servers)
216 }) 206 })
217 207
218 it('Should have the live deleted', async function () { 208 it('Should have the live deleted', async function () {
219 for (const server of servers) { 209 for (const server of servers) {
220 await getVideo(server.url, liveVideoUUID, HttpStatusCode.NOT_FOUND_404) 210 await server.videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
221 await getLive(server.url, server.accessToken, liveVideoUUID, HttpStatusCode.NOT_FOUND_404) 211 await server.live.get({ videoId: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
222 } 212 }
223 }) 213 })
224 }) 214 })
225 215
226 describe('Live filters', function () { 216 describe('Live filters', function () {
227 let command: any 217 let ffmpegCommand: any
228 let liveVideoId: string 218 let liveVideoId: string
229 let vodVideoId: string 219 let vodVideoId: string
230 220
231 before(async function () { 221 before(async function () {
232 this.timeout(120000) 222 this.timeout(120000)
233 223
234 vodVideoId = (await uploadVideoAndGetId({ server: servers[0], videoName: 'vod video' })).uuid 224 vodVideoId = (await servers[0].videos.quickUpload({ name: 'vod video' })).uuid
235 225
236 const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: servers[0].videoChannel.id } 226 const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: servers[0].store.channel.id }
237 const resLive = await createLive(servers[0].url, servers[0].accessToken, liveOptions) 227 const live = await commands[0].create({ fields: liveOptions })
238 liveVideoId = resLive.body.video.uuid 228 liveVideoId = live.uuid
239 229
240 command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId) 230 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoId })
241 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 231 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
242 await waitJobs(servers) 232 await waitJobs(servers)
243 }) 233 })
244 234
245 it('Should only display lives', async function () { 235 it('Should only display lives', async function () {
246 const res = await getVideosWithFilters(servers[0].url, { isLive: true }) 236 const { data, total } = await servers[0].videos.list({ isLive: true })
247 237
248 expect(res.body.total).to.equal(1) 238 expect(total).to.equal(1)
249 expect(res.body.data).to.have.lengthOf(1) 239 expect(data).to.have.lengthOf(1)
250 expect(res.body.data[0].name).to.equal('live') 240 expect(data[0].name).to.equal('live')
251 }) 241 })
252 242
253 it('Should not display lives', async function () { 243 it('Should not display lives', async function () {
254 const res = await getVideosWithFilters(servers[0].url, { isLive: false }) 244 const { data, total } = await servers[0].videos.list({ isLive: false })
255 245
256 expect(res.body.total).to.equal(1) 246 expect(total).to.equal(1)
257 expect(res.body.data).to.have.lengthOf(1) 247 expect(data).to.have.lengthOf(1)
258 expect(res.body.data[0].name).to.equal('vod video') 248 expect(data[0].name).to.equal('vod video')
259 }) 249 })
260 250
261 it('Should display my lives', async function () { 251 it('Should display my lives', async function () {
262 this.timeout(60000) 252 this.timeout(60000)
263 253
264 await stopFfmpeg(command) 254 await stopFfmpeg(ffmpegCommand)
265 await waitJobs(servers) 255 await waitJobs(servers)
266 256
267 const res = await getMyVideosWithFilter(servers[0].url, servers[0].accessToken, { isLive: true }) 257 const { data } = await servers[0].videos.listMyVideos({ isLive: true })
268 const videos = res.body.data as Video[]
269 258
270 const result = videos.every(v => v.isLive) 259 const result = data.every(v => v.isLive)
271 expect(result).to.be.true 260 expect(result).to.be.true
272 }) 261 })
273 262
274 it('Should not display my lives', async function () { 263 it('Should not display my lives', async function () {
275 const res = await getMyVideosWithFilter(servers[0].url, servers[0].accessToken, { isLive: false }) 264 const { data } = await servers[0].videos.listMyVideos({ isLive: false })
276 const videos = res.body.data as Video[]
277 265
278 const result = videos.every(v => !v.isLive) 266 const result = data.every(v => !v.isLive)
279 expect(result).to.be.true 267 expect(result).to.be.true
280 }) 268 })
281 269
282 after(async function () { 270 after(async function () {
283 await removeVideo(servers[0].url, servers[0].accessToken, vodVideoId) 271 await servers[0].videos.remove({ id: vodVideoId })
284 await removeVideo(servers[0].url, servers[0].accessToken, liveVideoId) 272 await servers[0].videos.remove({ id: liveVideoId })
285 }) 273 })
286 }) 274 })
287 275
@@ -296,18 +284,17 @@ describe('Test live', function () {
296 async function createLiveWrapper () { 284 async function createLiveWrapper () {
297 const liveAttributes = { 285 const liveAttributes = {
298 name: 'user live', 286 name: 'user live',
299 channelId: servers[0].videoChannel.id, 287 channelId: servers[0].store.channel.id,
300 privacy: VideoPrivacy.PUBLIC, 288 privacy: VideoPrivacy.PUBLIC,
301 saveReplay: false 289 saveReplay: false
302 } 290 }
303 291
304 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 292 const { uuid } = await commands[0].create({ fields: liveAttributes })
305 const uuid = res.body.video.uuid
306 293
307 const resLive = await getLive(servers[0].url, servers[0].accessToken, uuid) 294 const live = await commands[0].get({ videoId: uuid })
308 const resVideo = await getVideo(servers[0].url, uuid) 295 const video = await servers[0].videos.get({ id: uuid })
309 296
310 return Object.assign(resVideo.body, resLive.body) as LiveVideo & VideoDetails 297 return Object.assign(video, live)
311 } 298 }
312 299
313 it('Should not allow a stream without the appropriate path', async function () { 300 it('Should not allow a stream without the appropriate path', async function () {
@@ -335,13 +322,12 @@ describe('Test live', function () {
335 322
336 it('Should list this live now someone stream into it', async function () { 323 it('Should list this live now someone stream into it', async function () {
337 for (const server of servers) { 324 for (const server of servers) {
338 const res = await getVideosList(server.url) 325 const { total, data } = await server.videos.list()
339 326
340 expect(res.body.total).to.equal(1) 327 expect(total).to.equal(1)
341 expect(res.body.data).to.have.lengthOf(1) 328 expect(data).to.have.lengthOf(1)
342
343 const video: Video = res.body.data[0]
344 329
330 const video = data[0]
345 expect(video.name).to.equal('user live') 331 expect(video.name).to.equal('user live')
346 expect(video.isLive).to.be.true 332 expect(video.isLive).to.be.true
347 } 333 }
@@ -352,7 +338,7 @@ describe('Test live', function () {
352 338
353 liveVideo = await createLiveWrapper() 339 liveVideo = await createLiveWrapper()
354 340
355 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, liveVideo.uuid) 341 await servers[0].blacklist.add({ videoId: liveVideo.uuid })
356 342
357 const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey) 343 const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey)
358 await testFfmpegStreamError(command, true) 344 await testFfmpegStreamError(command, true)
@@ -363,7 +349,7 @@ describe('Test live', function () {
363 349
364 liveVideo = await createLiveWrapper() 350 liveVideo = await createLiveWrapper()
365 351
366 await removeVideo(servers[0].url, servers[0].accessToken, liveVideo.uuid) 352 await servers[0].videos.remove({ id: liveVideo.uuid })
367 353
368 const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey) 354 const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey)
369 await testFfmpegStreamError(command, true) 355 await testFfmpegStreamError(command, true)
@@ -376,24 +362,21 @@ describe('Test live', function () {
376 async function createLiveWrapper (saveReplay: boolean) { 362 async function createLiveWrapper (saveReplay: boolean) {
377 const liveAttributes = { 363 const liveAttributes = {
378 name: 'live video', 364 name: 'live video',
379 channelId: servers[0].videoChannel.id, 365 channelId: servers[0].store.channel.id,
380 privacy: VideoPrivacy.PUBLIC, 366 privacy: VideoPrivacy.PUBLIC,
381 saveReplay 367 saveReplay
382 } 368 }
383 369
384 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 370 const { uuid } = await commands[0].create({ fields: liveAttributes })
385 return res.body.video.uuid 371 return uuid
386 } 372 }
387 373
388 async function testVideoResolutions (liveVideoId: string, resolutions: number[]) { 374 async function testVideoResolutions (liveVideoId: string, resolutions: number[]) {
389 for (const server of servers) { 375 for (const server of servers) {
390 const resList = await getVideosList(server.url) 376 const { data } = await server.videos.list()
391 const videos: Video[] = resList.body.data 377 expect(data.find(v => v.uuid === liveVideoId)).to.exist
392
393 expect(videos.find(v => v.uuid === liveVideoId)).to.exist
394 378
395 const resVideo = await getVideo(server.url, liveVideoId) 379 const video = await server.videos.get({ id: liveVideoId })
396 const video: VideoDetails = resVideo.body
397 380
398 expect(video.streamingPlaylists).to.have.lengthOf(1) 381 expect(video.streamingPlaylists).to.have.lengthOf(1)
399 382
@@ -403,39 +386,48 @@ describe('Test live', function () {
403 // Only finite files are displayed 386 // Only finite files are displayed
404 expect(hlsPlaylist.files).to.have.lengthOf(0) 387 expect(hlsPlaylist.files).to.have.lengthOf(0)
405 388
406 await checkResolutionsInMasterPlaylist(hlsPlaylist.playlistUrl, resolutions) 389 await checkResolutionsInMasterPlaylist({ server, playlistUrl: hlsPlaylist.playlistUrl, resolutions })
407 390
408 for (let i = 0; i < resolutions.length; i++) { 391 for (let i = 0; i < resolutions.length; i++) {
409 const segmentNum = 3 392 const segmentNum = 3
410 const segmentName = `${i}-00000${segmentNum}.ts` 393 const segmentName = `${i}-00000${segmentNum}.ts`
411 await waitUntilLiveSegmentGeneration(servers[0], video.uuid, i, segmentNum) 394 await commands[0].waitUntilSegmentGeneration({ videoUUID: video.uuid, resolution: i, segment: segmentNum })
412 395
413 const res = await getPlaylist(`${servers[0].url}/static/streaming-playlists/hls/${video.uuid}/${i}.m3u8`) 396 const subPlaylist = await servers[0].streamingPlaylists.get({
414 const subPlaylist = res.text 397 url: `${servers[0].url}/static/streaming-playlists/hls/${video.uuid}/${i}.m3u8`
398 })
415 399
416 expect(subPlaylist).to.contain(segmentName) 400 expect(subPlaylist).to.contain(segmentName)
417 401
418 const baseUrlAndPath = servers[0].url + '/static/streaming-playlists/hls' 402 const baseUrlAndPath = servers[0].url + '/static/streaming-playlists/hls'
419 await checkLiveSegmentHash(baseUrlAndPath, video.uuid, segmentName, hlsPlaylist) 403 await checkLiveSegmentHash({
404 server,
405 baseUrlSegment: baseUrlAndPath,
406 videoUUID: video.uuid,
407 segmentName,
408 hlsPlaylist
409 })
420 } 410 }
421 } 411 }
422 } 412 }
423 413
424 function updateConf (resolutions: number[]) { 414 function updateConf (resolutions: number[]) {
425 return updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 415 return servers[0].config.updateCustomSubConfig({
426 live: { 416 newConfig: {
427 enabled: true, 417 live: {
428 allowReplay: true,
429 maxDuration: -1,
430 transcoding: {
431 enabled: true, 418 enabled: true,
432 resolutions: { 419 allowReplay: true,
433 '240p': resolutions.includes(240), 420 maxDuration: -1,
434 '360p': resolutions.includes(360), 421 transcoding: {
435 '480p': resolutions.includes(480), 422 enabled: true,
436 '720p': resolutions.includes(720), 423 resolutions: {
437 '1080p': resolutions.includes(1080), 424 '240p': resolutions.includes(240),
438 '2160p': resolutions.includes(2160) 425 '360p': resolutions.includes(360),
426 '480p': resolutions.includes(480),
427 '720p': resolutions.includes(720),
428 '1080p': resolutions.includes(1080),
429 '2160p': resolutions.includes(2160)
430 }
439 } 431 }
440 } 432 }
441 } 433 }
@@ -451,13 +443,13 @@ describe('Test live', function () {
451 443
452 liveVideoId = await createLiveWrapper(false) 444 liveVideoId = await createLiveWrapper(false)
453 445
454 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId) 446 const ffmpegCommand = await commands[0].sendRTMPStreamInVideo({ videoId: liveVideoId })
455 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 447 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
456 await waitJobs(servers) 448 await waitJobs(servers)
457 449
458 await testVideoResolutions(liveVideoId, [ 720 ]) 450 await testVideoResolutions(liveVideoId, [ 720 ])
459 451
460 await stopFfmpeg(command) 452 await stopFfmpeg(ffmpegCommand)
461 }) 453 })
462 454
463 it('Should enable transcoding with some resolutions', async function () { 455 it('Should enable transcoding with some resolutions', async function () {
@@ -467,13 +459,13 @@ describe('Test live', function () {
467 await updateConf(resolutions) 459 await updateConf(resolutions)
468 liveVideoId = await createLiveWrapper(false) 460 liveVideoId = await createLiveWrapper(false)
469 461
470 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId) 462 const ffmpegCommand = await commands[0].sendRTMPStreamInVideo({ videoId: liveVideoId })
471 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 463 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
472 await waitJobs(servers) 464 await waitJobs(servers)
473 465
474 await testVideoResolutions(liveVideoId, resolutions) 466 await testVideoResolutions(liveVideoId, resolutions)
475 467
476 await stopFfmpeg(command) 468 await stopFfmpeg(ffmpegCommand)
477 }) 469 })
478 470
479 it('Should enable transcoding with some resolutions and correctly save them', async function () { 471 it('Should enable transcoding with some resolutions and correctly save them', async function () {
@@ -484,14 +476,14 @@ describe('Test live', function () {
484 await updateConf(resolutions) 476 await updateConf(resolutions)
485 liveVideoId = await createLiveWrapper(true) 477 liveVideoId = await createLiveWrapper(true)
486 478
487 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId, 'video_short2.webm') 479 const ffmpegCommand = await commands[0].sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
488 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 480 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
489 await waitJobs(servers) 481 await waitJobs(servers)
490 482
491 await testVideoResolutions(liveVideoId, resolutions) 483 await testVideoResolutions(liveVideoId, resolutions)
492 484
493 await stopFfmpeg(command) 485 await stopFfmpeg(ffmpegCommand)
494 await waitUntilLiveEnded(servers[0].url, servers[0].accessToken, liveVideoId) 486 await commands[0].waitUntilEnded({ videoId: liveVideoId })
495 487
496 await waitJobs(servers) 488 await waitJobs(servers)
497 489
@@ -504,8 +496,7 @@ describe('Test live', function () {
504 } 496 }
505 497
506 for (const server of servers) { 498 for (const server of servers) {
507 const resVideo = await getVideo(server.url, liveVideoId) 499 const video = await server.videos.get({ id: liveVideoId })
508 const video: VideoDetails = resVideo.body
509 500
510 expect(video.state.id).to.equal(VideoState.PUBLISHED) 501 expect(video.state.id).to.equal(VideoState.PUBLISHED)
511 expect(video.duration).to.be.greaterThan(1) 502 expect(video.duration).to.be.greaterThan(1)
@@ -515,6 +506,10 @@ describe('Test live', function () {
515 await makeRawRequest(hlsPlaylist.playlistUrl, HttpStatusCode.OK_200) 506 await makeRawRequest(hlsPlaylist.playlistUrl, HttpStatusCode.OK_200)
516 await makeRawRequest(hlsPlaylist.segmentsSha256Url, HttpStatusCode.OK_200) 507 await makeRawRequest(hlsPlaylist.segmentsSha256Url, HttpStatusCode.OK_200)
517 508
509 // We should have generated random filenames
510 expect(basename(hlsPlaylist.playlistUrl)).to.not.equal('master.m3u8')
511 expect(basename(hlsPlaylist.segmentsSha256Url)).to.not.equal('segments-sha256.json')
512
518 expect(hlsPlaylist.files).to.have.lengthOf(resolutions.length) 513 expect(hlsPlaylist.files).to.have.lengthOf(resolutions.length)
519 514
520 for (const resolution of resolutions) { 515 for (const resolution of resolutions) {
@@ -529,8 +524,10 @@ describe('Test live', function () {
529 expect(file.fps).to.be.approximately(30, 2) 524 expect(file.fps).to.be.approximately(30, 2)
530 } 525 }
531 526
532 const filename = `${video.uuid}-${resolution}-fragmented.mp4` 527 const filename = basename(file.fileUrl)
533 const segmentPath = buildServerDirectory(servers[0], join('streaming-playlists', 'hls', video.uuid, filename)) 528 expect(filename).to.not.contain(video.uuid)
529
530 const segmentPath = servers[0].servers.buildDirectory(join('streaming-playlists', 'hls', video.uuid, filename))
534 531
535 const probe = await ffprobePromise(segmentPath) 532 const probe = await ffprobePromise(segmentPath)
536 const videoStream = await getVideoStreamFromFile(segmentPath, probe) 533 const videoStream = await getVideoStreamFromFile(segmentPath, probe)
@@ -546,7 +543,7 @@ describe('Test live', function () {
546 it('Should correctly have cleaned up the live files', async function () { 543 it('Should correctly have cleaned up the live files', async function () {
547 this.timeout(30000) 544 this.timeout(30000)
548 545
549 await checkLiveCleanup(servers[0], liveVideoId, [ 240, 360, 720 ]) 546 await checkLiveCleanupAfterSave(servers[0], liveVideoId, [ 240, 360, 720 ])
550 }) 547 })
551 }) 548 })
552 549
@@ -557,13 +554,13 @@ describe('Test live', function () {
557 async function createLiveWrapper (saveReplay: boolean) { 554 async function createLiveWrapper (saveReplay: boolean) {
558 const liveAttributes = { 555 const liveAttributes = {
559 name: 'live video', 556 name: 'live video',
560 channelId: servers[0].videoChannel.id, 557 channelId: servers[0].store.channel.id,
561 privacy: VideoPrivacy.PUBLIC, 558 privacy: VideoPrivacy.PUBLIC,
562 saveReplay 559 saveReplay
563 } 560 }
564 561
565 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 562 const { uuid } = await commands[0].create({ fields: liveAttributes })
566 return res.body.video.uuid 563 return uuid
567 } 564 }
568 565
569 before(async function () { 566 before(async function () {
@@ -573,20 +570,20 @@ describe('Test live', function () {
573 liveVideoReplayId = await createLiveWrapper(true) 570 liveVideoReplayId = await createLiveWrapper(true)
574 571
575 await Promise.all([ 572 await Promise.all([
576 sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId), 573 commands[0].sendRTMPStreamInVideo({ videoId: liveVideoId }),
577 sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoReplayId) 574 commands[0].sendRTMPStreamInVideo({ videoId: liveVideoReplayId })
578 ]) 575 ])
579 576
580 await Promise.all([ 577 await Promise.all([
581 waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoId), 578 commands[0].waitUntilPublished({ videoId: liveVideoId }),
582 waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoReplayId) 579 commands[0].waitUntilPublished({ videoId: liveVideoReplayId })
583 ]) 580 ])
584 581
585 await waitUntilLiveSegmentGeneration(servers[0], liveVideoId, 0, 2) 582 await commands[0].waitUntilSegmentGeneration({ videoUUID: liveVideoId, resolution: 0, segment: 2 })
586 await waitUntilLiveSegmentGeneration(servers[0], liveVideoReplayId, 0, 2) 583 await commands[0].waitUntilSegmentGeneration({ videoUUID: liveVideoReplayId, resolution: 0, segment: 2 })
587 584
588 await killallServers([ servers[0] ]) 585 await killallServers([ servers[0] ])
589 await reRunServer(servers[0]) 586 await servers[0].run()
590 587
591 await wait(5000) 588 await wait(5000)
592 }) 589 })
@@ -594,13 +591,13 @@ describe('Test live', function () {
594 it('Should cleanup lives', async function () { 591 it('Should cleanup lives', async function () {
595 this.timeout(60000) 592 this.timeout(60000)
596 593
597 await waitUntilLiveEnded(servers[0].url, servers[0].accessToken, liveVideoId) 594 await commands[0].waitUntilEnded({ videoId: liveVideoId })
598 }) 595 })
599 596
600 it('Should save a live replay', async function () { 597 it('Should save a live replay', async function () {
601 this.timeout(120000) 598 this.timeout(120000)
602 599
603 await waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoReplayId) 600 await commands[0].waitUntilPublished({ videoId: liveVideoReplayId })
604 }) 601 })
605 }) 602 })
606 603
diff --git a/server/tests/api/moderation/abuses.ts b/server/tests/api/moderation/abuses.ts
index fb765e7e3..c258414ce 100644
--- a/server/tests/api/moderation/abuses.ts
+++ b/server/tests/api/moderation/abuses.ts
@@ -3,70 +3,37 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 AbuseFilter, 6 AbusesCommand,
7 AbuseMessage,
8 AbusePredefinedReasonsString,
9 AbuseState,
10 Account,
11 AdminAbuse,
12 UserAbuse,
13 VideoComment
14} from '@shared/models'
15import {
16 addAbuseMessage,
17 addVideoCommentThread,
18 cleanupTests, 7 cleanupTests,
19 createUser, 8 createMultipleServers,
20 deleteAbuse, 9 doubleFollow,
21 deleteAbuseMessage, 10 PeerTubeServer,
22 deleteVideoComment,
23 flushAndRunMultipleServers,
24 generateUserAccessToken,
25 getAccount,
26 getAdminAbusesList,
27 getUserAbusesList,
28 getVideoCommentThreads,
29 getVideoIdFromUUID,
30 getVideosList,
31 immutableAssign,
32 listAbuseMessages,
33 removeUser,
34 removeVideo,
35 reportAbuse,
36 ServerInfo,
37 setAccessTokensToServers, 11 setAccessTokensToServers,
38 updateAbuse, 12 waitJobs
39 uploadVideo, 13} from '@shared/extra-utils'
40 uploadVideoAndGetId, 14import { AbuseMessage, AbusePredefinedReasonsString, AbuseState, AdminAbuse, UserAbuse } from '@shared/models'
41 userLogin
42} from '../../../../shared/extra-utils/index'
43import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
44import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
45import {
46 addAccountToServerBlocklist,
47 addServerToServerBlocklist,
48 removeAccountFromServerBlocklist,
49 removeServerFromServerBlocklist
50} from '../../../../shared/extra-utils/users/blocklist'
51 15
52const expect = chai.expect 16const expect = chai.expect
53 17
54describe('Test abuses', function () { 18describe('Test abuses', function () {
55 let servers: ServerInfo[] = [] 19 let servers: PeerTubeServer[] = []
56 let abuseServer1: AdminAbuse 20 let abuseServer1: AdminAbuse
57 let abuseServer2: AdminAbuse 21 let abuseServer2: AdminAbuse
22 let commands: AbusesCommand[]
58 23
59 before(async function () { 24 before(async function () {
60 this.timeout(50000) 25 this.timeout(50000)
61 26
62 // Run servers 27 // Run servers
63 servers = await flushAndRunMultipleServers(2) 28 servers = await createMultipleServers(2)
64 29
65 // Get the access tokens 30 // Get the access tokens
66 await setAccessTokensToServers(servers) 31 await setAccessTokensToServers(servers)
67 32
68 // Server 1 and server 2 follow each other 33 // Server 1 and server 2 follow each other
69 await doubleFollow(servers[0], servers[1]) 34 await doubleFollow(servers[0], servers[1])
35
36 commands = servers.map(s => s.abuses)
70 }) 37 })
71 38
72 describe('Video abuses', function () { 39 describe('Video abuses', function () {
@@ -75,179 +42,189 @@ describe('Test abuses', function () {
75 this.timeout(50000) 42 this.timeout(50000)
76 43
77 // Upload some videos on each servers 44 // Upload some videos on each servers
78 const video1Attributes = { 45 {
79 name: 'my super name for server 1', 46 const attributes = {
80 description: 'my super description for server 1' 47 name: 'my super name for server 1',
48 description: 'my super description for server 1'
49 }
50 await servers[0].videos.upload({ attributes })
81 } 51 }
82 await uploadVideo(servers[0].url, servers[0].accessToken, video1Attributes)
83 52
84 const video2Attributes = { 53 {
85 name: 'my super name for server 2', 54 const attributes = {
86 description: 'my super description for server 2' 55 name: 'my super name for server 2',
56 description: 'my super description for server 2'
57 }
58 await servers[1].videos.upload({ attributes })
87 } 59 }
88 await uploadVideo(servers[1].url, servers[1].accessToken, video2Attributes)
89 60
90 // Wait videos propagation, server 2 has transcoding enabled 61 // Wait videos propagation, server 2 has transcoding enabled
91 await waitJobs(servers) 62 await waitJobs(servers)
92 63
93 const res = await getVideosList(servers[0].url) 64 const { data } = await servers[0].videos.list()
94 const videos = res.body.data 65 expect(data.length).to.equal(2)
95 66
96 expect(videos.length).to.equal(2) 67 servers[0].store.videoCreated = data.find(video => video.name === 'my super name for server 1')
97 68 servers[1].store.videoCreated = data.find(video => video.name === 'my super name for server 2')
98 servers[0].video = videos.find(video => video.name === 'my super name for server 1')
99 servers[1].video = videos.find(video => video.name === 'my super name for server 2')
100 }) 69 })
101 70
102 it('Should not have abuses', async function () { 71 it('Should not have abuses', async function () {
103 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 72 const body = await commands[0].getAdminList()
104 73
105 expect(res.body.total).to.equal(0) 74 expect(body.total).to.equal(0)
106 expect(res.body.data).to.be.an('array') 75 expect(body.data).to.be.an('array')
107 expect(res.body.data.length).to.equal(0) 76 expect(body.data.length).to.equal(0)
108 }) 77 })
109 78
110 it('Should report abuse on a local video', async function () { 79 it('Should report abuse on a local video', async function () {
111 this.timeout(15000) 80 this.timeout(15000)
112 81
113 const reason = 'my super bad reason' 82 const reason = 'my super bad reason'
114 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, videoId: servers[0].video.id, reason }) 83 await commands[0].report({ videoId: servers[0].store.videoCreated.id, reason })
115 84
116 // We wait requests propagation, even if the server 1 is not supposed to make a request to server 2 85 // We wait requests propagation, even if the server 1 is not supposed to make a request to server 2
117 await waitJobs(servers) 86 await waitJobs(servers)
118 }) 87 })
119 88
120 it('Should have 1 video abuses on server 1 and 0 on server 2', async function () { 89 it('Should have 1 video abuses on server 1 and 0 on server 2', async function () {
121 const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 90 {
91 const body = await commands[0].getAdminList()
122 92
123 expect(res1.body.total).to.equal(1) 93 expect(body.total).to.equal(1)
124 expect(res1.body.data).to.be.an('array') 94 expect(body.data).to.be.an('array')
125 expect(res1.body.data.length).to.equal(1) 95 expect(body.data.length).to.equal(1)
126 96
127 const abuse: AdminAbuse = res1.body.data[0] 97 const abuse = body.data[0]
128 expect(abuse.reason).to.equal('my super bad reason') 98 expect(abuse.reason).to.equal('my super bad reason')
129 99
130 expect(abuse.reporterAccount.name).to.equal('root') 100 expect(abuse.reporterAccount.name).to.equal('root')
131 expect(abuse.reporterAccount.host).to.equal(servers[0].host) 101 expect(abuse.reporterAccount.host).to.equal(servers[0].host)
132 102
133 expect(abuse.video.id).to.equal(servers[0].video.id) 103 expect(abuse.video.id).to.equal(servers[0].store.videoCreated.id)
134 expect(abuse.video.channel).to.exist 104 expect(abuse.video.channel).to.exist
135 105
136 expect(abuse.comment).to.be.null 106 expect(abuse.comment).to.be.null
137 107
138 expect(abuse.flaggedAccount.name).to.equal('root') 108 expect(abuse.flaggedAccount.name).to.equal('root')
139 expect(abuse.flaggedAccount.host).to.equal(servers[0].host) 109 expect(abuse.flaggedAccount.host).to.equal(servers[0].host)
140 110
141 expect(abuse.video.countReports).to.equal(1) 111 expect(abuse.video.countReports).to.equal(1)
142 expect(abuse.video.nthReport).to.equal(1) 112 expect(abuse.video.nthReport).to.equal(1)
143 113
144 expect(abuse.countReportsForReporter).to.equal(1) 114 expect(abuse.countReportsForReporter).to.equal(1)
145 expect(abuse.countReportsForReportee).to.equal(1) 115 expect(abuse.countReportsForReportee).to.equal(1)
116 }
146 117
147 const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken }) 118 {
148 expect(res2.body.total).to.equal(0) 119 const body = await commands[1].getAdminList()
149 expect(res2.body.data).to.be.an('array') 120 expect(body.total).to.equal(0)
150 expect(res2.body.data.length).to.equal(0) 121 expect(body.data).to.be.an('array')
122 expect(body.data.length).to.equal(0)
123 }
151 }) 124 })
152 125
153 it('Should report abuse on a remote video', async function () { 126 it('Should report abuse on a remote video', async function () {
154 this.timeout(10000) 127 this.timeout(10000)
155 128
156 const reason = 'my super bad reason 2' 129 const reason = 'my super bad reason 2'
157 const videoId = await getVideoIdFromUUID(servers[0].url, servers[1].video.uuid) 130 const videoId = await servers[0].videos.getId({ uuid: servers[1].store.videoCreated.uuid })
158 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, videoId, reason }) 131 await commands[0].report({ videoId, reason })
159 132
160 // We wait requests propagation 133 // We wait requests propagation
161 await waitJobs(servers) 134 await waitJobs(servers)
162 }) 135 })
163 136
164 it('Should have 2 video abuses on server 1 and 1 on server 2', async function () { 137 it('Should have 2 video abuses on server 1 and 1 on server 2', async function () {
165 const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 138 {
139 const body = await commands[0].getAdminList()
166 140
167 expect(res1.body.total).to.equal(2) 141 expect(body.total).to.equal(2)
168 expect(res1.body.data.length).to.equal(2) 142 expect(body.data.length).to.equal(2)
169 143
170 const abuse1: AdminAbuse = res1.body.data[0] 144 const abuse1 = body.data[0]
171 expect(abuse1.reason).to.equal('my super bad reason') 145 expect(abuse1.reason).to.equal('my super bad reason')
172 expect(abuse1.reporterAccount.name).to.equal('root') 146 expect(abuse1.reporterAccount.name).to.equal('root')
173 expect(abuse1.reporterAccount.host).to.equal(servers[0].host) 147 expect(abuse1.reporterAccount.host).to.equal(servers[0].host)
174 148
175 expect(abuse1.video.id).to.equal(servers[0].video.id) 149 expect(abuse1.video.id).to.equal(servers[0].store.videoCreated.id)
176 expect(abuse1.video.countReports).to.equal(1) 150 expect(abuse1.video.countReports).to.equal(1)
177 expect(abuse1.video.nthReport).to.equal(1) 151 expect(abuse1.video.nthReport).to.equal(1)
178 152
179 expect(abuse1.comment).to.be.null 153 expect(abuse1.comment).to.be.null
180 154
181 expect(abuse1.flaggedAccount.name).to.equal('root') 155 expect(abuse1.flaggedAccount.name).to.equal('root')
182 expect(abuse1.flaggedAccount.host).to.equal(servers[0].host) 156 expect(abuse1.flaggedAccount.host).to.equal(servers[0].host)
183 157
184 expect(abuse1.state.id).to.equal(AbuseState.PENDING) 158 expect(abuse1.state.id).to.equal(AbuseState.PENDING)
185 expect(abuse1.state.label).to.equal('Pending') 159 expect(abuse1.state.label).to.equal('Pending')
186 expect(abuse1.moderationComment).to.be.null 160 expect(abuse1.moderationComment).to.be.null
187 161
188 const abuse2: AdminAbuse = res1.body.data[1] 162 const abuse2 = body.data[1]
189 expect(abuse2.reason).to.equal('my super bad reason 2') 163 expect(abuse2.reason).to.equal('my super bad reason 2')
190 164
191 expect(abuse2.reporterAccount.name).to.equal('root') 165 expect(abuse2.reporterAccount.name).to.equal('root')
192 expect(abuse2.reporterAccount.host).to.equal(servers[0].host) 166 expect(abuse2.reporterAccount.host).to.equal(servers[0].host)
193 167
194 expect(abuse2.video.id).to.equal(servers[1].video.id) 168 expect(abuse2.video.id).to.equal(servers[1].store.videoCreated.id)
195 169
196 expect(abuse2.comment).to.be.null 170 expect(abuse2.comment).to.be.null
197 171
198 expect(abuse2.flaggedAccount.name).to.equal('root') 172 expect(abuse2.flaggedAccount.name).to.equal('root')
199 expect(abuse2.flaggedAccount.host).to.equal(servers[1].host) 173 expect(abuse2.flaggedAccount.host).to.equal(servers[1].host)
200 174
201 expect(abuse2.state.id).to.equal(AbuseState.PENDING) 175 expect(abuse2.state.id).to.equal(AbuseState.PENDING)
202 expect(abuse2.state.label).to.equal('Pending') 176 expect(abuse2.state.label).to.equal('Pending')
203 expect(abuse2.moderationComment).to.be.null 177 expect(abuse2.moderationComment).to.be.null
178 }
204 179
205 const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken }) 180 {
206 expect(res2.body.total).to.equal(1) 181 const body = await commands[1].getAdminList()
207 expect(res2.body.data.length).to.equal(1) 182 expect(body.total).to.equal(1)
183 expect(body.data.length).to.equal(1)
208 184
209 abuseServer2 = res2.body.data[0] 185 abuseServer2 = body.data[0]
210 expect(abuseServer2.reason).to.equal('my super bad reason 2') 186 expect(abuseServer2.reason).to.equal('my super bad reason 2')
211 expect(abuseServer2.reporterAccount.name).to.equal('root') 187 expect(abuseServer2.reporterAccount.name).to.equal('root')
212 expect(abuseServer2.reporterAccount.host).to.equal(servers[0].host) 188 expect(abuseServer2.reporterAccount.host).to.equal(servers[0].host)
213 189
214 expect(abuse2.flaggedAccount.name).to.equal('root') 190 expect(abuseServer2.flaggedAccount.name).to.equal('root')
215 expect(abuse2.flaggedAccount.host).to.equal(servers[1].host) 191 expect(abuseServer2.flaggedAccount.host).to.equal(servers[1].host)
216 192
217 expect(abuseServer2.state.id).to.equal(AbuseState.PENDING) 193 expect(abuseServer2.state.id).to.equal(AbuseState.PENDING)
218 expect(abuseServer2.state.label).to.equal('Pending') 194 expect(abuseServer2.state.label).to.equal('Pending')
219 expect(abuseServer2.moderationComment).to.be.null 195 expect(abuseServer2.moderationComment).to.be.null
196 }
220 }) 197 })
221 198
222 it('Should hide video abuses from blocked accounts', async function () { 199 it('Should hide video abuses from blocked accounts', async function () {
223 this.timeout(10000) 200 this.timeout(10000)
224 201
225 { 202 {
226 const videoId = await getVideoIdFromUUID(servers[1].url, servers[0].video.uuid) 203 const videoId = await servers[1].videos.getId({ uuid: servers[0].store.videoCreated.uuid })
227 await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, videoId, reason: 'will mute this' }) 204 await commands[1].report({ videoId, reason: 'will mute this' })
228 await waitJobs(servers) 205 await waitJobs(servers)
229 206
230 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 207 const body = await commands[0].getAdminList()
231 expect(res.body.total).to.equal(3) 208 expect(body.total).to.equal(3)
232 } 209 }
233 210
234 const accountToBlock = 'root@' + servers[1].host 211 const accountToBlock = 'root@' + servers[1].host
235 212
236 { 213 {
237 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, accountToBlock) 214 await servers[0].blocklist.addToServerBlocklist({ account: accountToBlock })
238 215
239 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 216 const body = await commands[0].getAdminList()
240 expect(res.body.total).to.equal(2) 217 expect(body.total).to.equal(2)
241 218
242 const abuse = res.body.data.find(a => a.reason === 'will mute this') 219 const abuse = body.data.find(a => a.reason === 'will mute this')
243 expect(abuse).to.be.undefined 220 expect(abuse).to.be.undefined
244 } 221 }
245 222
246 { 223 {
247 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, accountToBlock) 224 await servers[0].blocklist.removeFromServerBlocklist({ account: accountToBlock })
248 225
249 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 226 const body = await commands[0].getAdminList()
250 expect(res.body.total).to.equal(3) 227 expect(body.total).to.equal(3)
251 } 228 }
252 }) 229 })
253 230
@@ -255,35 +232,35 @@ describe('Test abuses', function () {
255 const serverToBlock = servers[1].host 232 const serverToBlock = servers[1].host
256 233
257 { 234 {
258 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, servers[1].host) 235 await servers[0].blocklist.addToServerBlocklist({ server: serverToBlock })
259 236
260 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 237 const body = await commands[0].getAdminList()
261 expect(res.body.total).to.equal(2) 238 expect(body.total).to.equal(2)
262 239
263 const abuse = res.body.data.find(a => a.reason === 'will mute this') 240 const abuse = body.data.find(a => a.reason === 'will mute this')
264 expect(abuse).to.be.undefined 241 expect(abuse).to.be.undefined
265 } 242 }
266 243
267 { 244 {
268 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, serverToBlock) 245 await servers[0].blocklist.removeFromServerBlocklist({ server: serverToBlock })
269 246
270 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 247 const body = await commands[0].getAdminList()
271 expect(res.body.total).to.equal(3) 248 expect(body.total).to.equal(3)
272 } 249 }
273 }) 250 })
274 251
275 it('Should keep the video abuse when deleting the video', async function () { 252 it('Should keep the video abuse when deleting the video', async function () {
276 this.timeout(10000) 253 this.timeout(10000)
277 254
278 await removeVideo(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid) 255 await servers[1].videos.remove({ id: abuseServer2.video.uuid })
279 256
280 await waitJobs(servers) 257 await waitJobs(servers)
281 258
282 const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken }) 259 const body = await commands[1].getAdminList()
283 expect(res.body.total).to.equal(2, "wrong number of videos returned") 260 expect(body.total).to.equal(2, "wrong number of videos returned")
284 expect(res.body.data).to.have.lengthOf(2, "wrong number of videos returned") 261 expect(body.data).to.have.lengthOf(2, "wrong number of videos returned")
285 262
286 const abuse: AdminAbuse = res.body.data[0] 263 const abuse = body.data[0]
287 expect(abuse.id).to.equal(abuseServer2.id, "wrong origin server id for first video") 264 expect(abuse.id).to.equal(abuseServer2.id, "wrong origin server id for first video")
288 expect(abuse.video.id).to.equal(abuseServer2.video.id, "wrong video id") 265 expect(abuse.video.id).to.equal(abuseServer2.video.id, "wrong video id")
289 expect(abuse.video.channel).to.exist 266 expect(abuse.video.channel).to.exist
@@ -295,39 +272,36 @@ describe('Test abuses', function () {
295 272
296 // register a second user to have two reporters/reportees 273 // register a second user to have two reporters/reportees
297 const user = { username: 'user2', password: 'password' } 274 const user = { username: 'user2', password: 'password' }
298 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, ...user }) 275 await servers[0].users.create({ ...user })
299 const userAccessToken = await userLogin(servers[0], user) 276 const userAccessToken = await servers[0].login.getAccessToken(user)
300 277
301 // upload a third video via this user 278 // upload a third video via this user
302 const video3Attributes = { 279 const attributes = {
303 name: 'my second super name for server 1', 280 name: 'my second super name for server 1',
304 description: 'my second super description for server 1' 281 description: 'my second super description for server 1'
305 } 282 }
306 await uploadVideo(servers[0].url, userAccessToken, video3Attributes) 283 const { id } = await servers[0].videos.upload({ token: userAccessToken, attributes })
307 284 const video3Id = id
308 const res1 = await getVideosList(servers[0].url)
309 const videos = res1.body.data
310 const video3 = videos.find(video => video.name === 'my second super name for server 1')
311 285
312 // resume with the test 286 // resume with the test
313 const reason3 = 'my super bad reason 3' 287 const reason3 = 'my super bad reason 3'
314 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, videoId: video3.id, reason: reason3 }) 288 await commands[0].report({ videoId: video3Id, reason: reason3 })
315 289
316 const reason4 = 'my super bad reason 4' 290 const reason4 = 'my super bad reason 4'
317 await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: servers[0].video.id, reason: reason4 }) 291 await commands[0].report({ token: userAccessToken, videoId: servers[0].store.videoCreated.id, reason: reason4 })
318 292
319 { 293 {
320 const res2 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 294 const body = await commands[0].getAdminList()
321 const abuses = res2.body.data as AdminAbuse[] 295 const abuses = body.data
322 296
323 const abuseVideo3 = res2.body.data.find(a => a.video.id === video3.id) 297 const abuseVideo3 = body.data.find(a => a.video.id === video3Id)
324 expect(abuseVideo3).to.not.be.undefined 298 expect(abuseVideo3).to.not.be.undefined
325 expect(abuseVideo3.video.countReports).to.equal(1, "wrong reports count for video 3") 299 expect(abuseVideo3.video.countReports).to.equal(1, "wrong reports count for video 3")
326 expect(abuseVideo3.video.nthReport).to.equal(1, "wrong report position in report list for video 3") 300 expect(abuseVideo3.video.nthReport).to.equal(1, "wrong report position in report list for video 3")
327 expect(abuseVideo3.countReportsForReportee).to.equal(1, "wrong reports count for reporter on video 3 abuse") 301 expect(abuseVideo3.countReportsForReportee).to.equal(1, "wrong reports count for reporter on video 3 abuse")
328 expect(abuseVideo3.countReportsForReporter).to.equal(3, "wrong reports count for reportee on video 3 abuse") 302 expect(abuseVideo3.countReportsForReporter).to.equal(3, "wrong reports count for reportee on video 3 abuse")
329 303
330 const abuseServer1 = abuses.find(a => a.video.id === servers[0].video.id) 304 const abuseServer1 = abuses.find(a => a.video.id === servers[0].store.videoCreated.id)
331 expect(abuseServer1.countReportsForReportee).to.equal(3, "wrong reports count for reporter on video 1 abuse") 305 expect(abuseServer1.countReportsForReportee).to.equal(3, "wrong reports count for reporter on video 1 abuse")
332 } 306 }
333 }) 307 })
@@ -337,20 +311,18 @@ describe('Test abuses', function () {
337 311
338 const reason5 = 'my super bad reason 5' 312 const reason5 = 'my super bad reason 5'
339 const predefinedReasons5: AbusePredefinedReasonsString[] = [ 'violentOrRepulsive', 'captions' ] 313 const predefinedReasons5: AbusePredefinedReasonsString[] = [ 'violentOrRepulsive', 'captions' ]
340 const createdAbuse = (await reportAbuse({ 314 const createRes = await commands[0].report({
341 url: servers[0].url, 315 videoId: servers[0].store.videoCreated.id,
342 token: servers[0].accessToken,
343 videoId: servers[0].video.id,
344 reason: reason5, 316 reason: reason5,
345 predefinedReasons: predefinedReasons5, 317 predefinedReasons: predefinedReasons5,
346 startAt: 1, 318 startAt: 1,
347 endAt: 5 319 endAt: 5
348 })).body.abuse 320 })
349 321
350 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 322 const body = await commands[0].getAdminList()
351 323
352 { 324 {
353 const abuse = (res.body.data as AdminAbuse[]).find(a => a.id === createdAbuse.id) 325 const abuse = body.data.find(a => a.id === createRes.abuse.id)
354 expect(abuse.reason).to.equals(reason5) 326 expect(abuse.reason).to.equals(reason5)
355 expect(abuse.predefinedReasons).to.deep.equals(predefinedReasons5, "predefined reasons do not match the one reported") 327 expect(abuse.predefinedReasons).to.deep.equals(predefinedReasons5, "predefined reasons do not match the one reported")
356 expect(abuse.video.startAt).to.equal(1, "starting timestamp doesn't match the one reported") 328 expect(abuse.video.startAt).to.equal(1, "starting timestamp doesn't match the one reported")
@@ -361,37 +333,30 @@ describe('Test abuses', function () {
361 it('Should delete the video abuse', async function () { 333 it('Should delete the video abuse', async function () {
362 this.timeout(10000) 334 this.timeout(10000)
363 335
364 await deleteAbuse(servers[1].url, servers[1].accessToken, abuseServer2.id) 336 await commands[1].delete({ abuseId: abuseServer2.id })
365 337
366 await waitJobs(servers) 338 await waitJobs(servers)
367 339
368 { 340 {
369 const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken }) 341 const body = await commands[1].getAdminList()
370 expect(res.body.total).to.equal(1) 342 expect(body.total).to.equal(1)
371 expect(res.body.data.length).to.equal(1) 343 expect(body.data.length).to.equal(1)
372 expect(res.body.data[0].id).to.not.equal(abuseServer2.id) 344 expect(body.data[0].id).to.not.equal(abuseServer2.id)
373 } 345 }
374 346
375 { 347 {
376 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 348 const body = await commands[0].getAdminList()
377 expect(res.body.total).to.equal(6) 349 expect(body.total).to.equal(6)
378 } 350 }
379 }) 351 })
380 352
381 it('Should list and filter video abuses', async function () { 353 it('Should list and filter video abuses', async function () {
382 this.timeout(10000) 354 this.timeout(10000)
383 355
384 async function list (query: Omit<Parameters<typeof getAdminAbusesList>[0], 'url' | 'token'>) { 356 async function list (query: Parameters<AbusesCommand['getAdminList']>[0]) {
385 const options = { 357 const body = await commands[0].getAdminList(query)
386 url: servers[0].url,
387 token: servers[0].accessToken
388 }
389
390 Object.assign(options, query)
391 358
392 const res = await getAdminAbusesList(options) 359 return body.data
393
394 return res.body.data as AdminAbuse[]
395 } 360 }
396 361
397 expect(await list({ id: 56 })).to.have.lengthOf(0) 362 expect(await list({ id: 56 })).to.have.lengthOf(0)
@@ -424,24 +389,24 @@ describe('Test abuses', function () {
424 389
425 describe('Comment abuses', function () { 390 describe('Comment abuses', function () {
426 391
427 async function getComment (url: string, videoIdArg: number | string) { 392 async function getComment (server: PeerTubeServer, videoIdArg: number | string) {
428 const videoId = typeof videoIdArg === 'string' 393 const videoId = typeof videoIdArg === 'string'
429 ? await getVideoIdFromUUID(url, videoIdArg) 394 ? await server.videos.getId({ uuid: videoIdArg })
430 : videoIdArg 395 : videoIdArg
431 396
432 const res = await getVideoCommentThreads(url, videoId, 0, 5) 397 const { data } = await server.comments.listThreads({ videoId })
433 398
434 return res.body.data[0] as VideoComment 399 return data[0]
435 } 400 }
436 401
437 before(async function () { 402 before(async function () {
438 this.timeout(50000) 403 this.timeout(50000)
439 404
440 servers[0].video = await uploadVideoAndGetId({ server: servers[0], videoName: 'server 1' }) 405 servers[0].store.videoCreated = await servers[0].videos.quickUpload({ name: 'server 1' })
441 servers[1].video = await uploadVideoAndGetId({ server: servers[1], videoName: 'server 2' }) 406 servers[1].store.videoCreated = await servers[1].videos.quickUpload({ name: 'server 2' })
442 407
443 await addVideoCommentThread(servers[0].url, servers[0].accessToken, servers[0].video.id, 'comment server 1') 408 await servers[0].comments.createThread({ videoId: servers[0].store.videoCreated.id, text: 'comment server 1' })
444 await addVideoCommentThread(servers[1].url, servers[1].accessToken, servers[1].video.id, 'comment server 2') 409 await servers[1].comments.createThread({ videoId: servers[1].store.videoCreated.id, text: 'comment server 2' })
445 410
446 await waitJobs(servers) 411 await waitJobs(servers)
447 }) 412 })
@@ -449,23 +414,23 @@ describe('Test abuses', function () {
449 it('Should report abuse on a comment', async function () { 414 it('Should report abuse on a comment', async function () {
450 this.timeout(15000) 415 this.timeout(15000)
451 416
452 const comment = await getComment(servers[0].url, servers[0].video.id) 417 const comment = await getComment(servers[0], servers[0].store.videoCreated.id)
453 418
454 const reason = 'it is a bad comment' 419 const reason = 'it is a bad comment'
455 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, commentId: comment.id, reason }) 420 await commands[0].report({ commentId: comment.id, reason })
456 421
457 await waitJobs(servers) 422 await waitJobs(servers)
458 }) 423 })
459 424
460 it('Should have 1 comment abuse on server 1 and 0 on server 2', async function () { 425 it('Should have 1 comment abuse on server 1 and 0 on server 2', async function () {
461 { 426 {
462 const comment = await getComment(servers[0].url, servers[0].video.id) 427 const comment = await getComment(servers[0], servers[0].store.videoCreated.id)
463 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) 428 const body = await commands[0].getAdminList({ filter: 'comment' })
464 429
465 expect(res.body.total).to.equal(1) 430 expect(body.total).to.equal(1)
466 expect(res.body.data).to.have.lengthOf(1) 431 expect(body.data).to.have.lengthOf(1)
467 432
468 const abuse: AdminAbuse = res.body.data[0] 433 const abuse = body.data[0]
469 expect(abuse.reason).to.equal('it is a bad comment') 434 expect(abuse.reason).to.equal('it is a bad comment')
470 435
471 expect(abuse.reporterAccount.name).to.equal('root') 436 expect(abuse.reporterAccount.name).to.equal('root')
@@ -477,98 +442,102 @@ describe('Test abuses', function () {
477 expect(abuse.comment.id).to.equal(comment.id) 442 expect(abuse.comment.id).to.equal(comment.id)
478 expect(abuse.comment.text).to.equal(comment.text) 443 expect(abuse.comment.text).to.equal(comment.text)
479 expect(abuse.comment.video.name).to.equal('server 1') 444 expect(abuse.comment.video.name).to.equal('server 1')
480 expect(abuse.comment.video.id).to.equal(servers[0].video.id) 445 expect(abuse.comment.video.id).to.equal(servers[0].store.videoCreated.id)
481 expect(abuse.comment.video.uuid).to.equal(servers[0].video.uuid) 446 expect(abuse.comment.video.uuid).to.equal(servers[0].store.videoCreated.uuid)
482 447
483 expect(abuse.countReportsForReporter).to.equal(5) 448 expect(abuse.countReportsForReporter).to.equal(5)
484 expect(abuse.countReportsForReportee).to.equal(5) 449 expect(abuse.countReportsForReportee).to.equal(5)
485 } 450 }
486 451
487 { 452 {
488 const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) 453 const body = await commands[1].getAdminList({ filter: 'comment' })
489 expect(res.body.total).to.equal(0) 454 expect(body.total).to.equal(0)
490 expect(res.body.data.length).to.equal(0) 455 expect(body.data.length).to.equal(0)
491 } 456 }
492 }) 457 })
493 458
494 it('Should report abuse on a remote comment', async function () { 459 it('Should report abuse on a remote comment', async function () {
495 this.timeout(10000) 460 this.timeout(10000)
496 461
497 const comment = await getComment(servers[0].url, servers[1].video.uuid) 462 const comment = await getComment(servers[0], servers[1].store.videoCreated.uuid)
498 463
499 const reason = 'it is a really bad comment' 464 const reason = 'it is a really bad comment'
500 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, commentId: comment.id, reason }) 465 await commands[0].report({ commentId: comment.id, reason })
501 466
502 await waitJobs(servers) 467 await waitJobs(servers)
503 }) 468 })
504 469
505 it('Should have 2 comment abuses on server 1 and 1 on server 2', async function () { 470 it('Should have 2 comment abuses on server 1 and 1 on server 2', async function () {
506 const commentServer2 = await getComment(servers[0].url, servers[1].video.id) 471 const commentServer2 = await getComment(servers[0], servers[1].store.videoCreated.id)
507 472
508 const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) 473 {
509 expect(res1.body.total).to.equal(2) 474 const body = await commands[0].getAdminList({ filter: 'comment' })
510 expect(res1.body.data.length).to.equal(2) 475 expect(body.total).to.equal(2)
476 expect(body.data.length).to.equal(2)
511 477
512 const abuse: AdminAbuse = res1.body.data[0] 478 const abuse = body.data[0]
513 expect(abuse.reason).to.equal('it is a bad comment') 479 expect(abuse.reason).to.equal('it is a bad comment')
514 expect(abuse.countReportsForReporter).to.equal(6) 480 expect(abuse.countReportsForReporter).to.equal(6)
515 expect(abuse.countReportsForReportee).to.equal(5) 481 expect(abuse.countReportsForReportee).to.equal(5)
516 482
517 const abuse2: AdminAbuse = res1.body.data[1] 483 const abuse2 = body.data[1]
518 484
519 expect(abuse2.reason).to.equal('it is a really bad comment') 485 expect(abuse2.reason).to.equal('it is a really bad comment')
520 486
521 expect(abuse2.reporterAccount.name).to.equal('root') 487 expect(abuse2.reporterAccount.name).to.equal('root')
522 expect(abuse2.reporterAccount.host).to.equal(servers[0].host) 488 expect(abuse2.reporterAccount.host).to.equal(servers[0].host)
523 489
524 expect(abuse2.video).to.be.null 490 expect(abuse2.video).to.be.null
525 491
526 expect(abuse2.comment.deleted).to.be.false 492 expect(abuse2.comment.deleted).to.be.false
527 expect(abuse2.comment.id).to.equal(commentServer2.id) 493 expect(abuse2.comment.id).to.equal(commentServer2.id)
528 expect(abuse2.comment.text).to.equal(commentServer2.text) 494 expect(abuse2.comment.text).to.equal(commentServer2.text)
529 expect(abuse2.comment.video.name).to.equal('server 2') 495 expect(abuse2.comment.video.name).to.equal('server 2')
530 expect(abuse2.comment.video.uuid).to.equal(servers[1].video.uuid) 496 expect(abuse2.comment.video.uuid).to.equal(servers[1].store.videoCreated.uuid)
531 497
532 expect(abuse2.state.id).to.equal(AbuseState.PENDING) 498 expect(abuse2.state.id).to.equal(AbuseState.PENDING)
533 expect(abuse2.state.label).to.equal('Pending') 499 expect(abuse2.state.label).to.equal('Pending')
534 500
535 expect(abuse2.moderationComment).to.be.null 501 expect(abuse2.moderationComment).to.be.null
536 502
537 expect(abuse2.countReportsForReporter).to.equal(6) 503 expect(abuse2.countReportsForReporter).to.equal(6)
538 expect(abuse2.countReportsForReportee).to.equal(2) 504 expect(abuse2.countReportsForReportee).to.equal(2)
505 }
539 506
540 const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) 507 {
541 expect(res2.body.total).to.equal(1) 508 const body = await commands[1].getAdminList({ filter: 'comment' })
542 expect(res2.body.data.length).to.equal(1) 509 expect(body.total).to.equal(1)
510 expect(body.data.length).to.equal(1)
543 511
544 abuseServer2 = res2.body.data[0] 512 abuseServer2 = body.data[0]
545 expect(abuseServer2.reason).to.equal('it is a really bad comment') 513 expect(abuseServer2.reason).to.equal('it is a really bad comment')
546 expect(abuseServer2.reporterAccount.name).to.equal('root') 514 expect(abuseServer2.reporterAccount.name).to.equal('root')
547 expect(abuseServer2.reporterAccount.host).to.equal(servers[0].host) 515 expect(abuseServer2.reporterAccount.host).to.equal(servers[0].host)
548 516
549 expect(abuseServer2.state.id).to.equal(AbuseState.PENDING) 517 expect(abuseServer2.state.id).to.equal(AbuseState.PENDING)
550 expect(abuseServer2.state.label).to.equal('Pending') 518 expect(abuseServer2.state.label).to.equal('Pending')
551 519
552 expect(abuseServer2.moderationComment).to.be.null 520 expect(abuseServer2.moderationComment).to.be.null
553 521
554 expect(abuseServer2.countReportsForReporter).to.equal(1) 522 expect(abuseServer2.countReportsForReporter).to.equal(1)
555 expect(abuseServer2.countReportsForReportee).to.equal(1) 523 expect(abuseServer2.countReportsForReportee).to.equal(1)
524 }
556 }) 525 })
557 526
558 it('Should keep the comment abuse when deleting the comment', async function () { 527 it('Should keep the comment abuse when deleting the comment', async function () {
559 this.timeout(10000) 528 this.timeout(10000)
560 529
561 const commentServer2 = await getComment(servers[0].url, servers[1].video.id) 530 const commentServer2 = await getComment(servers[0], servers[1].store.videoCreated.id)
562 531
563 await deleteVideoComment(servers[0].url, servers[0].accessToken, servers[1].video.uuid, commentServer2.id) 532 await servers[0].comments.delete({ videoId: servers[1].store.videoCreated.uuid, commentId: commentServer2.id })
564 533
565 await waitJobs(servers) 534 await waitJobs(servers)
566 535
567 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) 536 const body = await commands[0].getAdminList({ filter: 'comment' })
568 expect(res.body.total).to.equal(2) 537 expect(body.total).to.equal(2)
569 expect(res.body.data).to.have.lengthOf(2) 538 expect(body.data).to.have.lengthOf(2)
570 539
571 const abuse = (res.body.data as AdminAbuse[]).find(a => a.comment?.id === commentServer2.id) 540 const abuse = body.data.find(a => a.comment?.id === commentServer2.id)
572 expect(abuse).to.not.be.undefined 541 expect(abuse).to.not.be.undefined
573 542
574 expect(abuse.comment.text).to.be.empty 543 expect(abuse.comment.text).to.be.empty
@@ -579,72 +548,60 @@ describe('Test abuses', function () {
579 it('Should delete the comment abuse', async function () { 548 it('Should delete the comment abuse', async function () {
580 this.timeout(10000) 549 this.timeout(10000)
581 550
582 await deleteAbuse(servers[1].url, servers[1].accessToken, abuseServer2.id) 551 await commands[1].delete({ abuseId: abuseServer2.id })
583 552
584 await waitJobs(servers) 553 await waitJobs(servers)
585 554
586 { 555 {
587 const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) 556 const body = await commands[1].getAdminList({ filter: 'comment' })
588 expect(res.body.total).to.equal(0) 557 expect(body.total).to.equal(0)
589 expect(res.body.data.length).to.equal(0) 558 expect(body.data.length).to.equal(0)
590 } 559 }
591 560
592 { 561 {
593 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) 562 const body = await commands[0].getAdminList({ filter: 'comment' })
594 expect(res.body.total).to.equal(2) 563 expect(body.total).to.equal(2)
595 } 564 }
596 }) 565 })
597 566
598 it('Should list and filter video abuses', async function () { 567 it('Should list and filter video abuses', async function () {
599 { 568 {
600 const res = await getAdminAbusesList({ 569 const body = await commands[0].getAdminList({ filter: 'comment', searchReportee: 'foo' })
601 url: servers[0].url, 570 expect(body.total).to.equal(0)
602 token: servers[0].accessToken,
603 filter: 'comment',
604 searchReportee: 'foo'
605 })
606 expect(res.body.total).to.equal(0)
607 } 571 }
608 572
609 { 573 {
610 const res = await getAdminAbusesList({ 574 const body = await commands[0].getAdminList({ filter: 'comment', searchReportee: 'ot' })
611 url: servers[0].url, 575 expect(body.total).to.equal(2)
612 token: servers[0].accessToken,
613 filter: 'comment',
614 searchReportee: 'ot'
615 })
616 expect(res.body.total).to.equal(2)
617 } 576 }
618 577
619 { 578 {
620 const baseParams = { url: servers[0].url, token: servers[0].accessToken, filter: 'comment' as AbuseFilter, start: 1, count: 1 } 579 const body = await commands[0].getAdminList({ filter: 'comment', start: 1, count: 1, sort: 'createdAt' })
621 580 expect(body.data).to.have.lengthOf(1)
622 const res1 = await getAdminAbusesList(immutableAssign(baseParams, { sort: 'createdAt' })) 581 expect(body.data[0].comment.text).to.be.empty
623 expect(res1.body.data).to.have.lengthOf(1) 582 }
624 expect(res1.body.data[0].comment.text).to.be.empty
625 583
626 const res2 = await getAdminAbusesList(immutableAssign(baseParams, { sort: '-createdAt' })) 584 {
627 expect(res2.body.data).to.have.lengthOf(1) 585 const body = await commands[0].getAdminList({ filter: 'comment', start: 1, count: 1, sort: '-createdAt' })
628 expect(res2.body.data[0].comment.text).to.equal('comment server 1') 586 expect(body.data).to.have.lengthOf(1)
587 expect(body.data[0].comment.text).to.equal('comment server 1')
629 } 588 }
630 }) 589 })
631 }) 590 })
632 591
633 describe('Account abuses', function () { 592 describe('Account abuses', function () {
634 593
635 async function getAccountFromServer (url: string, name: string, server: ServerInfo) { 594 function getAccountFromServer (server: PeerTubeServer, targetName: string, targetServer: PeerTubeServer) {
636 const res = await getAccount(url, name + '@' + server.host) 595 return server.accounts.get({ accountName: targetName + '@' + targetServer.host })
637
638 return res.body as Account
639 } 596 }
640 597
641 before(async function () { 598 before(async function () {
642 this.timeout(50000) 599 this.timeout(50000)
643 600
644 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: 'user_1', password: 'donald' }) 601 await servers[0].users.create({ username: 'user_1', password: 'donald' })
645 602
646 const token = await generateUserAccessToken(servers[1], 'user_2') 603 const token = await servers[1].users.generateUserAndToken('user_2')
647 await uploadVideo(servers[1].url, token, { name: 'super video' }) 604 await servers[1].videos.upload({ token, attributes: { name: 'super video' } })
648 605
649 await waitJobs(servers) 606 await waitJobs(servers)
650 }) 607 })
@@ -652,22 +609,22 @@ describe('Test abuses', function () {
652 it('Should report abuse on an account', async function () { 609 it('Should report abuse on an account', async function () {
653 this.timeout(15000) 610 this.timeout(15000)
654 611
655 const account = await getAccountFromServer(servers[0].url, 'user_1', servers[0]) 612 const account = await getAccountFromServer(servers[0], 'user_1', servers[0])
656 613
657 const reason = 'it is a bad account' 614 const reason = 'it is a bad account'
658 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, accountId: account.id, reason }) 615 await commands[0].report({ accountId: account.id, reason })
659 616
660 await waitJobs(servers) 617 await waitJobs(servers)
661 }) 618 })
662 619
663 it('Should have 1 account abuse on server 1 and 0 on server 2', async function () { 620 it('Should have 1 account abuse on server 1 and 0 on server 2', async function () {
664 { 621 {
665 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) 622 const body = await commands[0].getAdminList({ filter: 'account' })
666 623
667 expect(res.body.total).to.equal(1) 624 expect(body.total).to.equal(1)
668 expect(res.body.data).to.have.lengthOf(1) 625 expect(body.data).to.have.lengthOf(1)
669 626
670 const abuse: AdminAbuse = res.body.data[0] 627 const abuse = body.data[0]
671 expect(abuse.reason).to.equal('it is a bad account') 628 expect(abuse.reason).to.equal('it is a bad account')
672 629
673 expect(abuse.reporterAccount.name).to.equal('root') 630 expect(abuse.reporterAccount.name).to.equal('root')
@@ -681,96 +638,100 @@ describe('Test abuses', function () {
681 } 638 }
682 639
683 { 640 {
684 const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) 641 const body = await commands[1].getAdminList({ filter: 'comment' })
685 expect(res.body.total).to.equal(0) 642 expect(body.total).to.equal(0)
686 expect(res.body.data.length).to.equal(0) 643 expect(body.data.length).to.equal(0)
687 } 644 }
688 }) 645 })
689 646
690 it('Should report abuse on a remote account', async function () { 647 it('Should report abuse on a remote account', async function () {
691 this.timeout(10000) 648 this.timeout(10000)
692 649
693 const account = await getAccountFromServer(servers[0].url, 'user_2', servers[1]) 650 const account = await getAccountFromServer(servers[0], 'user_2', servers[1])
694 651
695 const reason = 'it is a really bad account' 652 const reason = 'it is a really bad account'
696 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, accountId: account.id, reason }) 653 await commands[0].report({ accountId: account.id, reason })
697 654
698 await waitJobs(servers) 655 await waitJobs(servers)
699 }) 656 })
700 657
701 it('Should have 2 comment abuses on server 1 and 1 on server 2', async function () { 658 it('Should have 2 comment abuses on server 1 and 1 on server 2', async function () {
702 const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) 659 {
703 expect(res1.body.total).to.equal(2) 660 const body = await commands[0].getAdminList({ filter: 'account' })
704 expect(res1.body.data.length).to.equal(2) 661 expect(body.total).to.equal(2)
662 expect(body.data.length).to.equal(2)
705 663
706 const abuse: AdminAbuse = res1.body.data[0] 664 const abuse: AdminAbuse = body.data[0]
707 expect(abuse.reason).to.equal('it is a bad account') 665 expect(abuse.reason).to.equal('it is a bad account')
708 666
709 const abuse2: AdminAbuse = res1.body.data[1] 667 const abuse2: AdminAbuse = body.data[1]
710 expect(abuse2.reason).to.equal('it is a really bad account') 668 expect(abuse2.reason).to.equal('it is a really bad account')
711 669
712 expect(abuse2.reporterAccount.name).to.equal('root') 670 expect(abuse2.reporterAccount.name).to.equal('root')
713 expect(abuse2.reporterAccount.host).to.equal(servers[0].host) 671 expect(abuse2.reporterAccount.host).to.equal(servers[0].host)
714 672
715 expect(abuse2.video).to.be.null 673 expect(abuse2.video).to.be.null
716 expect(abuse2.comment).to.be.null 674 expect(abuse2.comment).to.be.null
717 675
718 expect(abuse2.state.id).to.equal(AbuseState.PENDING) 676 expect(abuse2.state.id).to.equal(AbuseState.PENDING)
719 expect(abuse2.state.label).to.equal('Pending') 677 expect(abuse2.state.label).to.equal('Pending')
720 678
721 expect(abuse2.moderationComment).to.be.null 679 expect(abuse2.moderationComment).to.be.null
680 }
722 681
723 const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'account' }) 682 {
724 expect(res2.body.total).to.equal(1) 683 const body = await commands[1].getAdminList({ filter: 'account' })
725 expect(res2.body.data.length).to.equal(1) 684 expect(body.total).to.equal(1)
685 expect(body.data.length).to.equal(1)
726 686
727 abuseServer2 = res2.body.data[0] 687 abuseServer2 = body.data[0]
728 688
729 expect(abuseServer2.reason).to.equal('it is a really bad account') 689 expect(abuseServer2.reason).to.equal('it is a really bad account')
730 690
731 expect(abuseServer2.reporterAccount.name).to.equal('root') 691 expect(abuseServer2.reporterAccount.name).to.equal('root')
732 expect(abuseServer2.reporterAccount.host).to.equal(servers[0].host) 692 expect(abuseServer2.reporterAccount.host).to.equal(servers[0].host)
733 693
734 expect(abuseServer2.state.id).to.equal(AbuseState.PENDING) 694 expect(abuseServer2.state.id).to.equal(AbuseState.PENDING)
735 expect(abuseServer2.state.label).to.equal('Pending') 695 expect(abuseServer2.state.label).to.equal('Pending')
736 696
737 expect(abuseServer2.moderationComment).to.be.null 697 expect(abuseServer2.moderationComment).to.be.null
698 }
738 }) 699 })
739 700
740 it('Should keep the account abuse when deleting the account', async function () { 701 it('Should keep the account abuse when deleting the account', async function () {
741 this.timeout(10000) 702 this.timeout(10000)
742 703
743 const account = await getAccountFromServer(servers[1].url, 'user_2', servers[1]) 704 const account = await getAccountFromServer(servers[1], 'user_2', servers[1])
744 await removeUser(servers[1].url, account.userId, servers[1].accessToken) 705 await servers[1].users.remove({ userId: account.userId })
745 706
746 await waitJobs(servers) 707 await waitJobs(servers)
747 708
748 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) 709 const body = await commands[0].getAdminList({ filter: 'account' })
749 expect(res.body.total).to.equal(2) 710 expect(body.total).to.equal(2)
750 expect(res.body.data).to.have.lengthOf(2) 711 expect(body.data).to.have.lengthOf(2)
751 712
752 const abuse = (res.body.data as AdminAbuse[]).find(a => a.reason === 'it is a really bad account') 713 const abuse = body.data.find(a => a.reason === 'it is a really bad account')
753 expect(abuse).to.not.be.undefined 714 expect(abuse).to.not.be.undefined
754 }) 715 })
755 716
756 it('Should delete the account abuse', async function () { 717 it('Should delete the account abuse', async function () {
757 this.timeout(10000) 718 this.timeout(10000)
758 719
759 await deleteAbuse(servers[1].url, servers[1].accessToken, abuseServer2.id) 720 await commands[1].delete({ abuseId: abuseServer2.id })
760 721
761 await waitJobs(servers) 722 await waitJobs(servers)
762 723
763 { 724 {
764 const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'account' }) 725 const body = await commands[1].getAdminList({ filter: 'account' })
765 expect(res.body.total).to.equal(0) 726 expect(body.total).to.equal(0)
766 expect(res.body.data.length).to.equal(0) 727 expect(body.data.length).to.equal(0)
767 } 728 }
768 729
769 { 730 {
770 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) 731 const body = await commands[0].getAdminList({ filter: 'account' })
771 expect(res.body.total).to.equal(2) 732 expect(body.total).to.equal(2)
772 733
773 abuseServer1 = res.body.data[0] 734 abuseServer1 = body.data[0]
774 } 735 }
775 }) 736 })
776 }) 737 })
@@ -778,20 +739,18 @@ describe('Test abuses', function () {
778 describe('Common actions on abuses', function () { 739 describe('Common actions on abuses', function () {
779 740
780 it('Should update the state of an abuse', async function () { 741 it('Should update the state of an abuse', async function () {
781 const body = { state: AbuseState.REJECTED } 742 await commands[0].update({ abuseId: abuseServer1.id, body: { state: AbuseState.REJECTED } })
782 await updateAbuse(servers[0].url, servers[0].accessToken, abuseServer1.id, body)
783 743
784 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, id: abuseServer1.id }) 744 const body = await commands[0].getAdminList({ id: abuseServer1.id })
785 expect(res.body.data[0].state.id).to.equal(AbuseState.REJECTED) 745 expect(body.data[0].state.id).to.equal(AbuseState.REJECTED)
786 }) 746 })
787 747
788 it('Should add a moderation comment', async function () { 748 it('Should add a moderation comment', async function () {
789 const body = { state: AbuseState.ACCEPTED, moderationComment: 'It is valid' } 749 await commands[0].update({ abuseId: abuseServer1.id, body: { state: AbuseState.ACCEPTED, moderationComment: 'Valid' } })
790 await updateAbuse(servers[0].url, servers[0].accessToken, abuseServer1.id, body)
791 750
792 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, id: abuseServer1.id }) 751 const body = await commands[0].getAdminList({ id: abuseServer1.id })
793 expect(res.body.data[0].state.id).to.equal(AbuseState.ACCEPTED) 752 expect(body.data[0].state.id).to.equal(AbuseState.ACCEPTED)
794 expect(res.body.data[0].moderationComment).to.equal('It is valid') 753 expect(body.data[0].moderationComment).to.equal('Valid')
795 }) 754 })
796 }) 755 })
797 756
@@ -800,20 +759,20 @@ describe('Test abuses', function () {
800 let userAccessToken: string 759 let userAccessToken: string
801 760
802 before(async function () { 761 before(async function () {
803 userAccessToken = await generateUserAccessToken(servers[0], 'user_42') 762 userAccessToken = await servers[0].users.generateUserAndToken('user_42')
804 763
805 await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: servers[0].video.id, reason: 'user reason 1' }) 764 await commands[0].report({ token: userAccessToken, videoId: servers[0].store.videoCreated.id, reason: 'user reason 1' })
806 765
807 const videoId = await getVideoIdFromUUID(servers[0].url, servers[1].video.uuid) 766 const videoId = await servers[0].videos.getId({ uuid: servers[1].store.videoCreated.uuid })
808 await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId, reason: 'user reason 2' }) 767 await commands[0].report({ token: userAccessToken, videoId, reason: 'user reason 2' })
809 }) 768 })
810 769
811 it('Should correctly list my abuses', async function () { 770 it('Should correctly list my abuses', async function () {
812 { 771 {
813 const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 0, count: 5, sort: 'createdAt' }) 772 const body = await commands[0].getUserList({ token: userAccessToken, start: 0, count: 5, sort: 'createdAt' })
814 expect(res.body.total).to.equal(2) 773 expect(body.total).to.equal(2)
815 774
816 const abuses: UserAbuse[] = res.body.data 775 const abuses = body.data
817 expect(abuses[0].reason).to.equal('user reason 1') 776 expect(abuses[0].reason).to.equal('user reason 1')
818 expect(abuses[1].reason).to.equal('user reason 2') 777 expect(abuses[1].reason).to.equal('user reason 2')
819 778
@@ -821,95 +780,77 @@ describe('Test abuses', function () {
821 } 780 }
822 781
823 { 782 {
824 const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 1, count: 1, sort: 'createdAt' }) 783 const body = await commands[0].getUserList({ token: userAccessToken, start: 1, count: 1, sort: 'createdAt' })
825 expect(res.body.total).to.equal(2) 784 expect(body.total).to.equal(2)
826 785
827 const abuses: UserAbuse[] = res.body.data 786 const abuses: UserAbuse[] = body.data
828 expect(abuses[0].reason).to.equal('user reason 2') 787 expect(abuses[0].reason).to.equal('user reason 2')
829 } 788 }
830 789
831 { 790 {
832 const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 1, count: 1, sort: '-createdAt' }) 791 const body = await commands[0].getUserList({ token: userAccessToken, start: 1, count: 1, sort: '-createdAt' })
833 expect(res.body.total).to.equal(2) 792 expect(body.total).to.equal(2)
834 793
835 const abuses: UserAbuse[] = res.body.data 794 const abuses: UserAbuse[] = body.data
836 expect(abuses[0].reason).to.equal('user reason 1') 795 expect(abuses[0].reason).to.equal('user reason 1')
837 } 796 }
838 }) 797 })
839 798
840 it('Should correctly filter my abuses by id', async function () { 799 it('Should correctly filter my abuses by id', async function () {
841 const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, id: abuseId1 }) 800 const body = await commands[0].getUserList({ token: userAccessToken, id: abuseId1 })
801 expect(body.total).to.equal(1)
842 802
843 expect(res.body.total).to.equal(1) 803 const abuses: UserAbuse[] = body.data
844
845 const abuses: UserAbuse[] = res.body.data
846 expect(abuses[0].reason).to.equal('user reason 1') 804 expect(abuses[0].reason).to.equal('user reason 1')
847 }) 805 })
848 806
849 it('Should correctly filter my abuses by search', async function () { 807 it('Should correctly filter my abuses by search', async function () {
850 const res = await getUserAbusesList({ 808 const body = await commands[0].getUserList({ token: userAccessToken, search: 'server 2' })
851 url: servers[0].url, 809 expect(body.total).to.equal(1)
852 token: userAccessToken,
853 search: 'server 2'
854 })
855
856 expect(res.body.total).to.equal(1)
857 810
858 const abuses: UserAbuse[] = res.body.data 811 const abuses: UserAbuse[] = body.data
859 expect(abuses[0].reason).to.equal('user reason 2') 812 expect(abuses[0].reason).to.equal('user reason 2')
860 }) 813 })
861 814
862 it('Should correctly filter my abuses by state', async function () { 815 it('Should correctly filter my abuses by state', async function () {
863 const body = { state: AbuseState.REJECTED } 816 await commands[0].update({ abuseId: abuseId1, body: { state: AbuseState.REJECTED } })
864 await updateAbuse(servers[0].url, servers[0].accessToken, abuseId1, body)
865 817
866 const res = await getUserAbusesList({ 818 const body = await commands[0].getUserList({ token: userAccessToken, state: AbuseState.REJECTED })
867 url: servers[0].url, 819 expect(body.total).to.equal(1)
868 token: userAccessToken,
869 state: AbuseState.REJECTED
870 })
871
872 expect(res.body.total).to.equal(1)
873 820
874 const abuses: UserAbuse[] = res.body.data 821 const abuses: UserAbuse[] = body.data
875 expect(abuses[0].reason).to.equal('user reason 1') 822 expect(abuses[0].reason).to.equal('user reason 1')
876 }) 823 })
877 }) 824 })
878 825
879 describe('Abuse messages', async function () { 826 describe('Abuse messages', async function () {
880 let abuseId: number 827 let abuseId: number
881 let userAccessToken: string 828 let userToken: string
882 let abuseMessageUserId: number 829 let abuseMessageUserId: number
883 let abuseMessageModerationId: number 830 let abuseMessageModerationId: number
884 831
885 before(async function () { 832 before(async function () {
886 userAccessToken = await generateUserAccessToken(servers[0], 'user_43') 833 userToken = await servers[0].users.generateUserAndToken('user_43')
887 834
888 const res = await reportAbuse({ 835 const body = await commands[0].report({ token: userToken, videoId: servers[0].store.videoCreated.id, reason: 'user 43 reason 1' })
889 url: servers[0].url, 836 abuseId = body.abuse.id
890 token: userAccessToken,
891 videoId: servers[0].video.id,
892 reason: 'user 43 reason 1'
893 })
894
895 abuseId = res.body.abuse.id
896 }) 837 })
897 838
898 it('Should create some messages on the abuse', async function () { 839 it('Should create some messages on the abuse', async function () {
899 await addAbuseMessage(servers[0].url, userAccessToken, abuseId, 'message 1') 840 await commands[0].addMessage({ token: userToken, abuseId, message: 'message 1' })
900 await addAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, 'message 2') 841 await commands[0].addMessage({ abuseId, message: 'message 2' })
901 await addAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, 'message 3') 842 await commands[0].addMessage({ abuseId, message: 'message 3' })
902 await addAbuseMessage(servers[0].url, userAccessToken, abuseId, 'message 4') 843 await commands[0].addMessage({ token: userToken, abuseId, message: 'message 4' })
903 }) 844 })
904 845
905 it('Should have the correct messages count when listing abuses', async function () { 846 it('Should have the correct messages count when listing abuses', async function () {
906 const results = await Promise.all([ 847 const results = await Promise.all([
907 getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, start: 0, count: 50 }), 848 commands[0].getAdminList({ start: 0, count: 50 }),
908 getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 0, count: 50 }) 849 commands[0].getUserList({ token: userToken, start: 0, count: 50 })
909 ]) 850 ])
910 851
911 for (const res of results) { 852 for (const body of results) {
912 const abuses: AdminAbuse[] = res.body.data 853 const abuses = body.data
913 const abuse = abuses.find(a => a.id === abuseId) 854 const abuse = abuses.find(a => a.id === abuseId)
914 expect(abuse.countMessages).to.equal(4) 855 expect(abuse.countMessages).to.equal(4)
915 } 856 }
@@ -917,14 +858,14 @@ describe('Test abuses', function () {
917 858
918 it('Should correctly list messages of this abuse', async function () { 859 it('Should correctly list messages of this abuse', async function () {
919 const results = await Promise.all([ 860 const results = await Promise.all([
920 listAbuseMessages(servers[0].url, servers[0].accessToken, abuseId), 861 commands[0].listMessages({ abuseId }),
921 listAbuseMessages(servers[0].url, userAccessToken, abuseId) 862 commands[0].listMessages({ token: userToken, abuseId })
922 ]) 863 ])
923 864
924 for (const res of results) { 865 for (const body of results) {
925 expect(res.body.total).to.equal(4) 866 expect(body.total).to.equal(4)
926 867
927 const abuseMessages: AbuseMessage[] = res.body.data 868 const abuseMessages: AbuseMessage[] = body.data
928 869
929 expect(abuseMessages[0].message).to.equal('message 1') 870 expect(abuseMessages[0].message).to.equal('message 1')
930 expect(abuseMessages[0].byModerator).to.be.false 871 expect(abuseMessages[0].byModerator).to.be.false
@@ -948,19 +889,18 @@ describe('Test abuses', function () {
948 }) 889 })
949 890
950 it('Should delete messages', async function () { 891 it('Should delete messages', async function () {
951 await deleteAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, abuseMessageModerationId) 892 await commands[0].deleteMessage({ abuseId, messageId: abuseMessageModerationId })
952 await deleteAbuseMessage(servers[0].url, userAccessToken, abuseId, abuseMessageUserId) 893 await commands[0].deleteMessage({ token: userToken, abuseId, messageId: abuseMessageUserId })
953 894
954 const results = await Promise.all([ 895 const results = await Promise.all([
955 listAbuseMessages(servers[0].url, servers[0].accessToken, abuseId), 896 commands[0].listMessages({ abuseId }),
956 listAbuseMessages(servers[0].url, userAccessToken, abuseId) 897 commands[0].listMessages({ token: userToken, abuseId })
957 ]) 898 ])
958 899
959 for (const res of results) { 900 for (const body of results) {
960 expect(res.body.total).to.equal(2) 901 expect(body.total).to.equal(2)
961
962 const abuseMessages: AbuseMessage[] = res.body.data
963 902
903 const abuseMessages: AbuseMessage[] = body.data
964 expect(abuseMessages[0].message).to.equal('message 2') 904 expect(abuseMessages[0].message).to.equal('message 2')
965 expect(abuseMessages[1].message).to.equal('message 4') 905 expect(abuseMessages[1].message).to.equal('message 4')
966 } 906 }
diff --git a/server/tests/api/moderation/blocklist-notification.ts b/server/tests/api/moderation/blocklist-notification.ts
index 4fb3c95f2..75b15c298 100644
--- a/server/tests/api/moderation/blocklist-notification.ts
+++ b/server/tests/api/moderation/blocklist-notification.ts
@@ -2,47 +2,22 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { getUserNotifications, markAsReadAllNotifications } from '@shared/extra-utils/users/user-notifications' 5import { cleanupTests, createMultipleServers, doubleFollow, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
6import { addUserSubscription, removeUserSubscription } from '@shared/extra-utils/users/user-subscriptions' 6import { UserNotificationType } from '@shared/models'
7import { UserNotification, UserNotificationType } from '@shared/models'
8import {
9 cleanupTests,
10 createUser,
11 doubleFollow,
12 flushAndRunMultipleServers,
13 ServerInfo,
14 uploadVideo,
15 userLogin
16} from '../../../../shared/extra-utils/index'
17import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
18import {
19 addAccountToAccountBlocklist,
20 addAccountToServerBlocklist,
21 addServerToAccountBlocklist,
22 addServerToServerBlocklist,
23 removeAccountFromAccountBlocklist,
24 removeAccountFromServerBlocklist,
25 removeServerFromAccountBlocklist
26} from '../../../../shared/extra-utils/users/blocklist'
27import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
28import { addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments'
29 7
30const expect = chai.expect 8const expect = chai.expect
31 9
32async function checkNotifications (url: string, token: string, expected: UserNotificationType[]) { 10async function checkNotifications (server: PeerTubeServer, token: string, expected: UserNotificationType[]) {
33 const res = await getUserNotifications(url, token, 0, 10, true) 11 const { data } = await server.notifications.list({ token, start: 0, count: 10, unread: true })
34 12 expect(data).to.have.lengthOf(expected.length)
35 const notifications: UserNotification[] = res.body.data
36
37 expect(notifications).to.have.lengthOf(expected.length)
38 13
39 for (const type of expected) { 14 for (const type of expected) {
40 expect(notifications.find(n => n.type === type)).to.exist 15 expect(data.find(n => n.type === type)).to.exist
41 } 16 }
42} 17}
43 18
44describe('Test blocklist', function () { 19describe('Test blocklist', function () {
45 let servers: ServerInfo[] 20 let servers: PeerTubeServer[]
46 let videoUUID: string 21 let videoUUID: string
47 22
48 let userToken1: string 23 let userToken1: string
@@ -51,30 +26,34 @@ describe('Test blocklist', function () {
51 26
52 async function resetState () { 27 async function resetState () {
53 try { 28 try {
54 await removeUserSubscription(servers[1].url, remoteUserToken, 'user1_channel@' + servers[0].host) 29 await servers[1].subscriptions.remove({ token: remoteUserToken, uri: 'user1_channel@' + servers[0].host })
55 await removeUserSubscription(servers[1].url, remoteUserToken, 'user2_channel@' + servers[0].host) 30 await servers[1].subscriptions.remove({ token: remoteUserToken, uri: 'user2_channel@' + servers[0].host })
56 } catch {} 31 } catch {}
57 32
58 await waitJobs(servers) 33 await waitJobs(servers)
59 34
60 await markAsReadAllNotifications(servers[0].url, userToken1) 35 await servers[0].notifications.markAsReadAll({ token: userToken1 })
61 await markAsReadAllNotifications(servers[0].url, userToken2) 36 await servers[0].notifications.markAsReadAll({ token: userToken2 })
62 37
63 { 38 {
64 const res = await uploadVideo(servers[0].url, userToken1, { name: 'video' }) 39 const { uuid } = await servers[0].videos.upload({ token: userToken1, attributes: { name: 'video' } })
65 videoUUID = res.body.video.uuid 40 videoUUID = uuid
66 41
67 await waitJobs(servers) 42 await waitJobs(servers)
68 } 43 }
69 44
70 { 45 {
71 await addVideoCommentThread(servers[1].url, remoteUserToken, videoUUID, '@user2@' + servers[0].host + ' hello') 46 await servers[1].comments.createThread({
47 token: remoteUserToken,
48 videoId: videoUUID,
49 text: '@user2@' + servers[0].host + ' hello'
50 })
72 } 51 }
73 52
74 { 53 {
75 54
76 await addUserSubscription(servers[1].url, remoteUserToken, 'user1_channel@' + servers[0].host) 55 await servers[1].subscriptions.add({ token: remoteUserToken, targetUri: 'user1_channel@' + servers[0].host })
77 await addUserSubscription(servers[1].url, remoteUserToken, 'user2_channel@' + servers[0].host) 56 await servers[1].subscriptions.add({ token: remoteUserToken, targetUri: 'user2_channel@' + servers[0].host })
78 } 57 }
79 58
80 await waitJobs(servers) 59 await waitJobs(servers)
@@ -83,36 +62,34 @@ describe('Test blocklist', function () {
83 before(async function () { 62 before(async function () {
84 this.timeout(60000) 63 this.timeout(60000)
85 64
86 servers = await flushAndRunMultipleServers(2) 65 servers = await createMultipleServers(2)
87 await setAccessTokensToServers(servers) 66 await setAccessTokensToServers(servers)
88 67
89 { 68 {
90 const user = { username: 'user1', password: 'password' } 69 const user = { username: 'user1', password: 'password' }
91 await createUser({ 70 await servers[0].users.create({
92 url: servers[0].url,
93 accessToken: servers[0].accessToken,
94 username: user.username, 71 username: user.username,
95 password: user.password, 72 password: user.password,
96 videoQuota: -1, 73 videoQuota: -1,
97 videoQuotaDaily: -1 74 videoQuotaDaily: -1
98 }) 75 })
99 76
100 userToken1 = await userLogin(servers[0], user) 77 userToken1 = await servers[0].login.getAccessToken(user)
101 await uploadVideo(servers[0].url, userToken1, { name: 'video user 1' }) 78 await servers[0].videos.upload({ token: userToken1, attributes: { name: 'video user 1' } })
102 } 79 }
103 80
104 { 81 {
105 const user = { username: 'user2', password: 'password' } 82 const user = { username: 'user2', password: 'password' }
106 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 83 await servers[0].users.create({ username: user.username, password: user.password })
107 84
108 userToken2 = await userLogin(servers[0], user) 85 userToken2 = await servers[0].login.getAccessToken(user)
109 } 86 }
110 87
111 { 88 {
112 const user = { username: 'user3', password: 'password' } 89 const user = { username: 'user3', password: 'password' }
113 await createUser({ url: servers[1].url, accessToken: servers[1].accessToken, username: user.username, password: user.password }) 90 await servers[1].users.create({ username: user.username, password: user.password })
114 91
115 remoteUserToken = await userLogin(servers[1], user) 92 remoteUserToken = await servers[1].login.getAccessToken(user)
116 } 93 }
117 94
118 await doubleFollow(servers[0], servers[1]) 95 await doubleFollow(servers[0], servers[1])
@@ -128,26 +105,26 @@ describe('Test blocklist', function () {
128 105
129 it('Should have appropriate notifications', async function () { 106 it('Should have appropriate notifications', async function () {
130 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ] 107 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ]
131 await checkNotifications(servers[0].url, userToken1, notifs) 108 await checkNotifications(servers[0], userToken1, notifs)
132 }) 109 })
133 110
134 it('Should block an account', async function () { 111 it('Should block an account', async function () {
135 this.timeout(10000) 112 this.timeout(10000)
136 113
137 await addAccountToAccountBlocklist(servers[0].url, userToken1, 'user3@' + servers[1].host) 114 await servers[0].blocklist.addToMyBlocklist({ token: userToken1, account: 'user3@' + servers[1].host })
138 await waitJobs(servers) 115 await waitJobs(servers)
139 }) 116 })
140 117
141 it('Should not have notifications from this account', async function () { 118 it('Should not have notifications from this account', async function () {
142 await checkNotifications(servers[0].url, userToken1, []) 119 await checkNotifications(servers[0], userToken1, [])
143 }) 120 })
144 121
145 it('Should have notifications of this account on user 2', async function () { 122 it('Should have notifications of this account on user 2', async function () {
146 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ] 123 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ]
147 124
148 await checkNotifications(servers[0].url, userToken2, notifs) 125 await checkNotifications(servers[0], userToken2, notifs)
149 126
150 await removeAccountFromAccountBlocklist(servers[0].url, userToken1, 'user3@' + servers[1].host) 127 await servers[0].blocklist.removeFromMyBlocklist({ token: userToken1, account: 'user3@' + servers[1].host })
151 }) 128 })
152 }) 129 })
153 130
@@ -161,26 +138,26 @@ describe('Test blocklist', function () {
161 138
162 it('Should have appropriate notifications', async function () { 139 it('Should have appropriate notifications', async function () {
163 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ] 140 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ]
164 await checkNotifications(servers[0].url, userToken1, notifs) 141 await checkNotifications(servers[0], userToken1, notifs)
165 }) 142 })
166 143
167 it('Should block an account', async function () { 144 it('Should block an account', async function () {
168 this.timeout(10000) 145 this.timeout(10000)
169 146
170 await addServerToAccountBlocklist(servers[0].url, userToken1, servers[1].host) 147 await servers[0].blocklist.addToMyBlocklist({ token: userToken1, server: servers[1].host })
171 await waitJobs(servers) 148 await waitJobs(servers)
172 }) 149 })
173 150
174 it('Should not have notifications from this account', async function () { 151 it('Should not have notifications from this account', async function () {
175 await checkNotifications(servers[0].url, userToken1, []) 152 await checkNotifications(servers[0], userToken1, [])
176 }) 153 })
177 154
178 it('Should have notifications of this account on user 2', async function () { 155 it('Should have notifications of this account on user 2', async function () {
179 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ] 156 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ]
180 157
181 await checkNotifications(servers[0].url, userToken2, notifs) 158 await checkNotifications(servers[0], userToken2, notifs)
182 159
183 await removeServerFromAccountBlocklist(servers[0].url, userToken1, servers[1].host) 160 await servers[0].blocklist.removeFromMyBlocklist({ token: userToken1, server: servers[1].host })
184 }) 161 })
185 }) 162 })
186 163
@@ -195,27 +172,27 @@ describe('Test blocklist', function () {
195 it('Should have appropriate notifications', async function () { 172 it('Should have appropriate notifications', async function () {
196 { 173 {
197 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ] 174 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ]
198 await checkNotifications(servers[0].url, userToken1, notifs) 175 await checkNotifications(servers[0], userToken1, notifs)
199 } 176 }
200 177
201 { 178 {
202 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ] 179 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ]
203 await checkNotifications(servers[0].url, userToken2, notifs) 180 await checkNotifications(servers[0], userToken2, notifs)
204 } 181 }
205 }) 182 })
206 183
207 it('Should block an account', async function () { 184 it('Should block an account', async function () {
208 this.timeout(10000) 185 this.timeout(10000)
209 186
210 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'user3@' + servers[1].host) 187 await servers[0].blocklist.addToServerBlocklist({ account: 'user3@' + servers[1].host })
211 await waitJobs(servers) 188 await waitJobs(servers)
212 }) 189 })
213 190
214 it('Should not have notifications from this account', async function () { 191 it('Should not have notifications from this account', async function () {
215 await checkNotifications(servers[0].url, userToken1, []) 192 await checkNotifications(servers[0], userToken1, [])
216 await checkNotifications(servers[0].url, userToken2, []) 193 await checkNotifications(servers[0], userToken2, [])
217 194
218 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'user3@' + servers[1].host) 195 await servers[0].blocklist.removeFromServerBlocklist({ account: 'user3@' + servers[1].host })
219 }) 196 })
220 }) 197 })
221 198
@@ -230,25 +207,25 @@ describe('Test blocklist', function () {
230 it('Should have appropriate notifications', async function () { 207 it('Should have appropriate notifications', async function () {
231 { 208 {
232 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ] 209 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ]
233 await checkNotifications(servers[0].url, userToken1, notifs) 210 await checkNotifications(servers[0], userToken1, notifs)
234 } 211 }
235 212
236 { 213 {
237 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ] 214 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ]
238 await checkNotifications(servers[0].url, userToken2, notifs) 215 await checkNotifications(servers[0], userToken2, notifs)
239 } 216 }
240 }) 217 })
241 218
242 it('Should block an account', async function () { 219 it('Should block an account', async function () {
243 this.timeout(10000) 220 this.timeout(10000)
244 221
245 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, servers[1].host) 222 await servers[0].blocklist.addToServerBlocklist({ server: servers[1].host })
246 await waitJobs(servers) 223 await waitJobs(servers)
247 }) 224 })
248 225
249 it('Should not have notifications from this account', async function () { 226 it('Should not have notifications from this account', async function () {
250 await checkNotifications(servers[0].url, userToken1, []) 227 await checkNotifications(servers[0], userToken1, [])
251 await checkNotifications(servers[0].url, userToken2, []) 228 await checkNotifications(servers[0], userToken2, [])
252 }) 229 })
253 }) 230 })
254 231
diff --git a/server/tests/api/moderation/blocklist.ts b/server/tests/api/moderation/blocklist.ts
index 793abbcb4..089af8b15 100644
--- a/server/tests/api/moderation/blocklist.ts
+++ b/server/tests/api/moderation/blocklist.ts
@@ -3,106 +3,67 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 addAccountToAccountBlocklist, 6 BlocklistCommand,
7 addAccountToServerBlocklist,
8 addServerToAccountBlocklist,
9 addServerToServerBlocklist,
10 addVideoCommentReply,
11 addVideoCommentThread,
12 cleanupTests, 7 cleanupTests,
13 createUser, 8 CommentsCommand,
14 deleteVideoComment, 9 createMultipleServers,
15 doubleFollow, 10 doubleFollow,
16 findCommentId, 11 PeerTubeServer,
17 flushAndRunMultipleServers,
18 follow,
19 getAccountBlocklistByAccount,
20 getAccountBlocklistByServer,
21 getServerBlocklistByAccount,
22 getServerBlocklistByServer,
23 getUserNotifications,
24 getVideoCommentThreads,
25 getVideosList,
26 getVideosListWithToken,
27 getVideoThreadComments,
28 removeAccountFromAccountBlocklist,
29 removeAccountFromServerBlocklist,
30 removeServerFromAccountBlocklist,
31 removeServerFromServerBlocklist,
32 ServerInfo,
33 setAccessTokensToServers, 12 setAccessTokensToServers,
34 unfollow,
35 uploadVideo,
36 userLogin,
37 waitJobs 13 waitJobs
38} from '@shared/extra-utils' 14} from '@shared/extra-utils'
39import { 15import { UserNotificationType } from '@shared/models'
40 AccountBlock,
41 ServerBlock,
42 UserNotification,
43 UserNotificationType,
44 Video,
45 VideoComment,
46 VideoCommentThreadTree
47} from '@shared/models'
48 16
49const expect = chai.expect 17const expect = chai.expect
50 18
51async function checkAllVideos (url: string, token: string) { 19async function checkAllVideos (server: PeerTubeServer, token: string) {
52 { 20 {
53 const res = await getVideosListWithToken(url, token) 21 const { data } = await server.videos.listWithToken({ token })
54 22 expect(data).to.have.lengthOf(5)
55 expect(res.body.data).to.have.lengthOf(5)
56 } 23 }
57 24
58 { 25 {
59 const res = await getVideosList(url) 26 const { data } = await server.videos.list()
60 27 expect(data).to.have.lengthOf(5)
61 expect(res.body.data).to.have.lengthOf(5)
62 } 28 }
63} 29}
64 30
65async function checkAllComments (url: string, token: string, videoUUID: string) { 31async function checkAllComments (server: PeerTubeServer, token: string, videoUUID: string) {
66 const resThreads = await getVideoCommentThreads(url, videoUUID, 0, 25, '-createdAt', token) 32 const { data } = await server.comments.listThreads({ videoId: videoUUID, start: 0, count: 25, sort: '-createdAt', token })
67 33
68 const allThreads: VideoComment[] = resThreads.body.data 34 const threads = data.filter(t => t.isDeleted === false)
69 const threads = allThreads.filter(t => t.isDeleted === false)
70 expect(threads).to.have.lengthOf(2) 35 expect(threads).to.have.lengthOf(2)
71 36
72 for (const thread of threads) { 37 for (const thread of threads) {
73 const res = await getVideoThreadComments(url, videoUUID, thread.id, token) 38 const tree = await server.comments.getThread({ videoId: videoUUID, threadId: thread.id, token })
74
75 const tree: VideoCommentThreadTree = res.body
76 expect(tree.children).to.have.lengthOf(1) 39 expect(tree.children).to.have.lengthOf(1)
77 } 40 }
78} 41}
79 42
80async function checkCommentNotification ( 43async function checkCommentNotification (
81 mainServer: ServerInfo, 44 mainServer: PeerTubeServer,
82 comment: { server: ServerInfo, token: string, videoUUID: string, text: string }, 45 comment: { server: PeerTubeServer, token: string, videoUUID: string, text: string },
83 check: 'presence' | 'absence' 46 check: 'presence' | 'absence'
84) { 47) {
85 const resComment = await addVideoCommentThread(comment.server.url, comment.token, comment.videoUUID, comment.text) 48 const command = comment.server.comments
86 const created = resComment.body.comment as VideoComment 49
87 const threadId = created.id 50 const { threadId, createdAt } = await command.createThread({ token: comment.token, videoId: comment.videoUUID, text: comment.text })
88 const createdAt = created.createdAt
89 51
90 await waitJobs([ mainServer, comment.server ]) 52 await waitJobs([ mainServer, comment.server ])
91 53
92 const res = await getUserNotifications(mainServer.url, mainServer.accessToken, 0, 30) 54 const { data } = await mainServer.notifications.list({ start: 0, count: 30 })
93 const commentNotifications = (res.body.data as UserNotification[]) 55 const commentNotifications = data.filter(n => n.comment && n.comment.video.uuid === comment.videoUUID && n.createdAt >= createdAt)
94 .filter(n => n.comment && n.comment.video.uuid === comment.videoUUID && n.createdAt >= createdAt)
95 56
96 if (check === 'presence') expect(commentNotifications).to.have.lengthOf(1) 57 if (check === 'presence') expect(commentNotifications).to.have.lengthOf(1)
97 else expect(commentNotifications).to.have.lengthOf(0) 58 else expect(commentNotifications).to.have.lengthOf(0)
98 59
99 await deleteVideoComment(comment.server.url, comment.token, comment.videoUUID, threadId) 60 await command.delete({ token: comment.token, videoId: comment.videoUUID, commentId: threadId })
100 61
101 await waitJobs([ mainServer, comment.server ]) 62 await waitJobs([ mainServer, comment.server ])
102} 63}
103 64
104describe('Test blocklist', function () { 65describe('Test blocklist', function () {
105 let servers: ServerInfo[] 66 let servers: PeerTubeServer[]
106 let videoUUID1: string 67 let videoUUID1: string
107 let videoUUID2: string 68 let videoUUID2: string
108 let videoUUID3: string 69 let videoUUID3: string
@@ -110,62 +71,73 @@ describe('Test blocklist', function () {
110 let userModeratorToken: string 71 let userModeratorToken: string
111 let userToken2: string 72 let userToken2: string
112 73
74 let command: BlocklistCommand
75 let commentsCommand: CommentsCommand[]
76
113 before(async function () { 77 before(async function () {
114 this.timeout(120000) 78 this.timeout(120000)
115 79
116 servers = await flushAndRunMultipleServers(3) 80 servers = await createMultipleServers(3)
117 await setAccessTokensToServers(servers) 81 await setAccessTokensToServers(servers)
118 82
83 command = servers[0].blocklist
84 commentsCommand = servers.map(s => s.comments)
85
119 { 86 {
120 const user = { username: 'user1', password: 'password' } 87 const user = { username: 'user1', password: 'password' }
121 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 88 await servers[0].users.create({ username: user.username, password: user.password })
122 89
123 userToken1 = await userLogin(servers[0], user) 90 userToken1 = await servers[0].login.getAccessToken(user)
124 await uploadVideo(servers[0].url, userToken1, { name: 'video user 1' }) 91 await servers[0].videos.upload({ token: userToken1, attributes: { name: 'video user 1' } })
125 } 92 }
126 93
127 { 94 {
128 const user = { username: 'moderator', password: 'password' } 95 const user = { username: 'moderator', password: 'password' }
129 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 96 await servers[0].users.create({ username: user.username, password: user.password })
130 97
131 userModeratorToken = await userLogin(servers[0], user) 98 userModeratorToken = await servers[0].login.getAccessToken(user)
132 } 99 }
133 100
134 { 101 {
135 const user = { username: 'user2', password: 'password' } 102 const user = { username: 'user2', password: 'password' }
136 await createUser({ url: servers[1].url, accessToken: servers[1].accessToken, username: user.username, password: user.password }) 103 await servers[1].users.create({ username: user.username, password: user.password })
137 104
138 userToken2 = await userLogin(servers[1], user) 105 userToken2 = await servers[1].login.getAccessToken(user)
139 await uploadVideo(servers[1].url, userToken2, { name: 'video user 2' }) 106 await servers[1].videos.upload({ token: userToken2, attributes: { name: 'video user 2' } })
140 } 107 }
141 108
142 { 109 {
143 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video server 1' }) 110 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video server 1' } })
144 videoUUID1 = res.body.video.uuid 111 videoUUID1 = uuid
145 } 112 }
146 113
147 { 114 {
148 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video server 2' }) 115 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video server 2' } })
149 videoUUID2 = res.body.video.uuid 116 videoUUID2 = uuid
150 } 117 }
151 118
152 { 119 {
153 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video 2 server 1' }) 120 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video 2 server 1' } })
154 videoUUID3 = res.body.video.uuid 121 videoUUID3 = uuid
155 } 122 }
156 123
157 await doubleFollow(servers[0], servers[1]) 124 await doubleFollow(servers[0], servers[1])
158 await doubleFollow(servers[0], servers[2]) 125 await doubleFollow(servers[0], servers[2])
159 126
160 { 127 {
161 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID1, 'comment root 1') 128 const created = await commentsCommand[0].createThread({ videoId: videoUUID1, text: 'comment root 1' })
162 const resReply = await addVideoCommentReply(servers[0].url, userToken1, videoUUID1, resComment.body.comment.id, 'comment user 1') 129 const reply = await commentsCommand[0].addReply({
163 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID1, resReply.body.comment.id, 'comment root 1') 130 token: userToken1,
131 videoId: videoUUID1,
132 toCommentId: created.id,
133 text: 'comment user 1'
134 })
135 await commentsCommand[0].addReply({ videoId: videoUUID1, toCommentId: reply.id, text: 'comment root 1' })
164 } 136 }
165 137
166 { 138 {
167 const resComment = await addVideoCommentThread(servers[0].url, userToken1, videoUUID1, 'comment user 1') 139 const created = await commentsCommand[0].createThread({ token: userToken1, videoId: videoUUID1, text: 'comment user 1' })
168 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID1, resComment.body.comment.id, 'comment root 1') 140 await commentsCommand[0].addReply({ videoId: videoUUID1, toCommentId: created.id, text: 'comment root 1' })
169 } 141 }
170 142
171 await waitJobs(servers) 143 await waitJobs(servers)
@@ -175,55 +147,60 @@ describe('Test blocklist', function () {
175 147
176 describe('When managing account blocklist', function () { 148 describe('When managing account blocklist', function () {
177 it('Should list all videos', function () { 149 it('Should list all videos', function () {
178 return checkAllVideos(servers[0].url, servers[0].accessToken) 150 return checkAllVideos(servers[0], servers[0].accessToken)
179 }) 151 })
180 152
181 it('Should list the comments', function () { 153 it('Should list the comments', function () {
182 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1) 154 return checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
183 }) 155 })
184 156
185 it('Should block a remote account', async function () { 157 it('Should block a remote account', async function () {
186 await addAccountToAccountBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:' + servers[1].port) 158 await command.addToMyBlocklist({ account: 'user2@localhost:' + servers[1].port })
187 }) 159 })
188 160
189 it('Should hide its videos', async function () { 161 it('Should hide its videos', async function () {
190 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) 162 const { data } = await servers[0].videos.listWithToken()
191 163
192 const videos: Video[] = res.body.data 164 expect(data).to.have.lengthOf(4)
193 expect(videos).to.have.lengthOf(4)
194 165
195 const v = videos.find(v => v.name === 'video user 2') 166 const v = data.find(v => v.name === 'video user 2')
196 expect(v).to.be.undefined 167 expect(v).to.be.undefined
197 }) 168 })
198 169
199 it('Should block a local account', async function () { 170 it('Should block a local account', async function () {
200 await addAccountToAccountBlocklist(servers[0].url, servers[0].accessToken, 'user1') 171 await command.addToMyBlocklist({ account: 'user1' })
201 }) 172 })
202 173
203 it('Should hide its videos', async function () { 174 it('Should hide its videos', async function () {
204 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) 175 const { data } = await servers[0].videos.listWithToken()
205 176
206 const videos: Video[] = res.body.data 177 expect(data).to.have.lengthOf(3)
207 expect(videos).to.have.lengthOf(3)
208 178
209 const v = videos.find(v => v.name === 'video user 1') 179 const v = data.find(v => v.name === 'video user 1')
210 expect(v).to.be.undefined 180 expect(v).to.be.undefined
211 }) 181 })
212 182
213 it('Should hide its comments', async function () { 183 it('Should hide its comments', async function () {
214 const resThreads = await getVideoCommentThreads(servers[0].url, videoUUID1, 0, 25, '-createdAt', servers[0].accessToken) 184 const { data } = await commentsCommand[0].listThreads({
215 185 token: servers[0].accessToken,
216 const threads: VideoComment[] = resThreads.body.data 186 videoId: videoUUID1,
217 expect(threads).to.have.lengthOf(1) 187 start: 0,
218 expect(threads[0].totalReplies).to.equal(1) 188 count: 25,
219 189 sort: '-createdAt'
220 const t = threads.find(t => t.text === 'comment user 1') 190 })
191
192 expect(data).to.have.lengthOf(1)
193 expect(data[0].totalReplies).to.equal(1)
194
195 const t = data.find(t => t.text === 'comment user 1')
221 expect(t).to.be.undefined 196 expect(t).to.be.undefined
222 197
223 for (const thread of threads) { 198 for (const thread of data) {
224 const res = await getVideoThreadComments(servers[0].url, videoUUID1, thread.id, servers[0].accessToken) 199 const tree = await commentsCommand[0].getThread({
225 200 videoId: videoUUID1,
226 const tree: VideoCommentThreadTree = res.body 201 threadId: thread.id,
202 token: servers[0].accessToken
203 })
227 expect(tree.children).to.have.lengthOf(0) 204 expect(tree.children).to.have.lengthOf(0)
228 } 205 }
229 }) 206 })
@@ -248,17 +225,15 @@ describe('Test blocklist', function () {
248 }) 225 })
249 226
250 it('Should list all the videos with another user', async function () { 227 it('Should list all the videos with another user', async function () {
251 return checkAllVideos(servers[0].url, userToken1) 228 return checkAllVideos(servers[0], userToken1)
252 }) 229 })
253 230
254 it('Should list blocked accounts', async function () { 231 it('Should list blocked accounts', async function () {
255 { 232 {
256 const res = await getAccountBlocklistByAccount(servers[0].url, servers[0].accessToken, 0, 1, 'createdAt') 233 const body = await command.listMyAccountBlocklist({ start: 0, count: 1, sort: 'createdAt' })
257 const blocks: AccountBlock[] = res.body.data 234 expect(body.total).to.equal(2)
258 235
259 expect(res.body.total).to.equal(2) 236 const block = body.data[0]
260
261 const block = blocks[0]
262 expect(block.byAccount.displayName).to.equal('root') 237 expect(block.byAccount.displayName).to.equal('root')
263 expect(block.byAccount.name).to.equal('root') 238 expect(block.byAccount.name).to.equal('root')
264 expect(block.blockedAccount.displayName).to.equal('user2') 239 expect(block.blockedAccount.displayName).to.equal('user2')
@@ -267,12 +242,10 @@ describe('Test blocklist', function () {
267 } 242 }
268 243
269 { 244 {
270 const res = await getAccountBlocklistByAccount(servers[0].url, servers[0].accessToken, 1, 2, 'createdAt') 245 const body = await command.listMyAccountBlocklist({ start: 1, count: 2, sort: 'createdAt' })
271 const blocks: AccountBlock[] = res.body.data 246 expect(body.total).to.equal(2)
272
273 expect(res.body.total).to.equal(2)
274 247
275 const block = blocks[0] 248 const block = body.data[0]
276 expect(block.byAccount.displayName).to.equal('root') 249 expect(block.byAccount.displayName).to.equal('root')
277 expect(block.byAccount.name).to.equal('root') 250 expect(block.byAccount.name).to.equal('root')
278 expect(block.blockedAccount.displayName).to.equal('user1') 251 expect(block.blockedAccount.displayName).to.equal('user1')
@@ -285,32 +258,29 @@ describe('Test blocklist', function () {
285 this.timeout(60000) 258 this.timeout(60000)
286 259
287 { 260 {
288 await addVideoCommentThread(servers[1].url, userToken2, videoUUID3, 'comment user 2') 261 await commentsCommand[1].createThread({ token: userToken2, videoId: videoUUID3, text: 'comment user 2' })
289 await waitJobs(servers) 262 await waitJobs(servers)
290 263
291 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID3, 'uploader') 264 await commentsCommand[0].createThread({ token: servers[0].accessToken, videoId: videoUUID3, text: 'uploader' })
292 await waitJobs(servers) 265 await waitJobs(servers)
293 266
294 const commentId = await findCommentId(servers[1].url, videoUUID3, 'uploader') 267 const commentId = await commentsCommand[1].findCommentId({ videoId: videoUUID3, text: 'uploader' })
295 const message = 'reply by user 2' 268 const message = 'reply by user 2'
296 const resReply = await addVideoCommentReply(servers[1].url, userToken2, videoUUID3, commentId, message) 269 const reply = await commentsCommand[1].addReply({ token: userToken2, videoId: videoUUID3, toCommentId: commentId, text: message })
297 await addVideoCommentReply(servers[1].url, servers[1].accessToken, videoUUID3, resReply.body.comment.id, 'another reply') 270 await commentsCommand[1].addReply({ videoId: videoUUID3, toCommentId: reply.id, text: 'another reply' })
298 271
299 await waitJobs(servers) 272 await waitJobs(servers)
300 } 273 }
301 274
302 // Server 2 has all the comments 275 // Server 2 has all the comments
303 { 276 {
304 const resThreads = await getVideoCommentThreads(servers[1].url, videoUUID3, 0, 25, '-createdAt') 277 const { data } = await commentsCommand[1].listThreads({ videoId: videoUUID3, count: 25, sort: '-createdAt' })
305 const threads: VideoComment[] = resThreads.body.data
306
307 expect(threads).to.have.lengthOf(2)
308 expect(threads[0].text).to.equal('uploader')
309 expect(threads[1].text).to.equal('comment user 2')
310 278
311 const resReplies = await getVideoThreadComments(servers[1].url, videoUUID3, threads[0].id) 279 expect(data).to.have.lengthOf(2)
280 expect(data[0].text).to.equal('uploader')
281 expect(data[1].text).to.equal('comment user 2')
312 282
313 const tree: VideoCommentThreadTree = resReplies.body 283 const tree = await commentsCommand[1].getThread({ videoId: videoUUID3, threadId: data[0].id })
314 expect(tree.children).to.have.lengthOf(1) 284 expect(tree.children).to.have.lengthOf(1)
315 expect(tree.children[0].comment.text).to.equal('reply by user 2') 285 expect(tree.children[0].comment.text).to.equal('reply by user 2')
316 expect(tree.children[0].children).to.have.lengthOf(1) 286 expect(tree.children[0].children).to.have.lengthOf(1)
@@ -319,55 +289,45 @@ describe('Test blocklist', function () {
319 289
320 // Server 1 and 3 should only have uploader comments 290 // Server 1 and 3 should only have uploader comments
321 for (const server of [ servers[0], servers[2] ]) { 291 for (const server of [ servers[0], servers[2] ]) {
322 const resThreads = await getVideoCommentThreads(server.url, videoUUID3, 0, 25, '-createdAt') 292 const { data } = await server.comments.listThreads({ videoId: videoUUID3, count: 25, sort: '-createdAt' })
323 const threads: VideoComment[] = resThreads.body.data
324 293
325 expect(threads).to.have.lengthOf(1) 294 expect(data).to.have.lengthOf(1)
326 expect(threads[0].text).to.equal('uploader') 295 expect(data[0].text).to.equal('uploader')
327 296
328 const resReplies = await getVideoThreadComments(server.url, videoUUID3, threads[0].id) 297 const tree = await server.comments.getThread({ videoId: videoUUID3, threadId: data[0].id })
329 298
330 const tree: VideoCommentThreadTree = resReplies.body 299 if (server.serverNumber === 1) expect(tree.children).to.have.lengthOf(0)
331 if (server.serverNumber === 1) { 300 else expect(tree.children).to.have.lengthOf(1)
332 expect(tree.children).to.have.lengthOf(0)
333 } else {
334 expect(tree.children).to.have.lengthOf(1)
335 }
336 } 301 }
337 }) 302 })
338 303
339 it('Should unblock the remote account', async function () { 304 it('Should unblock the remote account', async function () {
340 await removeAccountFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:' + servers[1].port) 305 await command.removeFromMyBlocklist({ account: 'user2@localhost:' + servers[1].port })
341 }) 306 })
342 307
343 it('Should display its videos', async function () { 308 it('Should display its videos', async function () {
344 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) 309 const { data } = await servers[0].videos.listWithToken()
345 310 expect(data).to.have.lengthOf(4)
346 const videos: Video[] = res.body.data
347 expect(videos).to.have.lengthOf(4)
348 311
349 const v = videos.find(v => v.name === 'video user 2') 312 const v = data.find(v => v.name === 'video user 2')
350 expect(v).not.to.be.undefined 313 expect(v).not.to.be.undefined
351 }) 314 })
352 315
353 it('Should display its comments on my video', async function () { 316 it('Should display its comments on my video', async function () {
354 for (const server of servers) { 317 for (const server of servers) {
355 const resThreads = await getVideoCommentThreads(server.url, videoUUID3, 0, 25, '-createdAt') 318 const { data } = await server.comments.listThreads({ videoId: videoUUID3, count: 25, sort: '-createdAt' })
356 const threads: VideoComment[] = resThreads.body.data
357 319
358 // Server 3 should not have 2 comment threads, because server 1 did not forward the server 2 comment 320 // Server 3 should not have 2 comment threads, because server 1 did not forward the server 2 comment
359 if (server.serverNumber === 3) { 321 if (server.serverNumber === 3) {
360 expect(threads).to.have.lengthOf(1) 322 expect(data).to.have.lengthOf(1)
361 continue 323 continue
362 } 324 }
363 325
364 expect(threads).to.have.lengthOf(2) 326 expect(data).to.have.lengthOf(2)
365 expect(threads[0].text).to.equal('uploader') 327 expect(data[0].text).to.equal('uploader')
366 expect(threads[1].text).to.equal('comment user 2') 328 expect(data[1].text).to.equal('comment user 2')
367 329
368 const resReplies = await getVideoThreadComments(server.url, videoUUID3, threads[0].id) 330 const tree = await server.comments.getThread({ videoId: videoUUID3, threadId: data[0].id })
369
370 const tree: VideoCommentThreadTree = resReplies.body
371 expect(tree.children).to.have.lengthOf(1) 331 expect(tree.children).to.have.lengthOf(1)
372 expect(tree.children[0].comment.text).to.equal('reply by user 2') 332 expect(tree.children[0].comment.text).to.equal('reply by user 2')
373 expect(tree.children[0].children).to.have.lengthOf(1) 333 expect(tree.children[0].children).to.have.lengthOf(1)
@@ -376,11 +336,11 @@ describe('Test blocklist', function () {
376 }) 336 })
377 337
378 it('Should unblock the local account', async function () { 338 it('Should unblock the local account', async function () {
379 await removeAccountFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'user1') 339 await command.removeFromMyBlocklist({ account: 'user1' })
380 }) 340 })
381 341
382 it('Should display its comments', function () { 342 it('Should display its comments', function () {
383 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1) 343 return checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
384 }) 344 })
385 345
386 it('Should have a notification from a non blocked account', async function () { 346 it('Should have a notification from a non blocked account', async function () {
@@ -404,46 +364,45 @@ describe('Test blocklist', function () {
404 }) 364 })
405 365
406 describe('When managing server blocklist', function () { 366 describe('When managing server blocklist', function () {
367
407 it('Should list all videos', function () { 368 it('Should list all videos', function () {
408 return checkAllVideos(servers[0].url, servers[0].accessToken) 369 return checkAllVideos(servers[0], servers[0].accessToken)
409 }) 370 })
410 371
411 it('Should list the comments', function () { 372 it('Should list the comments', function () {
412 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1) 373 return checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
413 }) 374 })
414 375
415 it('Should block a remote server', async function () { 376 it('Should block a remote server', async function () {
416 await addServerToAccountBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port) 377 await command.addToMyBlocklist({ server: 'localhost:' + servers[1].port })
417 }) 378 })
418 379
419 it('Should hide its videos', async function () { 380 it('Should hide its videos', async function () {
420 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) 381 const { data } = await servers[0].videos.listWithToken()
421 382
422 const videos: Video[] = res.body.data 383 expect(data).to.have.lengthOf(3)
423 expect(videos).to.have.lengthOf(3)
424 384
425 const v1 = videos.find(v => v.name === 'video user 2') 385 const v1 = data.find(v => v.name === 'video user 2')
426 const v2 = videos.find(v => v.name === 'video server 2') 386 const v2 = data.find(v => v.name === 'video server 2')
427 387
428 expect(v1).to.be.undefined 388 expect(v1).to.be.undefined
429 expect(v2).to.be.undefined 389 expect(v2).to.be.undefined
430 }) 390 })
431 391
432 it('Should list all the videos with another user', async function () { 392 it('Should list all the videos with another user', async function () {
433 return checkAllVideos(servers[0].url, userToken1) 393 return checkAllVideos(servers[0], userToken1)
434 }) 394 })
435 395
436 it('Should hide its comments', async function () { 396 it('Should hide its comments', async function () {
437 this.timeout(10000) 397 this.timeout(10000)
438 398
439 const resThreads = await addVideoCommentThread(servers[1].url, userToken2, videoUUID1, 'hidden comment 2') 399 const { id } = await commentsCommand[1].createThread({ token: userToken2, videoId: videoUUID1, text: 'hidden comment 2' })
440 const threadId = resThreads.body.comment.id
441 400
442 await waitJobs(servers) 401 await waitJobs(servers)
443 402
444 await checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1) 403 await checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
445 404
446 await deleteVideoComment(servers[1].url, userToken2, videoUUID1, threadId) 405 await commentsCommand[1].delete({ token: userToken2, videoId: videoUUID1, commentId: id })
447 }) 406 })
448 407
449 it('Should not have notifications from blocked server', async function () { 408 it('Should not have notifications from blocked server', async function () {
@@ -466,27 +425,25 @@ describe('Test blocklist', function () {
466 }) 425 })
467 426
468 it('Should list blocked servers', async function () { 427 it('Should list blocked servers', async function () {
469 const res = await getServerBlocklistByAccount(servers[0].url, servers[0].accessToken, 0, 1, 'createdAt') 428 const body = await command.listMyServerBlocklist({ start: 0, count: 1, sort: 'createdAt' })
470 const blocks: ServerBlock[] = res.body.data 429 expect(body.total).to.equal(1)
471
472 expect(res.body.total).to.equal(1)
473 430
474 const block = blocks[0] 431 const block = body.data[0]
475 expect(block.byAccount.displayName).to.equal('root') 432 expect(block.byAccount.displayName).to.equal('root')
476 expect(block.byAccount.name).to.equal('root') 433 expect(block.byAccount.name).to.equal('root')
477 expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port) 434 expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port)
478 }) 435 })
479 436
480 it('Should unblock the remote server', async function () { 437 it('Should unblock the remote server', async function () {
481 await removeServerFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port) 438 await command.removeFromMyBlocklist({ server: 'localhost:' + servers[1].port })
482 }) 439 })
483 440
484 it('Should display its videos', function () { 441 it('Should display its videos', function () {
485 return checkAllVideos(servers[0].url, servers[0].accessToken) 442 return checkAllVideos(servers[0], servers[0].accessToken)
486 }) 443 })
487 444
488 it('Should display its comments', function () { 445 it('Should display its comments', function () {
489 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1) 446 return checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
490 }) 447 })
491 448
492 it('Should have notification from unblocked server', async function () { 449 it('Should have notification from unblocked server', async function () {
@@ -515,54 +472,50 @@ describe('Test blocklist', function () {
515 describe('When managing account blocklist', function () { 472 describe('When managing account blocklist', function () {
516 it('Should list all videos', async function () { 473 it('Should list all videos', async function () {
517 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 474 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
518 await checkAllVideos(servers[0].url, token) 475 await checkAllVideos(servers[0], token)
519 } 476 }
520 }) 477 })
521 478
522 it('Should list the comments', async function () { 479 it('Should list the comments', async function () {
523 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 480 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
524 await checkAllComments(servers[0].url, token, videoUUID1) 481 await checkAllComments(servers[0], token, videoUUID1)
525 } 482 }
526 }) 483 })
527 484
528 it('Should block a remote account', async function () { 485 it('Should block a remote account', async function () {
529 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:' + servers[1].port) 486 await command.addToServerBlocklist({ account: 'user2@localhost:' + servers[1].port })
530 }) 487 })
531 488
532 it('Should hide its videos', async function () { 489 it('Should hide its videos', async function () {
533 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 490 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
534 const res = await getVideosListWithToken(servers[0].url, token) 491 const { data } = await servers[0].videos.listWithToken({ token })
535 492
536 const videos: Video[] = res.body.data 493 expect(data).to.have.lengthOf(4)
537 expect(videos).to.have.lengthOf(4)
538 494
539 const v = videos.find(v => v.name === 'video user 2') 495 const v = data.find(v => v.name === 'video user 2')
540 expect(v).to.be.undefined 496 expect(v).to.be.undefined
541 } 497 }
542 }) 498 })
543 499
544 it('Should block a local account', async function () { 500 it('Should block a local account', async function () {
545 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'user1') 501 await command.addToServerBlocklist({ account: 'user1' })
546 }) 502 })
547 503
548 it('Should hide its videos', async function () { 504 it('Should hide its videos', async function () {
549 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 505 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
550 const res = await getVideosListWithToken(servers[0].url, token) 506 const { data } = await servers[0].videos.listWithToken({ token })
551 507
552 const videos: Video[] = res.body.data 508 expect(data).to.have.lengthOf(3)
553 expect(videos).to.have.lengthOf(3)
554 509
555 const v = videos.find(v => v.name === 'video user 1') 510 const v = data.find(v => v.name === 'video user 1')
556 expect(v).to.be.undefined 511 expect(v).to.be.undefined
557 } 512 }
558 }) 513 })
559 514
560 it('Should hide its comments', async function () { 515 it('Should hide its comments', async function () {
561 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 516 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
562 const resThreads = await getVideoCommentThreads(servers[0].url, videoUUID1, 0, 20, '-createdAt', token) 517 const { data } = await commentsCommand[0].listThreads({ videoId: videoUUID1, count: 20, sort: '-createdAt', token })
563 518 const threads = data.filter(t => t.isDeleted === false)
564 let threads: VideoComment[] = resThreads.body.data
565 threads = threads.filter(t => t.isDeleted === false)
566 519
567 expect(threads).to.have.lengthOf(1) 520 expect(threads).to.have.lengthOf(1)
568 expect(threads[0].totalReplies).to.equal(1) 521 expect(threads[0].totalReplies).to.equal(1)
@@ -571,9 +524,7 @@ describe('Test blocklist', function () {
571 expect(t).to.be.undefined 524 expect(t).to.be.undefined
572 525
573 for (const thread of threads) { 526 for (const thread of threads) {
574 const res = await getVideoThreadComments(servers[0].url, videoUUID1, thread.id, token) 527 const tree = await commentsCommand[0].getThread({ videoId: videoUUID1, threadId: thread.id, token })
575
576 const tree: VideoCommentThreadTree = res.body
577 expect(tree.children).to.have.lengthOf(0) 528 expect(tree.children).to.have.lengthOf(0)
578 } 529 }
579 } 530 }
@@ -600,12 +551,10 @@ describe('Test blocklist', function () {
600 551
601 it('Should list blocked accounts', async function () { 552 it('Should list blocked accounts', async function () {
602 { 553 {
603 const res = await getAccountBlocklistByServer(servers[0].url, servers[0].accessToken, 0, 1, 'createdAt') 554 const body = await command.listServerAccountBlocklist({ start: 0, count: 1, sort: 'createdAt' })
604 const blocks: AccountBlock[] = res.body.data 555 expect(body.total).to.equal(2)
605
606 expect(res.body.total).to.equal(2)
607 556
608 const block = blocks[0] 557 const block = body.data[0]
609 expect(block.byAccount.displayName).to.equal('peertube') 558 expect(block.byAccount.displayName).to.equal('peertube')
610 expect(block.byAccount.name).to.equal('peertube') 559 expect(block.byAccount.name).to.equal('peertube')
611 expect(block.blockedAccount.displayName).to.equal('user2') 560 expect(block.blockedAccount.displayName).to.equal('user2')
@@ -614,12 +563,10 @@ describe('Test blocklist', function () {
614 } 563 }
615 564
616 { 565 {
617 const res = await getAccountBlocklistByServer(servers[0].url, servers[0].accessToken, 1, 2, 'createdAt') 566 const body = await command.listServerAccountBlocklist({ start: 1, count: 2, sort: 'createdAt' })
618 const blocks: AccountBlock[] = res.body.data 567 expect(body.total).to.equal(2)
619 568
620 expect(res.body.total).to.equal(2) 569 const block = body.data[0]
621
622 const block = blocks[0]
623 expect(block.byAccount.displayName).to.equal('peertube') 570 expect(block.byAccount.displayName).to.equal('peertube')
624 expect(block.byAccount.name).to.equal('peertube') 571 expect(block.byAccount.name).to.equal('peertube')
625 expect(block.blockedAccount.displayName).to.equal('user1') 572 expect(block.blockedAccount.displayName).to.equal('user1')
@@ -629,28 +576,26 @@ describe('Test blocklist', function () {
629 }) 576 })
630 577
631 it('Should unblock the remote account', async function () { 578 it('Should unblock the remote account', async function () {
632 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:' + servers[1].port) 579 await command.removeFromServerBlocklist({ account: 'user2@localhost:' + servers[1].port })
633 }) 580 })
634 581
635 it('Should display its videos', async function () { 582 it('Should display its videos', async function () {
636 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 583 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
637 const res = await getVideosListWithToken(servers[0].url, token) 584 const { data } = await servers[0].videos.listWithToken({ token })
638 585 expect(data).to.have.lengthOf(4)
639 const videos: Video[] = res.body.data
640 expect(videos).to.have.lengthOf(4)
641 586
642 const v = videos.find(v => v.name === 'video user 2') 587 const v = data.find(v => v.name === 'video user 2')
643 expect(v).not.to.be.undefined 588 expect(v).not.to.be.undefined
644 } 589 }
645 }) 590 })
646 591
647 it('Should unblock the local account', async function () { 592 it('Should unblock the local account', async function () {
648 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'user1') 593 await command.removeFromServerBlocklist({ account: 'user1' })
649 }) 594 })
650 595
651 it('Should display its comments', async function () { 596 it('Should display its comments', async function () {
652 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 597 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
653 await checkAllComments(servers[0].url, token, videoUUID1) 598 await checkAllComments(servers[0], token, videoUUID1)
654 } 599 }
655 }) 600 })
656 601
@@ -677,31 +622,33 @@ describe('Test blocklist', function () {
677 describe('When managing server blocklist', function () { 622 describe('When managing server blocklist', function () {
678 it('Should list all videos', async function () { 623 it('Should list all videos', async function () {
679 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 624 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
680 await checkAllVideos(servers[0].url, token) 625 await checkAllVideos(servers[0], token)
681 } 626 }
682 }) 627 })
683 628
684 it('Should list the comments', async function () { 629 it('Should list the comments', async function () {
685 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 630 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
686 await checkAllComments(servers[0].url, token, videoUUID1) 631 await checkAllComments(servers[0], token, videoUUID1)
687 } 632 }
688 }) 633 })
689 634
690 it('Should block a remote server', async function () { 635 it('Should block a remote server', async function () {
691 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port) 636 await command.addToServerBlocklist({ server: 'localhost:' + servers[1].port })
692 }) 637 })
693 638
694 it('Should hide its videos', async function () { 639 it('Should hide its videos', async function () {
695 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 640 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
696 const res1 = await getVideosList(servers[0].url) 641 const requests = [
697 const res2 = await getVideosListWithToken(servers[0].url, token) 642 servers[0].videos.list(),
643 servers[0].videos.listWithToken({ token })
644 ]
698 645
699 for (const res of [ res1, res2 ]) { 646 for (const req of requests) {
700 const videos: Video[] = res.body.data 647 const { data } = await req
701 expect(videos).to.have.lengthOf(3) 648 expect(data).to.have.lengthOf(3)
702 649
703 const v1 = videos.find(v => v.name === 'video user 2') 650 const v1 = data.find(v => v.name === 'video user 2')
704 const v2 = videos.find(v => v.name === 'video server 2') 651 const v2 = data.find(v => v.name === 'video server 2')
705 652
706 expect(v1).to.be.undefined 653 expect(v1).to.be.undefined
707 expect(v2).to.be.undefined 654 expect(v2).to.be.undefined
@@ -712,14 +659,13 @@ describe('Test blocklist', function () {
712 it('Should hide its comments', async function () { 659 it('Should hide its comments', async function () {
713 this.timeout(10000) 660 this.timeout(10000)
714 661
715 const resThreads = await addVideoCommentThread(servers[1].url, userToken2, videoUUID1, 'hidden comment 2') 662 const { id } = await commentsCommand[1].createThread({ token: userToken2, videoId: videoUUID1, text: 'hidden comment 2' })
716 const threadId = resThreads.body.comment.id
717 663
718 await waitJobs(servers) 664 await waitJobs(servers)
719 665
720 await checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1) 666 await checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
721 667
722 await deleteVideoComment(servers[1].url, userToken2, videoUUID1, threadId) 668 await commentsCommand[1].delete({ token: userToken2, videoId: videoUUID1, commentId: id })
723 }) 669 })
724 670
725 it('Should not have notification from blocked instances by instance', async function () { 671 it('Should not have notification from blocked instances by instance', async function () {
@@ -742,48 +688,44 @@ describe('Test blocklist', function () {
742 688
743 { 689 {
744 const now = new Date() 690 const now = new Date()
745 await unfollow(servers[1].url, servers[1].accessToken, servers[0]) 691 await servers[1].follows.unfollow({ target: servers[0] })
746 await waitJobs(servers) 692 await waitJobs(servers)
747 await follow(servers[1].url, [ servers[0].host ], servers[1].accessToken) 693 await servers[1].follows.follow({ hosts: [ servers[0].host ] })
748 694
749 await waitJobs(servers) 695 await waitJobs(servers)
750 696
751 const res = await getUserNotifications(servers[0].url, servers[0].accessToken, 0, 30) 697 const { data } = await servers[0].notifications.list({ start: 0, count: 30 })
752 const commentNotifications = (res.body.data as UserNotification[]) 698 const commentNotifications = data.filter(n => {
753 .filter(n => { 699 return n.type === UserNotificationType.NEW_INSTANCE_FOLLOWER && n.createdAt >= now.toISOString()
754 return n.type === UserNotificationType.NEW_INSTANCE_FOLLOWER && 700 })
755 n.createdAt >= now.toISOString()
756 })
757 701
758 expect(commentNotifications).to.have.lengthOf(0) 702 expect(commentNotifications).to.have.lengthOf(0)
759 } 703 }
760 }) 704 })
761 705
762 it('Should list blocked servers', async function () { 706 it('Should list blocked servers', async function () {
763 const res = await getServerBlocklistByServer(servers[0].url, servers[0].accessToken, 0, 1, 'createdAt') 707 const body = await command.listServerServerBlocklist({ start: 0, count: 1, sort: 'createdAt' })
764 const blocks: ServerBlock[] = res.body.data 708 expect(body.total).to.equal(1)
765
766 expect(res.body.total).to.equal(1)
767 709
768 const block = blocks[0] 710 const block = body.data[0]
769 expect(block.byAccount.displayName).to.equal('peertube') 711 expect(block.byAccount.displayName).to.equal('peertube')
770 expect(block.byAccount.name).to.equal('peertube') 712 expect(block.byAccount.name).to.equal('peertube')
771 expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port) 713 expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port)
772 }) 714 })
773 715
774 it('Should unblock the remote server', async function () { 716 it('Should unblock the remote server', async function () {
775 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port) 717 await command.removeFromServerBlocklist({ server: 'localhost:' + servers[1].port })
776 }) 718 })
777 719
778 it('Should list all videos', async function () { 720 it('Should list all videos', async function () {
779 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 721 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
780 await checkAllVideos(servers[0].url, token) 722 await checkAllVideos(servers[0], token)
781 } 723 }
782 }) 724 })
783 725
784 it('Should list the comments', async function () { 726 it('Should list the comments', async function () {
785 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 727 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
786 await checkAllComments(servers[0].url, token, videoUUID1) 728 await checkAllComments(servers[0], token, videoUUID1)
787 } 729 }
788 }) 730 })
789 731
@@ -807,18 +749,16 @@ describe('Test blocklist', function () {
807 749
808 { 750 {
809 const now = new Date() 751 const now = new Date()
810 await unfollow(servers[1].url, servers[1].accessToken, servers[0]) 752 await servers[1].follows.unfollow({ target: servers[0] })
811 await waitJobs(servers) 753 await waitJobs(servers)
812 await follow(servers[1].url, [ servers[0].host ], servers[1].accessToken) 754 await servers[1].follows.follow({ hosts: [ servers[0].host ] })
813 755
814 await waitJobs(servers) 756 await waitJobs(servers)
815 757
816 const res = await getUserNotifications(servers[0].url, servers[0].accessToken, 0, 30) 758 const { data } = await servers[0].notifications.list({ start: 0, count: 30 })
817 const commentNotifications = (res.body.data as UserNotification[]) 759 const commentNotifications = data.filter(n => {
818 .filter(n => { 760 return n.type === UserNotificationType.NEW_INSTANCE_FOLLOWER && n.createdAt >= now.toISOString()
819 return n.type === UserNotificationType.NEW_INSTANCE_FOLLOWER && 761 })
820 n.createdAt >= now.toISOString()
821 })
822 762
823 expect(commentNotifications).to.have.lengthOf(1) 763 expect(commentNotifications).to.have.lengthOf(1)
824 } 764 }
diff --git a/server/tests/api/moderation/video-blacklist.ts b/server/tests/api/moderation/video-blacklist.ts
index 52cac20d9..d5838191a 100644
--- a/server/tests/api/moderation/video-blacklist.ts
+++ b/server/tests/api/moderation/video-blacklist.ts
@@ -4,44 +4,30 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { orderBy } from 'lodash' 5import { orderBy } from 'lodash'
6import { 6import {
7 addVideoToBlacklist, 7 BlacklistCommand,
8 cleanupTests, 8 cleanupTests,
9 createUser, 9 createMultipleServers,
10 flushAndRunMultipleServers, 10 doubleFollow,
11 getBlacklistedVideosList, 11 FIXTURE_URLS,
12 getMyUserInformation,
13 getMyVideos,
14 getVideosList,
15 killallServers, 12 killallServers,
16 removeVideoFromBlacklist, 13 PeerTubeServer,
17 reRunServer,
18 searchVideo,
19 ServerInfo,
20 setAccessTokensToServers, 14 setAccessTokensToServers,
21 updateVideo, 15 waitJobs
22 updateVideoBlacklist, 16} from '@shared/extra-utils'
23 uploadVideo, 17import { UserAdminFlag, UserRole, VideoBlacklist, VideoBlacklistType } from '@shared/models'
24 userLogin
25} from '../../../../shared/extra-utils/index'
26import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
27import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
28import { getGoodVideoUrl, getMagnetURI, importVideo } from '../../../../shared/extra-utils/videos/video-imports'
29import { User, UserRole } from '../../../../shared/models/users'
30import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
31import { VideoBlacklist, VideoBlacklistType } from '../../../../shared/models/videos'
32 18
33const expect = chai.expect 19const expect = chai.expect
34 20
35describe('Test video blacklist', function () { 21describe('Test video blacklist', function () {
36 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
37 let videoId: number 23 let videoId: number
24 let command: BlacklistCommand
38 25
39 async function blacklistVideosOnServer (server: ServerInfo) { 26 async function blacklistVideosOnServer (server: PeerTubeServer) {
40 const res = await getVideosList(server.url) 27 const { data } = await server.videos.list()
41 28
42 const videos = res.body.data 29 for (const video of data) {
43 for (const video of videos) { 30 await server.blacklist.add({ videoId: video.id, reason: 'super reason' })
44 await addVideoToBlacklist(server.url, server.accessToken, video.id, 'super reason')
45 } 31 }
46 } 32 }
47 33
@@ -49,7 +35,7 @@ describe('Test video blacklist', function () {
49 this.timeout(50000) 35 this.timeout(50000)
50 36
51 // Run servers 37 // Run servers
52 servers = await flushAndRunMultipleServers(2) 38 servers = await createMultipleServers(2)
53 39
54 // Get the access tokens 40 // Get the access tokens
55 await setAccessTokensToServers(servers) 41 await setAccessTokensToServers(servers)
@@ -58,12 +44,14 @@ describe('Test video blacklist', function () {
58 await doubleFollow(servers[0], servers[1]) 44 await doubleFollow(servers[0], servers[1])
59 45
60 // Upload 2 videos on server 2 46 // Upload 2 videos on server 2
61 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 1st video', description: 'A video on server 2' }) 47 await servers[1].videos.upload({ attributes: { name: 'My 1st video', description: 'A video on server 2' } })
62 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 2nd video', description: 'A video on server 2' }) 48 await servers[1].videos.upload({ attributes: { name: 'My 2nd video', description: 'A video on server 2' } })
63 49
64 // Wait videos propagation, server 2 has transcoding enabled 50 // Wait videos propagation, server 2 has transcoding enabled
65 await waitJobs(servers) 51 await waitJobs(servers)
66 52
53 command = servers[0].blacklist
54
67 // Blacklist the two videos on server 1 55 // Blacklist the two videos on server 1
68 await blacklistVideosOnServer(servers[0]) 56 await blacklistVideosOnServer(servers[0])
69 }) 57 })
@@ -72,48 +60,47 @@ describe('Test video blacklist', function () {
72 60
73 it('Should not have the video blacklisted in videos list/search on server 1', async function () { 61 it('Should not have the video blacklisted in videos list/search on server 1', async function () {
74 { 62 {
75 const res = await getVideosList(servers[0].url) 63 const { total, data } = await servers[0].videos.list()
76 64
77 expect(res.body.total).to.equal(0) 65 expect(total).to.equal(0)
78 expect(res.body.data).to.be.an('array') 66 expect(data).to.be.an('array')
79 expect(res.body.data.length).to.equal(0) 67 expect(data.length).to.equal(0)
80 } 68 }
81 69
82 { 70 {
83 const res = await searchVideo(servers[0].url, 'name') 71 const body = await servers[0].search.searchVideos({ search: 'video' })
84 72
85 expect(res.body.total).to.equal(0) 73 expect(body.total).to.equal(0)
86 expect(res.body.data).to.be.an('array') 74 expect(body.data).to.be.an('array')
87 expect(res.body.data.length).to.equal(0) 75 expect(body.data.length).to.equal(0)
88 } 76 }
89 }) 77 })
90 78
91 it('Should have the blacklisted video in videos list/search on server 2', async function () { 79 it('Should have the blacklisted video in videos list/search on server 2', async function () {
92 { 80 {
93 const res = await getVideosList(servers[1].url) 81 const { total, data } = await servers[1].videos.list()
94 82
95 expect(res.body.total).to.equal(2) 83 expect(total).to.equal(2)
96 expect(res.body.data).to.be.an('array') 84 expect(data).to.be.an('array')
97 expect(res.body.data.length).to.equal(2) 85 expect(data.length).to.equal(2)
98 } 86 }
99 87
100 { 88 {
101 const res = await searchVideo(servers[1].url, 'video') 89 const body = await servers[1].search.searchVideos({ search: 'video' })
102 90
103 expect(res.body.total).to.equal(2) 91 expect(body.total).to.equal(2)
104 expect(res.body.data).to.be.an('array') 92 expect(body.data).to.be.an('array')
105 expect(res.body.data.length).to.equal(2) 93 expect(body.data.length).to.equal(2)
106 } 94 }
107 }) 95 })
108 }) 96 })
109 97
110 describe('When listing manually blacklisted videos', function () { 98 describe('When listing manually blacklisted videos', function () {
111 it('Should display all the blacklisted videos', async function () { 99 it('Should display all the blacklisted videos', async function () {
112 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken }) 100 const body = await command.list()
113 101 expect(body.total).to.equal(2)
114 expect(res.body.total).to.equal(2)
115 102
116 const blacklistedVideos = res.body.data 103 const blacklistedVideos = body.data
117 expect(blacklistedVideos).to.be.an('array') 104 expect(blacklistedVideos).to.be.an('array')
118 expect(blacklistedVideos.length).to.equal(2) 105 expect(blacklistedVideos.length).to.equal(2)
119 106
@@ -124,79 +111,66 @@ describe('Test video blacklist', function () {
124 }) 111 })
125 112
126 it('Should display all the blacklisted videos when applying manual type filter', async function () { 113 it('Should display all the blacklisted videos when applying manual type filter', async function () {
127 const res = await getBlacklistedVideosList({ 114 const body = await command.list({ type: VideoBlacklistType.MANUAL })
128 url: servers[0].url, 115 expect(body.total).to.equal(2)
129 token: servers[0].accessToken,
130 type: VideoBlacklistType.MANUAL
131 })
132 116
133 expect(res.body.total).to.equal(2) 117 const blacklistedVideos = body.data
134
135 const blacklistedVideos = res.body.data
136 expect(blacklistedVideos).to.be.an('array') 118 expect(blacklistedVideos).to.be.an('array')
137 expect(blacklistedVideos.length).to.equal(2) 119 expect(blacklistedVideos.length).to.equal(2)
138 }) 120 })
139 121
140 it('Should display nothing when applying automatic type filter', async function () { 122 it('Should display nothing when applying automatic type filter', async function () {
141 const res = await getBlacklistedVideosList({ 123 const body = await command.list({ type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED })
142 url: servers[0].url, 124 expect(body.total).to.equal(0)
143 token: servers[0].accessToken,
144 type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
145 })
146
147 expect(res.body.total).to.equal(0)
148 125
149 const blacklistedVideos = res.body.data 126 const blacklistedVideos = body.data
150 expect(blacklistedVideos).to.be.an('array') 127 expect(blacklistedVideos).to.be.an('array')
151 expect(blacklistedVideos.length).to.equal(0) 128 expect(blacklistedVideos.length).to.equal(0)
152 }) 129 })
153 130
154 it('Should get the correct sort when sorting by descending id', async function () { 131 it('Should get the correct sort when sorting by descending id', async function () {
155 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: '-id' }) 132 const body = await command.list({ sort: '-id' })
156 expect(res.body.total).to.equal(2) 133 expect(body.total).to.equal(2)
157 134
158 const blacklistedVideos = res.body.data 135 const blacklistedVideos = body.data
159 expect(blacklistedVideos).to.be.an('array') 136 expect(blacklistedVideos).to.be.an('array')
160 expect(blacklistedVideos.length).to.equal(2) 137 expect(blacklistedVideos.length).to.equal(2)
161 138
162 const result = orderBy(res.body.data, [ 'id' ], [ 'desc' ]) 139 const result = orderBy(body.data, [ 'id' ], [ 'desc' ])
163
164 expect(blacklistedVideos).to.deep.equal(result) 140 expect(blacklistedVideos).to.deep.equal(result)
165 }) 141 })
166 142
167 it('Should get the correct sort when sorting by descending video name', async function () { 143 it('Should get the correct sort when sorting by descending video name', async function () {
168 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: '-name' }) 144 const body = await command.list({ sort: '-name' })
169 expect(res.body.total).to.equal(2) 145 expect(body.total).to.equal(2)
170 146
171 const blacklistedVideos = res.body.data 147 const blacklistedVideos = body.data
172 expect(blacklistedVideos).to.be.an('array') 148 expect(blacklistedVideos).to.be.an('array')
173 expect(blacklistedVideos.length).to.equal(2) 149 expect(blacklistedVideos.length).to.equal(2)
174 150
175 const result = orderBy(res.body.data, [ 'name' ], [ 'desc' ]) 151 const result = orderBy(body.data, [ 'name' ], [ 'desc' ])
176
177 expect(blacklistedVideos).to.deep.equal(result) 152 expect(blacklistedVideos).to.deep.equal(result)
178 }) 153 })
179 154
180 it('Should get the correct sort when sorting by ascending creation date', async function () { 155 it('Should get the correct sort when sorting by ascending creation date', async function () {
181 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: 'createdAt' }) 156 const body = await command.list({ sort: 'createdAt' })
182 expect(res.body.total).to.equal(2) 157 expect(body.total).to.equal(2)
183 158
184 const blacklistedVideos = res.body.data 159 const blacklistedVideos = body.data
185 expect(blacklistedVideos).to.be.an('array') 160 expect(blacklistedVideos).to.be.an('array')
186 expect(blacklistedVideos.length).to.equal(2) 161 expect(blacklistedVideos.length).to.equal(2)
187 162
188 const result = orderBy(res.body.data, [ 'createdAt' ]) 163 const result = orderBy(body.data, [ 'createdAt' ])
189
190 expect(blacklistedVideos).to.deep.equal(result) 164 expect(blacklistedVideos).to.deep.equal(result)
191 }) 165 })
192 }) 166 })
193 167
194 describe('When updating blacklisted videos', function () { 168 describe('When updating blacklisted videos', function () {
195 it('Should change the reason', async function () { 169 it('Should change the reason', async function () {
196 await updateVideoBlacklist(servers[0].url, servers[0].accessToken, videoId, 'my super reason updated') 170 await command.update({ videoId, reason: 'my super reason updated' })
197 171
198 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: '-name' }) 172 const body = await command.list({ sort: '-name' })
199 const video = res.body.data.find(b => b.video.id === videoId) 173 const video = body.data.find(b => b.video.id === videoId)
200 174
201 expect(video.reason).to.equal('my super reason updated') 175 expect(video.reason).to.equal('my super reason updated')
202 }) 176 })
@@ -206,12 +180,12 @@ describe('Test video blacklist', function () {
206 it('Should display blacklisted videos', async function () { 180 it('Should display blacklisted videos', async function () {
207 await blacklistVideosOnServer(servers[1]) 181 await blacklistVideosOnServer(servers[1])
208 182
209 const res = await getMyVideos(servers[1].url, servers[1].accessToken, 0, 5) 183 const { total, data } = await servers[1].videos.listMyVideos()
210 184
211 expect(res.body.total).to.equal(2) 185 expect(total).to.equal(2)
212 expect(res.body.data).to.have.lengthOf(2) 186 expect(data).to.have.lengthOf(2)
213 187
214 for (const video of res.body.data) { 188 for (const video of data) {
215 expect(video.blacklisted).to.be.true 189 expect(video.blacklisted).to.be.true
216 expect(video.blacklistedReason).to.equal('super reason') 190 expect(video.blacklistedReason).to.equal('super reason')
217 } 191 }
@@ -223,39 +197,38 @@ describe('Test video blacklist', function () {
223 let blacklist = [] 197 let blacklist = []
224 198
225 it('Should not have any video in videos list on server 1', async function () { 199 it('Should not have any video in videos list on server 1', async function () {
226 const res = await getVideosList(servers[0].url) 200 const { total, data } = await servers[0].videos.list()
227 expect(res.body.total).to.equal(0) 201 expect(total).to.equal(0)
228 expect(res.body.data).to.be.an('array') 202 expect(data).to.be.an('array')
229 expect(res.body.data.length).to.equal(0) 203 expect(data.length).to.equal(0)
230 }) 204 })
231 205
232 it('Should remove a video from the blacklist on server 1', async function () { 206 it('Should remove a video from the blacklist on server 1', async function () {
233 // Get one video in the blacklist 207 // Get one video in the blacklist
234 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: '-name' }) 208 const body = await command.list({ sort: '-name' })
235 videoToRemove = res.body.data[0] 209 videoToRemove = body.data[0]
236 blacklist = res.body.data.slice(1) 210 blacklist = body.data.slice(1)
237 211
238 // Remove it 212 // Remove it
239 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, videoToRemove.video.id) 213 await command.remove({ videoId: videoToRemove.video.id })
240 }) 214 })
241 215
242 it('Should have the ex-blacklisted video in videos list on server 1', async function () { 216 it('Should have the ex-blacklisted video in videos list on server 1', async function () {
243 const res = await getVideosList(servers[0].url) 217 const { total, data } = await servers[0].videos.list()
244 expect(res.body.total).to.equal(1) 218 expect(total).to.equal(1)
245 219
246 const videos = res.body.data 220 expect(data).to.be.an('array')
247 expect(videos).to.be.an('array') 221 expect(data.length).to.equal(1)
248 expect(videos.length).to.equal(1)
249 222
250 expect(videos[0].name).to.equal(videoToRemove.video.name) 223 expect(data[0].name).to.equal(videoToRemove.video.name)
251 expect(videos[0].id).to.equal(videoToRemove.video.id) 224 expect(data[0].id).to.equal(videoToRemove.video.id)
252 }) 225 })
253 226
254 it('Should not have the ex-blacklisted video in videos blacklist list on server 1', async function () { 227 it('Should not have the ex-blacklisted video in videos blacklist list on server 1', async function () {
255 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: '-name' }) 228 const body = await command.list({ sort: '-name' })
256 expect(res.body.total).to.equal(1) 229 expect(body.total).to.equal(1)
257 230
258 const videos = res.body.data 231 const videos = body.data
259 expect(videos).to.be.an('array') 232 expect(videos).to.be.an('array')
260 expect(videos.length).to.equal(1) 233 expect(videos.length).to.equal(1)
261 expect(videos).to.deep.equal(blacklist) 234 expect(videos).to.deep.equal(blacklist)
@@ -270,12 +243,12 @@ describe('Test video blacklist', function () {
270 this.timeout(10000) 243 this.timeout(10000)
271 244
272 { 245 {
273 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'Video 3' }) 246 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'Video 3' } })
274 video3UUID = res.body.video.uuid 247 video3UUID = uuid
275 } 248 }
276 { 249 {
277 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'Video 4' }) 250 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'Video 4' } })
278 video4UUID = res.body.video.uuid 251 video4UUID = uuid
279 } 252 }
280 253
281 await waitJobs(servers) 254 await waitJobs(servers)
@@ -284,51 +257,51 @@ describe('Test video blacklist', function () {
284 it('Should blacklist video 3 and keep it federated', async function () { 257 it('Should blacklist video 3 and keep it federated', async function () {
285 this.timeout(10000) 258 this.timeout(10000)
286 259
287 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, video3UUID, 'super reason', false) 260 await command.add({ videoId: video3UUID, reason: 'super reason', unfederate: false })
288 261
289 await waitJobs(servers) 262 await waitJobs(servers)
290 263
291 { 264 {
292 const res = await getVideosList(servers[0].url) 265 const { data } = await servers[0].videos.list()
293 expect(res.body.data.find(v => v.uuid === video3UUID)).to.be.undefined 266 expect(data.find(v => v.uuid === video3UUID)).to.be.undefined
294 } 267 }
295 268
296 { 269 {
297 const res = await getVideosList(servers[1].url) 270 const { data } = await servers[1].videos.list()
298 expect(res.body.data.find(v => v.uuid === video3UUID)).to.not.be.undefined 271 expect(data.find(v => v.uuid === video3UUID)).to.not.be.undefined
299 } 272 }
300 }) 273 })
301 274
302 it('Should unfederate the video', async function () { 275 it('Should unfederate the video', async function () {
303 this.timeout(10000) 276 this.timeout(10000)
304 277
305 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, video4UUID, 'super reason', true) 278 await command.add({ videoId: video4UUID, reason: 'super reason', unfederate: true })
306 279
307 await waitJobs(servers) 280 await waitJobs(servers)
308 281
309 for (const server of servers) { 282 for (const server of servers) {
310 const res = await getVideosList(server.url) 283 const { data } = await server.videos.list()
311 expect(res.body.data.find(v => v.uuid === video4UUID)).to.be.undefined 284 expect(data.find(v => v.uuid === video4UUID)).to.be.undefined
312 } 285 }
313 }) 286 })
314 287
315 it('Should have the video unfederated even after an Update AP message', async function () { 288 it('Should have the video unfederated even after an Update AP message', async function () {
316 this.timeout(10000) 289 this.timeout(10000)
317 290
318 await updateVideo(servers[0].url, servers[0].accessToken, video4UUID, { description: 'super description' }) 291 await servers[0].videos.update({ id: video4UUID, attributes: { description: 'super description' } })
319 292
320 await waitJobs(servers) 293 await waitJobs(servers)
321 294
322 for (const server of servers) { 295 for (const server of servers) {
323 const res = await getVideosList(server.url) 296 const { data } = await server.videos.list()
324 expect(res.body.data.find(v => v.uuid === video4UUID)).to.be.undefined 297 expect(data.find(v => v.uuid === video4UUID)).to.be.undefined
325 } 298 }
326 }) 299 })
327 300
328 it('Should have the correct video blacklist unfederate attribute', async function () { 301 it('Should have the correct video blacklist unfederate attribute', async function () {
329 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: 'createdAt' }) 302 const body = await command.list({ sort: 'createdAt' })
330 303
331 const blacklistedVideos: VideoBlacklist[] = res.body.data 304 const blacklistedVideos = body.data
332 const video3Blacklisted = blacklistedVideos.find(b => b.video.uuid === video3UUID) 305 const video3Blacklisted = blacklistedVideos.find(b => b.video.uuid === video3UUID)
333 const video4Blacklisted = blacklistedVideos.find(b => b.video.uuid === video4UUID) 306 const video4Blacklisted = blacklistedVideos.find(b => b.video.uuid === video4UUID)
334 307
@@ -339,13 +312,13 @@ describe('Test video blacklist', function () {
339 it('Should remove the video from blacklist and refederate the video', async function () { 312 it('Should remove the video from blacklist and refederate the video', async function () {
340 this.timeout(10000) 313 this.timeout(10000)
341 314
342 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, video4UUID) 315 await command.remove({ videoId: video4UUID })
343 316
344 await waitJobs(servers) 317 await waitJobs(servers)
345 318
346 for (const server of servers) { 319 for (const server of servers) {
347 const res = await getVideosList(server.url) 320 const { data } = await server.videos.list()
348 expect(res.body.data.find(v => v.uuid === video4UUID)).to.not.be.undefined 321 expect(data.find(v => v.uuid === video4UUID)).to.not.be.undefined
349 } 322 }
350 }) 323 })
351 324
@@ -359,7 +332,7 @@ describe('Test video blacklist', function () {
359 before(async function () { 332 before(async function () {
360 this.timeout(20000) 333 this.timeout(20000)
361 334
362 killallServers([ servers[0] ]) 335 await killallServers([ servers[0] ])
363 336
364 const config = { 337 const config = {
365 auto_blacklist: { 338 auto_blacklist: {
@@ -370,106 +343,79 @@ describe('Test video blacklist', function () {
370 } 343 }
371 } 344 }
372 } 345 }
373 await reRunServer(servers[0], config) 346 await servers[0].run(config)
374 347
375 { 348 {
376 const user = { username: 'user_without_flag', password: 'password' } 349 const user = { username: 'user_without_flag', password: 'password' }
377 await createUser({ 350 await servers[0].users.create({
378 url: servers[0].url,
379 accessToken: servers[0].accessToken,
380 username: user.username, 351 username: user.username,
381 adminFlags: UserAdminFlag.NONE, 352 adminFlags: UserAdminFlag.NONE,
382 password: user.password, 353 password: user.password,
383 role: UserRole.USER 354 role: UserRole.USER
384 }) 355 })
385 356
386 userWithoutFlag = await userLogin(servers[0], user) 357 userWithoutFlag = await servers[0].login.getAccessToken(user)
387 358
388 const res = await getMyUserInformation(servers[0].url, userWithoutFlag) 359 const { videoChannels } = await servers[0].users.getMyInfo({ token: userWithoutFlag })
389 const body: User = res.body 360 channelOfUserWithoutFlag = videoChannels[0].id
390 channelOfUserWithoutFlag = body.videoChannels[0].id
391 } 361 }
392 362
393 { 363 {
394 const user = { username: 'user_with_flag', password: 'password' } 364 const user = { username: 'user_with_flag', password: 'password' }
395 await createUser({ 365 await servers[0].users.create({
396 url: servers[0].url,
397 accessToken: servers[0].accessToken,
398 username: user.username, 366 username: user.username,
399 adminFlags: UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST, 367 adminFlags: UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST,
400 password: user.password, 368 password: user.password,
401 role: UserRole.USER 369 role: UserRole.USER
402 }) 370 })
403 371
404 userWithFlag = await userLogin(servers[0], user) 372 userWithFlag = await servers[0].login.getAccessToken(user)
405 } 373 }
406 374
407 await waitJobs(servers) 375 await waitJobs(servers)
408 }) 376 })
409 377
410 it('Should auto blacklist a video on upload', async function () { 378 it('Should auto blacklist a video on upload', async function () {
411 await uploadVideo(servers[0].url, userWithoutFlag, { name: 'blacklisted' }) 379 await servers[0].videos.upload({ token: userWithoutFlag, attributes: { name: 'blacklisted' } })
412
413 const res = await getBlacklistedVideosList({
414 url: servers[0].url,
415 token: servers[0].accessToken,
416 type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
417 })
418 380
419 expect(res.body.total).to.equal(1) 381 const body = await command.list({ type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED })
420 expect(res.body.data[0].video.name).to.equal('blacklisted') 382 expect(body.total).to.equal(1)
383 expect(body.data[0].video.name).to.equal('blacklisted')
421 }) 384 })
422 385
423 it('Should auto blacklist a video on URL import', async function () { 386 it('Should auto blacklist a video on URL import', async function () {
424 this.timeout(15000) 387 this.timeout(15000)
425 388
426 const attributes = { 389 const attributes = {
427 targetUrl: getGoodVideoUrl(), 390 targetUrl: FIXTURE_URLS.goodVideo,
428 name: 'URL import', 391 name: 'URL import',
429 channelId: channelOfUserWithoutFlag 392 channelId: channelOfUserWithoutFlag
430 } 393 }
431 await importVideo(servers[0].url, userWithoutFlag, attributes) 394 await servers[0].imports.importVideo({ token: userWithoutFlag, attributes })
432 395
433 const res = await getBlacklistedVideosList({ 396 const body = await command.list({ sort: 'createdAt', type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED })
434 url: servers[0].url, 397 expect(body.total).to.equal(2)
435 token: servers[0].accessToken, 398 expect(body.data[1].video.name).to.equal('URL import')
436 sort: 'createdAt',
437 type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
438 })
439
440 expect(res.body.total).to.equal(2)
441 expect(res.body.data[1].video.name).to.equal('URL import')
442 }) 399 })
443 400
444 it('Should auto blacklist a video on torrent import', async function () { 401 it('Should auto blacklist a video on torrent import', async function () {
445 const attributes = { 402 const attributes = {
446 magnetUri: getMagnetURI(), 403 magnetUri: FIXTURE_URLS.magnet,
447 name: 'Torrent import', 404 name: 'Torrent import',
448 channelId: channelOfUserWithoutFlag 405 channelId: channelOfUserWithoutFlag
449 } 406 }
450 await importVideo(servers[0].url, userWithoutFlag, attributes) 407 await servers[0].imports.importVideo({ token: userWithoutFlag, attributes })
451
452 const res = await getBlacklistedVideosList({
453 url: servers[0].url,
454 token: servers[0].accessToken,
455 sort: 'createdAt',
456 type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
457 })
458 408
459 expect(res.body.total).to.equal(3) 409 const body = await command.list({ sort: 'createdAt', type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED })
460 expect(res.body.data[2].video.name).to.equal('Torrent import') 410 expect(body.total).to.equal(3)
411 expect(body.data[2].video.name).to.equal('Torrent import')
461 }) 412 })
462 413
463 it('Should not auto blacklist a video on upload if the user has the bypass blacklist flag', async function () { 414 it('Should not auto blacklist a video on upload if the user has the bypass blacklist flag', async function () {
464 await uploadVideo(servers[0].url, userWithFlag, { name: 'not blacklisted' }) 415 await servers[0].videos.upload({ token: userWithFlag, attributes: { name: 'not blacklisted' } })
465
466 const res = await getBlacklistedVideosList({
467 url: servers[0].url,
468 token: servers[0].accessToken,
469 type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
470 })
471 416
472 expect(res.body.total).to.equal(3) 417 const body = await command.list({ type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED })
418 expect(body.total).to.equal(3)
473 }) 419 })
474 }) 420 })
475 421
diff --git a/server/tests/api/notifications/admin-notifications.ts b/server/tests/api/notifications/admin-notifications.ts
index cfe0bd2bb..c00d4e257 100644
--- a/server/tests/api/notifications/admin-notifications.ts
+++ b/server/tests/api/notifications/admin-notifications.ts
@@ -2,21 +2,21 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { MockJoinPeerTubeVersions } from '@shared/extra-utils/mock-servers/joinpeertube-versions'
6import { PluginType } from '@shared/models'
7import { cleanupTests, installPlugin, setPluginLatestVersion, setPluginVersion, wait } from '../../../../shared/extra-utils'
8import { ServerInfo } from '../../../../shared/extra-utils/index'
9import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
10import { 5import {
11 CheckerBaseParams, 6 CheckerBaseParams,
12 checkNewPeerTubeVersion, 7 checkNewPeerTubeVersion,
13 checkNewPluginVersion, 8 checkNewPluginVersion,
14 prepareNotificationsTest 9 cleanupTests,
15} from '../../../../shared/extra-utils/users/user-notifications' 10 MockJoinPeerTubeVersions,
16import { UserNotification, UserNotificationType } from '../../../../shared/models/users' 11 MockSmtpServer,
12 PeerTubeServer,
13 prepareNotificationsTest,
14 wait
15} from '@shared/extra-utils'
16import { PluginType, UserNotification, UserNotificationType } from '@shared/models'
17 17
18describe('Test admin notifications', function () { 18describe('Test admin notifications', function () {
19 let server: ServerInfo 19 let server: PeerTubeServer
20 let userNotifications: UserNotification[] = [] 20 let userNotifications: UserNotification[] = []
21 let adminNotifications: UserNotification[] = [] 21 let adminNotifications: UserNotification[] = []
22 let emails: object[] = [] 22 let emails: object[] = []
@@ -58,17 +58,8 @@ describe('Test admin notifications', function () {
58 token: server.accessToken 58 token: server.accessToken
59 } 59 }
60 60
61 await installPlugin({ 61 await server.plugins.install({ npmName: 'peertube-plugin-hello-world' })
62 url: server.url, 62 await server.plugins.install({ npmName: 'peertube-theme-background-red' })
63 accessToken: server.accessToken,
64 npmName: 'peertube-plugin-hello-world'
65 })
66
67 await installPlugin({
68 url: server.url,
69 accessToken: server.accessToken,
70 npmName: 'peertube-theme-background-red'
71 })
72 }) 63 })
73 64
74 describe('Latest PeerTube version notification', function () { 65 describe('Latest PeerTube version notification', function () {
@@ -79,7 +70,7 @@ describe('Test admin notifications', function () {
79 joinPeerTubeServer.setLatestVersion('1.4.2') 70 joinPeerTubeServer.setLatestVersion('1.4.2')
80 71
81 await wait(3000) 72 await wait(3000)
82 await checkNewPeerTubeVersion(baseParams, '1.4.2', 'absence') 73 await checkNewPeerTubeVersion({ ...baseParams, latestVersion: '1.4.2', checkType: 'absence' })
83 }) 74 })
84 75
85 it('Should send a notification to admins on new plugin version', async function () { 76 it('Should send a notification to admins on new plugin version', async function () {
@@ -88,7 +79,7 @@ describe('Test admin notifications', function () {
88 joinPeerTubeServer.setLatestVersion('15.4.2') 79 joinPeerTubeServer.setLatestVersion('15.4.2')
89 80
90 await wait(3000) 81 await wait(3000)
91 await checkNewPeerTubeVersion(baseParams, '15.4.2', 'presence') 82 await checkNewPeerTubeVersion({ ...baseParams, latestVersion: '15.4.2', checkType: 'presence' })
92 }) 83 })
93 84
94 it('Should not send the same notification to admins', async function () { 85 it('Should not send the same notification to admins', async function () {
@@ -110,7 +101,7 @@ describe('Test admin notifications', function () {
110 joinPeerTubeServer.setLatestVersion('15.4.3') 101 joinPeerTubeServer.setLatestVersion('15.4.3')
111 102
112 await wait(3000) 103 await wait(3000)
113 await checkNewPeerTubeVersion(baseParams, '15.4.3', 'presence') 104 await checkNewPeerTubeVersion({ ...baseParams, latestVersion: '15.4.3', checkType: 'presence' })
114 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(2) 105 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(2)
115 }) 106 })
116 }) 107 })
@@ -121,17 +112,17 @@ describe('Test admin notifications', function () {
121 this.timeout(30000) 112 this.timeout(30000)
122 113
123 await wait(6000) 114 await wait(6000)
124 await checkNewPluginVersion(baseParams, PluginType.PLUGIN, 'hello-world', 'absence') 115 await checkNewPluginVersion({ ...baseParams, pluginType: PluginType.PLUGIN, pluginName: 'hello-world', checkType: 'absence' })
125 }) 116 })
126 117
127 it('Should send a notification to admins on new plugin version', async function () { 118 it('Should send a notification to admins on new plugin version', async function () {
128 this.timeout(30000) 119 this.timeout(30000)
129 120
130 await setPluginVersion(server.internalServerNumber, 'hello-world', '0.0.1') 121 await server.sql.setPluginVersion('hello-world', '0.0.1')
131 await setPluginLatestVersion(server.internalServerNumber, 'hello-world', '0.0.1') 122 await server.sql.setPluginLatestVersion('hello-world', '0.0.1')
132 await wait(6000) 123 await wait(6000)
133 124
134 await checkNewPluginVersion(baseParams, PluginType.PLUGIN, 'hello-world', 'presence') 125 await checkNewPluginVersion({ ...baseParams, pluginType: PluginType.PLUGIN, pluginName: 'hello-world', checkType: 'presence' })
135 }) 126 })
136 127
137 it('Should not send the same notification to admins', async function () { 128 it('Should not send the same notification to admins', async function () {
@@ -149,8 +140,8 @@ describe('Test admin notifications', function () {
149 it('Should send a new notification after a new plugin release', async function () { 140 it('Should send a new notification after a new plugin release', async function () {
150 this.timeout(30000) 141 this.timeout(30000)
151 142
152 await setPluginVersion(server.internalServerNumber, 'hello-world', '0.0.1') 143 await server.sql.setPluginVersion('hello-world', '0.0.1')
153 await setPluginLatestVersion(server.internalServerNumber, 'hello-world', '0.0.1') 144 await server.sql.setPluginLatestVersion('hello-world', '0.0.1')
154 await wait(6000) 145 await wait(6000)
155 146
156 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(2) 147 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(2)
diff --git a/server/tests/api/notifications/comments-notifications.ts b/server/tests/api/notifications/comments-notifications.ts
index d2badf237..7cbb21397 100644
--- a/server/tests/api/notifications/comments-notifications.ts
+++ b/server/tests/api/notifications/comments-notifications.ts
@@ -3,30 +3,22 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 addAccountToAccountBlocklist,
7 addVideoCommentReply,
8 addVideoCommentThread,
9 checkCommentMention, 6 checkCommentMention,
10 CheckerBaseParams, 7 CheckerBaseParams,
11 checkNewCommentOnMyVideo, 8 checkNewCommentOnMyVideo,
12 cleanupTests, 9 cleanupTests,
13 getVideoCommentThreads,
14 getVideoThreadComments,
15 MockSmtpServer, 10 MockSmtpServer,
11 PeerTubeServer,
16 prepareNotificationsTest, 12 prepareNotificationsTest,
17 removeAccountFromAccountBlocklist,
18 ServerInfo,
19 updateMyUser,
20 uploadVideo,
21 waitJobs 13 waitJobs
22} from '@shared/extra-utils' 14} from '@shared/extra-utils'
23import { UserNotification, VideoCommentThreadTree } from '@shared/models' 15import { UserNotification } from '@shared/models'
24 16
25const expect = chai.expect 17const expect = chai.expect
26 18
27describe('Test comments notifications', function () { 19describe('Test comments notifications', function () {
28 let servers: ServerInfo[] = [] 20 let servers: PeerTubeServer[] = []
29 let userAccessToken: string 21 let userToken: string
30 let userNotifications: UserNotification[] = [] 22 let userNotifications: UserNotification[] = []
31 let emails: object[] = [] 23 let emails: object[] = []
32 24
@@ -40,7 +32,7 @@ describe('Test comments notifications', function () {
40 32
41 const res = await prepareNotificationsTest(2) 33 const res = await prepareNotificationsTest(2)
42 emails = res.emails 34 emails = res.emails
43 userAccessToken = res.userAccessToken 35 userToken = res.userAccessToken
44 servers = res.servers 36 servers = res.servers
45 userNotifications = res.userNotifications 37 userNotifications = res.userNotifications
46 }) 38 })
@@ -53,136 +45,125 @@ describe('Test comments notifications', function () {
53 server: servers[0], 45 server: servers[0],
54 emails, 46 emails,
55 socketNotifications: userNotifications, 47 socketNotifications: userNotifications,
56 token: userAccessToken 48 token: userToken
57 } 49 }
58 }) 50 })
59 51
60 it('Should not send a new comment notification after a comment on another video', async function () { 52 it('Should not send a new comment notification after a comment on another video', async function () {
61 this.timeout(20000) 53 this.timeout(20000)
62 54
63 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 55 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
64 const uuid = resVideo.body.video.uuid
65 56
66 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, 'comment') 57 const created = await servers[0].comments.createThread({ videoId: uuid, text: 'comment' })
67 const commentId = resComment.body.comment.id 58 const commentId = created.id
68 59
69 await waitJobs(servers) 60 await waitJobs(servers)
70 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'absence') 61 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'absence' })
71 }) 62 })
72 63
73 it('Should not send a new comment notification if I comment my own video', async function () { 64 it('Should not send a new comment notification if I comment my own video', async function () {
74 this.timeout(20000) 65 this.timeout(20000)
75 66
76 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 67 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
77 const uuid = resVideo.body.video.uuid
78 68
79 const resComment = await addVideoCommentThread(servers[0].url, userAccessToken, uuid, 'comment') 69 const created = await servers[0].comments.createThread({ token: userToken, videoId: uuid, text: 'comment' })
80 const commentId = resComment.body.comment.id 70 const commentId = created.id
81 71
82 await waitJobs(servers) 72 await waitJobs(servers)
83 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'absence') 73 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'absence' })
84 }) 74 })
85 75
86 it('Should not send a new comment notification if the account is muted', async function () { 76 it('Should not send a new comment notification if the account is muted', async function () {
87 this.timeout(20000) 77 this.timeout(20000)
88 78
89 await addAccountToAccountBlocklist(servers[0].url, userAccessToken, 'root') 79 await servers[0].blocklist.addToMyBlocklist({ token: userToken, account: 'root' })
90 80
91 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 81 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
92 const uuid = resVideo.body.video.uuid
93 82
94 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, 'comment') 83 const created = await servers[0].comments.createThread({ videoId: uuid, text: 'comment' })
95 const commentId = resComment.body.comment.id 84 const commentId = created.id
96 85
97 await waitJobs(servers) 86 await waitJobs(servers)
98 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'absence') 87 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'absence' })
99 88
100 await removeAccountFromAccountBlocklist(servers[0].url, userAccessToken, 'root') 89 await servers[0].blocklist.removeFromMyBlocklist({ token: userToken, account: 'root' })
101 }) 90 })
102 91
103 it('Should send a new comment notification after a local comment on my video', async function () { 92 it('Should send a new comment notification after a local comment on my video', async function () {
104 this.timeout(20000) 93 this.timeout(20000)
105 94
106 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 95 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
107 const uuid = resVideo.body.video.uuid
108 96
109 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, 'comment') 97 const created = await servers[0].comments.createThread({ videoId: uuid, text: 'comment' })
110 const commentId = resComment.body.comment.id 98 const commentId = created.id
111 99
112 await waitJobs(servers) 100 await waitJobs(servers)
113 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'presence') 101 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'presence' })
114 }) 102 })
115 103
116 it('Should send a new comment notification after a remote comment on my video', async function () { 104 it('Should send a new comment notification after a remote comment on my video', async function () {
117 this.timeout(20000) 105 this.timeout(20000)
118 106
119 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 107 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
120 const uuid = resVideo.body.video.uuid
121 108
122 await waitJobs(servers) 109 await waitJobs(servers)
123 110
124 await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, 'comment') 111 await servers[1].comments.createThread({ videoId: uuid, text: 'comment' })
125 112
126 await waitJobs(servers) 113 await waitJobs(servers)
127 114
128 const resComment = await getVideoCommentThreads(servers[0].url, uuid, 0, 5) 115 const { data } = await servers[0].comments.listThreads({ videoId: uuid })
129 expect(resComment.body.data).to.have.lengthOf(1) 116 expect(data).to.have.lengthOf(1)
130 const commentId = resComment.body.data[0].id
131 117
132 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'presence') 118 const commentId = data[0].id
119 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'presence' })
133 }) 120 })
134 121
135 it('Should send a new comment notification after a local reply on my video', async function () { 122 it('Should send a new comment notification after a local reply on my video', async function () {
136 this.timeout(20000) 123 this.timeout(20000)
137 124
138 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 125 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
139 const uuid = resVideo.body.video.uuid
140 126
141 const resThread = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, 'comment') 127 const { id: threadId } = await servers[0].comments.createThread({ videoId: uuid, text: 'comment' })
142 const threadId = resThread.body.comment.id
143 128
144 const resComment = await addVideoCommentReply(servers[0].url, servers[0].accessToken, uuid, threadId, 'reply') 129 const { id: commentId } = await servers[0].comments.addReply({ videoId: uuid, toCommentId: threadId, text: 'reply' })
145 const commentId = resComment.body.comment.id
146 130
147 await waitJobs(servers) 131 await waitJobs(servers)
148 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, threadId, 'presence') 132 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId, commentId, checkType: 'presence' })
149 }) 133 })
150 134
151 it('Should send a new comment notification after a remote reply on my video', async function () { 135 it('Should send a new comment notification after a remote reply on my video', async function () {
152 this.timeout(20000) 136 this.timeout(20000)
153 137
154 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 138 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
155 const uuid = resVideo.body.video.uuid
156 await waitJobs(servers) 139 await waitJobs(servers)
157 140
158 { 141 {
159 const resThread = await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, 'comment') 142 const created = await servers[1].comments.createThread({ videoId: uuid, text: 'comment' })
160 const threadId = resThread.body.comment.id 143 const threadId = created.id
161 await addVideoCommentReply(servers[1].url, servers[1].accessToken, uuid, threadId, 'reply') 144 await servers[1].comments.addReply({ videoId: uuid, toCommentId: threadId, text: 'reply' })
162 } 145 }
163 146
164 await waitJobs(servers) 147 await waitJobs(servers)
165 148
166 const resThread = await getVideoCommentThreads(servers[0].url, uuid, 0, 5) 149 const { data } = await servers[0].comments.listThreads({ videoId: uuid })
167 expect(resThread.body.data).to.have.lengthOf(1) 150 expect(data).to.have.lengthOf(1)
168 const threadId = resThread.body.data[0].id
169 151
170 const resComments = await getVideoThreadComments(servers[0].url, uuid, threadId) 152 const threadId = data[0].id
171 const tree = resComments.body as VideoCommentThreadTree 153 const tree = await servers[0].comments.getThread({ videoId: uuid, threadId })
172 154
173 expect(tree.children).to.have.lengthOf(1) 155 expect(tree.children).to.have.lengthOf(1)
174 const commentId = tree.children[0].comment.id 156 const commentId = tree.children[0].comment.id
175 157
176 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, threadId, 'presence') 158 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId, commentId, checkType: 'presence' })
177 }) 159 })
178 160
179 it('Should convert markdown in comment to html', async function () { 161 it('Should convert markdown in comment to html', async function () {
180 this.timeout(20000) 162 this.timeout(20000)
181 163
182 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'cool video' }) 164 const { uuid } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'cool video' } })
183 const uuid = resVideo.body.video.uuid
184 165
185 await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, commentText) 166 await servers[0].comments.createThread({ videoId: uuid, text: commentText })
186 167
187 await waitJobs(servers) 168 await waitJobs(servers)
188 169
@@ -193,147 +174,127 @@ describe('Test comments notifications', function () {
193 174
194 describe('Mention notifications', function () { 175 describe('Mention notifications', function () {
195 let baseParams: CheckerBaseParams 176 let baseParams: CheckerBaseParams
177 const byAccountDisplayName = 'super root name'
196 178
197 before(async () => { 179 before(async () => {
198 baseParams = { 180 baseParams = {
199 server: servers[0], 181 server: servers[0],
200 emails, 182 emails,
201 socketNotifications: userNotifications, 183 socketNotifications: userNotifications,
202 token: userAccessToken 184 token: userToken
203 } 185 }
204 186
205 await updateMyUser({ 187 await servers[0].users.updateMe({ displayName: 'super root name' })
206 url: servers[0].url, 188 await servers[1].users.updateMe({ displayName: 'super root 2 name' })
207 accessToken: servers[0].accessToken,
208 displayName: 'super root name'
209 })
210
211 await updateMyUser({
212 url: servers[1].url,
213 accessToken: servers[1].accessToken,
214 displayName: 'super root 2 name'
215 })
216 }) 189 })
217 190
218 it('Should not send a new mention comment notification if I mention the video owner', async function () { 191 it('Should not send a new mention comment notification if I mention the video owner', async function () {
219 this.timeout(10000) 192 this.timeout(10000)
220 193
221 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 194 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
222 const uuid = resVideo.body.video.uuid
223 195
224 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, '@user_1 hello') 196 const { id: commentId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hello' })
225 const commentId = resComment.body.comment.id
226 197
227 await waitJobs(servers) 198 await waitJobs(servers)
228 await checkCommentMention(baseParams, uuid, commentId, commentId, 'super root name', 'absence') 199 await checkCommentMention({ ...baseParams, shortUUID, threadId: commentId, commentId, byAccountDisplayName, checkType: 'absence' })
229 }) 200 })
230 201
231 it('Should not send a new mention comment notification if I mention myself', async function () { 202 it('Should not send a new mention comment notification if I mention myself', async function () {
232 this.timeout(10000) 203 this.timeout(10000)
233 204
234 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 205 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
235 const uuid = resVideo.body.video.uuid
236 206
237 const resComment = await addVideoCommentThread(servers[0].url, userAccessToken, uuid, '@user_1 hello') 207 const { id: commentId } = await servers[0].comments.createThread({ token: userToken, videoId: uuid, text: '@user_1 hello' })
238 const commentId = resComment.body.comment.id
239 208
240 await waitJobs(servers) 209 await waitJobs(servers)
241 await checkCommentMention(baseParams, uuid, commentId, commentId, 'super root name', 'absence') 210 await checkCommentMention({ ...baseParams, shortUUID, threadId: commentId, commentId, byAccountDisplayName, checkType: 'absence' })
242 }) 211 })
243 212
244 it('Should not send a new mention notification if the account is muted', async function () { 213 it('Should not send a new mention notification if the account is muted', async function () {
245 this.timeout(10000) 214 this.timeout(10000)
246 215
247 await addAccountToAccountBlocklist(servers[0].url, userAccessToken, 'root') 216 await servers[0].blocklist.addToMyBlocklist({ token: userToken, account: 'root' })
248 217
249 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 218 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
250 const uuid = resVideo.body.video.uuid
251 219
252 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, '@user_1 hello') 220 const { id: commentId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hello' })
253 const commentId = resComment.body.comment.id
254 221
255 await waitJobs(servers) 222 await waitJobs(servers)
256 await checkCommentMention(baseParams, uuid, commentId, commentId, 'super root name', 'absence') 223 await checkCommentMention({ ...baseParams, shortUUID, threadId: commentId, commentId, byAccountDisplayName, checkType: 'absence' })
257 224
258 await removeAccountFromAccountBlocklist(servers[0].url, userAccessToken, 'root') 225 await servers[0].blocklist.removeFromMyBlocklist({ token: userToken, account: 'root' })
259 }) 226 })
260 227
261 it('Should not send a new mention notification if the remote account mention a local account', async function () { 228 it('Should not send a new mention notification if the remote account mention a local account', async function () {
262 this.timeout(20000) 229 this.timeout(20000)
263 230
264 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 231 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
265 const uuid = resVideo.body.video.uuid
266 232
267 await waitJobs(servers) 233 await waitJobs(servers)
268 const resThread = await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, '@user_1 hello') 234 const { id: threadId } = await servers[1].comments.createThread({ videoId: uuid, text: '@user_1 hello' })
269 const threadId = resThread.body.comment.id
270 235
271 await waitJobs(servers) 236 await waitJobs(servers)
272 await checkCommentMention(baseParams, uuid, threadId, threadId, 'super root 2 name', 'absence') 237
238 const byAccountDisplayName = 'super root 2 name'
239 await checkCommentMention({ ...baseParams, shortUUID, threadId, commentId: threadId, byAccountDisplayName, checkType: 'absence' })
273 }) 240 })
274 241
275 it('Should send a new mention notification after local comments', async function () { 242 it('Should send a new mention notification after local comments', async function () {
276 this.timeout(10000) 243 this.timeout(10000)
277 244
278 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 245 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
279 const uuid = resVideo.body.video.uuid
280 246
281 const resThread = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, '@user_1 hello 1') 247 const { id: threadId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hellotext: 1' })
282 const threadId = resThread.body.comment.id
283 248
284 await waitJobs(servers) 249 await waitJobs(servers)
285 await checkCommentMention(baseParams, uuid, threadId, threadId, 'super root name', 'presence') 250 await checkCommentMention({ ...baseParams, shortUUID, threadId, commentId: threadId, byAccountDisplayName, checkType: 'presence' })
286 251
287 const resComment = await addVideoCommentReply(servers[0].url, servers[0].accessToken, uuid, threadId, 'hello 2 @user_1') 252 const { id: commentId } = await servers[0].comments.addReply({ videoId: uuid, toCommentId: threadId, text: 'hello 2 @user_1' })
288 const commentId = resComment.body.comment.id
289 253
290 await waitJobs(servers) 254 await waitJobs(servers)
291 await checkCommentMention(baseParams, uuid, commentId, threadId, 'super root name', 'presence') 255 await checkCommentMention({ ...baseParams, shortUUID, commentId, threadId, byAccountDisplayName, checkType: 'presence' })
292 }) 256 })
293 257
294 it('Should send a new mention notification after remote comments', async function () { 258 it('Should send a new mention notification after remote comments', async function () {
295 this.timeout(20000) 259 this.timeout(20000)
296 260
297 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 261 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
298 const uuid = resVideo.body.video.uuid
299 262
300 await waitJobs(servers) 263 await waitJobs(servers)
301 264
302 const text1 = `hello @user_1@localhost:${servers[0].port} 1` 265 const text1 = `hello @user_1@localhost:${servers[0].port} 1`
303 const resThread = await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, text1) 266 const { id: server2ThreadId } = await servers[1].comments.createThread({ videoId: uuid, text: text1 })
304 const server2ThreadId = resThread.body.comment.id
305 267
306 await waitJobs(servers) 268 await waitJobs(servers)
307 269
308 const resThread2 = await getVideoCommentThreads(servers[0].url, uuid, 0, 5) 270 const { data } = await servers[0].comments.listThreads({ videoId: uuid })
309 expect(resThread2.body.data).to.have.lengthOf(1) 271 expect(data).to.have.lengthOf(1)
310 const server1ThreadId = resThread2.body.data[0].id 272
311 await checkCommentMention(baseParams, uuid, server1ThreadId, server1ThreadId, 'super root 2 name', 'presence') 273 const byAccountDisplayName = 'super root 2 name'
274 const threadId = data[0].id
275 await checkCommentMention({ ...baseParams, shortUUID, commentId: threadId, threadId, byAccountDisplayName, checkType: 'presence' })
312 276
313 const text2 = `@user_1@localhost:${servers[0].port} hello 2 @root@localhost:${servers[0].port}` 277 const text2 = `@user_1@localhost:${servers[0].port} hello 2 @root@localhost:${servers[0].port}`
314 await addVideoCommentReply(servers[1].url, servers[1].accessToken, uuid, server2ThreadId, text2) 278 await servers[1].comments.addReply({ videoId: uuid, toCommentId: server2ThreadId, text: text2 })
315 279
316 await waitJobs(servers) 280 await waitJobs(servers)
317 281
318 const resComments = await getVideoThreadComments(servers[0].url, uuid, server1ThreadId) 282 const tree = await servers[0].comments.getThread({ videoId: uuid, threadId })
319 const tree = resComments.body as VideoCommentThreadTree
320 283
321 expect(tree.children).to.have.lengthOf(1) 284 expect(tree.children).to.have.lengthOf(1)
322 const commentId = tree.children[0].comment.id 285 const commentId = tree.children[0].comment.id
323 286
324 await checkCommentMention(baseParams, uuid, commentId, server1ThreadId, 'super root 2 name', 'presence') 287 await checkCommentMention({ ...baseParams, shortUUID, commentId, threadId, byAccountDisplayName, checkType: 'presence' })
325 }) 288 })
326 289
327 it('Should convert markdown in comment to html', async function () { 290 it('Should convert markdown in comment to html', async function () {
328 this.timeout(10000) 291 this.timeout(10000)
329 292
330 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 293 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
331 const uuid = resVideo.body.video.uuid
332 294
333 const resThread = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, '@user_1 hello 1') 295 const { id: threadId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hello 1' })
334 const threadId = resThread.body.comment.id
335 296
336 await addVideoCommentReply(servers[0].url, servers[0].accessToken, uuid, threadId, '@user_1 ' + commentText) 297 await servers[0].comments.addReply({ videoId: uuid, toCommentId: threadId, text: '@user_1 ' + commentText })
337 298
338 await waitJobs(servers) 299 await waitJobs(servers)
339 300
diff --git a/server/tests/api/notifications/moderation-notifications.ts b/server/tests/api/notifications/moderation-notifications.ts
index 3425480ae..eb3c29fe7 100644
--- a/server/tests/api/notifications/moderation-notifications.ts
+++ b/server/tests/api/notifications/moderation-notifications.ts
@@ -2,33 +2,6 @@
2 2
3import 'mocha' 3import 'mocha'
4import { buildUUID } from '@server/helpers/uuid' 4import { buildUUID } from '@server/helpers/uuid'
5import { AbuseState } from '@shared/models'
6import {
7 addAbuseMessage,
8 addVideoCommentThread,
9 addVideoToBlacklist,
10 cleanupTests,
11 createUser,
12 follow,
13 generateUserAccessToken,
14 getAccount,
15 getCustomConfig,
16 getVideoCommentThreads,
17 getVideoIdFromUUID,
18 immutableAssign,
19 MockInstancesIndex,
20 registerUser,
21 removeVideoFromBlacklist,
22 reportAbuse,
23 unfollow,
24 updateAbuse,
25 updateCustomConfig,
26 updateCustomSubConfig,
27 wait
28} from '../../../../shared/extra-utils'
29import { ServerInfo, uploadVideo } from '../../../../shared/extra-utils/index'
30import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
31import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
32import { 5import {
33 checkAbuseStateChange, 6 checkAbuseStateChange,
34 checkAutoInstanceFollowing, 7 checkAutoInstanceFollowing,
@@ -43,15 +16,18 @@ import {
43 checkUserRegistered, 16 checkUserRegistered,
44 checkVideoAutoBlacklistForModerators, 17 checkVideoAutoBlacklistForModerators,
45 checkVideoIsPublished, 18 checkVideoIsPublished,
46 prepareNotificationsTest 19 cleanupTests,
47} from '../../../../shared/extra-utils/users/user-notifications' 20 MockInstancesIndex,
48import { addUserSubscription, removeUserSubscription } from '../../../../shared/extra-utils/users/user-subscriptions' 21 MockSmtpServer,
49import { CustomConfig } from '../../../../shared/models/server' 22 PeerTubeServer,
50import { UserNotification } from '../../../../shared/models/users' 23 prepareNotificationsTest,
51import { VideoPrivacy } from '../../../../shared/models/videos' 24 wait,
25 waitJobs
26} from '@shared/extra-utils'
27import { AbuseState, CustomConfig, UserNotification, VideoPrivacy } from '@shared/models'
52 28
53describe('Test moderation notifications', function () { 29describe('Test moderation notifications', function () {
54 let servers: ServerInfo[] = [] 30 let servers: PeerTubeServer[] = []
55 let userAccessToken: string 31 let userAccessToken: string
56 let userNotifications: UserNotification[] = [] 32 let userNotifications: UserNotification[] = []
57 let adminNotifications: UserNotification[] = [] 33 let adminNotifications: UserNotification[] = []
@@ -86,93 +62,97 @@ describe('Test moderation notifications', function () {
86 this.timeout(20000) 62 this.timeout(20000)
87 63
88 const name = 'video for abuse ' + buildUUID() 64 const name = 'video for abuse ' + buildUUID()
89 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 65 const video = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
90 const video = resVideo.body.video
91 66
92 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, videoId: video.id, reason: 'super reason' }) 67 await servers[0].abuses.report({ videoId: video.id, reason: 'super reason' })
93 68
94 await waitJobs(servers) 69 await waitJobs(servers)
95 await checkNewVideoAbuseForModerators(baseParams, video.uuid, name, 'presence') 70 await checkNewVideoAbuseForModerators({ ...baseParams, shortUUID: video.shortUUID, videoName: name, checkType: 'presence' })
96 }) 71 })
97 72
98 it('Should send a notification to moderators on remote video abuse', async function () { 73 it('Should send a notification to moderators on remote video abuse', async function () {
99 this.timeout(20000) 74 this.timeout(20000)
100 75
101 const name = 'video for abuse ' + buildUUID() 76 const name = 'video for abuse ' + buildUUID()
102 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 77 const video = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
103 const video = resVideo.body.video
104 78
105 await waitJobs(servers) 79 await waitJobs(servers)
106 80
107 const videoId = await getVideoIdFromUUID(servers[1].url, video.uuid) 81 const videoId = await servers[1].videos.getId({ uuid: video.uuid })
108 await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, videoId, reason: 'super reason' }) 82 await servers[1].abuses.report({ videoId, reason: 'super reason' })
109 83
110 await waitJobs(servers) 84 await waitJobs(servers)
111 await checkNewVideoAbuseForModerators(baseParams, video.uuid, name, 'presence') 85 await checkNewVideoAbuseForModerators({ ...baseParams, shortUUID: video.shortUUID, videoName: name, checkType: 'presence' })
112 }) 86 })
113 87
114 it('Should send a notification to moderators on local comment abuse', async function () { 88 it('Should send a notification to moderators on local comment abuse', async function () {
115 this.timeout(20000) 89 this.timeout(20000)
116 90
117 const name = 'video for abuse ' + buildUUID() 91 const name = 'video for abuse ' + buildUUID()
118 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 92 const video = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
119 const video = resVideo.body.video 93 const comment = await servers[0].comments.createThread({
120 const resComment = await addVideoCommentThread(servers[0].url, userAccessToken, video.id, 'comment abuse ' + buildUUID()) 94 token: userAccessToken,
121 const comment = resComment.body.comment 95 videoId: video.id,
96 text: 'comment abuse ' + buildUUID()
97 })
122 98
123 await waitJobs(servers) 99 await waitJobs(servers)
124 100
125 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, commentId: comment.id, reason: 'super reason' }) 101 await servers[0].abuses.report({ commentId: comment.id, reason: 'super reason' })
126 102
127 await waitJobs(servers) 103 await waitJobs(servers)
128 await checkNewCommentAbuseForModerators(baseParams, video.uuid, name, 'presence') 104 await checkNewCommentAbuseForModerators({ ...baseParams, shortUUID: video.shortUUID, videoName: name, checkType: 'presence' })
129 }) 105 })
130 106
131 it('Should send a notification to moderators on remote comment abuse', async function () { 107 it('Should send a notification to moderators on remote comment abuse', async function () {
132 this.timeout(20000) 108 this.timeout(20000)
133 109
134 const name = 'video for abuse ' + buildUUID() 110 const name = 'video for abuse ' + buildUUID()
135 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 111 const video = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
136 const video = resVideo.body.video 112
137 await addVideoCommentThread(servers[0].url, userAccessToken, video.id, 'comment abuse ' + buildUUID()) 113 await servers[0].comments.createThread({
114 token: userAccessToken,
115 videoId: video.id,
116 text: 'comment abuse ' + buildUUID()
117 })
138 118
139 await waitJobs(servers) 119 await waitJobs(servers)
140 120
141 const resComments = await getVideoCommentThreads(servers[1].url, video.uuid, 0, 5) 121 const { data } = await servers[1].comments.listThreads({ videoId: video.uuid })
142 const commentId = resComments.body.data[0].id 122 const commentId = data[0].id
143 await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, commentId, reason: 'super reason' }) 123 await servers[1].abuses.report({ commentId, reason: 'super reason' })
144 124
145 await waitJobs(servers) 125 await waitJobs(servers)
146 await checkNewCommentAbuseForModerators(baseParams, video.uuid, name, 'presence') 126 await checkNewCommentAbuseForModerators({ ...baseParams, shortUUID: video.shortUUID, videoName: name, checkType: 'presence' })
147 }) 127 })
148 128
149 it('Should send a notification to moderators on local account abuse', async function () { 129 it('Should send a notification to moderators on local account abuse', async function () {
150 this.timeout(20000) 130 this.timeout(20000)
151 131
152 const username = 'user' + new Date().getTime() 132 const username = 'user' + new Date().getTime()
153 const resUser = await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username, password: 'donald' }) 133 const { account } = await servers[0].users.create({ username, password: 'donald' })
154 const accountId = resUser.body.user.account.id 134 const accountId = account.id
155 135
156 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, accountId, reason: 'super reason' }) 136 await servers[0].abuses.report({ accountId, reason: 'super reason' })
157 137
158 await waitJobs(servers) 138 await waitJobs(servers)
159 await checkNewAccountAbuseForModerators(baseParams, username, 'presence') 139 await checkNewAccountAbuseForModerators({ ...baseParams, displayName: username, checkType: 'presence' })
160 }) 140 })
161 141
162 it('Should send a notification to moderators on remote account abuse', async function () { 142 it('Should send a notification to moderators on remote account abuse', async function () {
163 this.timeout(20000) 143 this.timeout(20000)
164 144
165 const username = 'user' + new Date().getTime() 145 const username = 'user' + new Date().getTime()
166 const tmpToken = await generateUserAccessToken(servers[0], username) 146 const tmpToken = await servers[0].users.generateUserAndToken(username)
167 await uploadVideo(servers[0].url, tmpToken, { name: 'super video' }) 147 await servers[0].videos.upload({ token: tmpToken, attributes: { name: 'super video' } })
168 148
169 await waitJobs(servers) 149 await waitJobs(servers)
170 150
171 const resAccount = await getAccount(servers[1].url, username + '@' + servers[0].host) 151 const account = await servers[1].accounts.get({ accountName: username + '@' + servers[0].host })
172 await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, accountId: resAccount.body.id, reason: 'super reason' }) 152 await servers[1].abuses.report({ accountId: account.id, reason: 'super reason' })
173 153
174 await waitJobs(servers) 154 await waitJobs(servers)
175 await checkNewAccountAbuseForModerators(baseParams, username, 'presence') 155 await checkNewAccountAbuseForModerators({ ...baseParams, displayName: username, checkType: 'presence' })
176 }) 156 })
177 }) 157 })
178 158
@@ -189,29 +169,28 @@ describe('Test moderation notifications', function () {
189 } 169 }
190 170
191 const name = 'abuse ' + buildUUID() 171 const name = 'abuse ' + buildUUID()
192 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 172 const video = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
193 const video = resVideo.body.video
194 173
195 const res = await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: video.id, reason: 'super reason' }) 174 const body = await servers[0].abuses.report({ token: userAccessToken, videoId: video.id, reason: 'super reason' })
196 abuseId = res.body.abuse.id 175 abuseId = body.abuse.id
197 }) 176 })
198 177
199 it('Should send a notification to reporter if the abuse has been accepted', async function () { 178 it('Should send a notification to reporter if the abuse has been accepted', async function () {
200 this.timeout(10000) 179 this.timeout(10000)
201 180
202 await updateAbuse(servers[0].url, servers[0].accessToken, abuseId, { state: AbuseState.ACCEPTED }) 181 await servers[0].abuses.update({ abuseId, body: { state: AbuseState.ACCEPTED } })
203 await waitJobs(servers) 182 await waitJobs(servers)
204 183
205 await checkAbuseStateChange(baseParams, abuseId, AbuseState.ACCEPTED, 'presence') 184 await checkAbuseStateChange({ ...baseParams, abuseId, state: AbuseState.ACCEPTED, checkType: 'presence' })
206 }) 185 })
207 186
208 it('Should send a notification to reporter if the abuse has been rejected', async function () { 187 it('Should send a notification to reporter if the abuse has been rejected', async function () {
209 this.timeout(10000) 188 this.timeout(10000)
210 189
211 await updateAbuse(servers[0].url, servers[0].accessToken, abuseId, { state: AbuseState.REJECTED }) 190 await servers[0].abuses.update({ abuseId, body: { state: AbuseState.REJECTED } })
212 await waitJobs(servers) 191 await waitJobs(servers)
213 192
214 await checkAbuseStateChange(baseParams, abuseId, AbuseState.REJECTED, 'presence') 193 await checkAbuseStateChange({ ...baseParams, abuseId, state: AbuseState.REJECTED, checkType: 'presence' })
215 }) 194 })
216 }) 195 })
217 196
@@ -237,17 +216,16 @@ describe('Test moderation notifications', function () {
237 } 216 }
238 217
239 const name = 'abuse ' + buildUUID() 218 const name = 'abuse ' + buildUUID()
240 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 219 const video = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
241 const video = resVideo.body.video
242 220
243 { 221 {
244 const res = await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: video.id, reason: 'super reason' }) 222 const body = await servers[0].abuses.report({ token: userAccessToken, videoId: video.id, reason: 'super reason' })
245 abuseId = res.body.abuse.id 223 abuseId = body.abuse.id
246 } 224 }
247 225
248 { 226 {
249 const res = await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: video.id, reason: 'super reason 2' }) 227 const body = await servers[0].abuses.report({ token: userAccessToken, videoId: video.id, reason: 'super reason 2' })
250 abuseId2 = res.body.abuse.id 228 abuseId2 = body.abuse.id
251 } 229 }
252 }) 230 })
253 231
@@ -255,40 +233,43 @@ describe('Test moderation notifications', function () {
255 this.timeout(10000) 233 this.timeout(10000)
256 234
257 const message = 'my super message to users' 235 const message = 'my super message to users'
258 await addAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, message) 236 await servers[0].abuses.addMessage({ abuseId, message })
259 await waitJobs(servers) 237 await waitJobs(servers)
260 238
261 await checkNewAbuseMessage(baseParamsUser, abuseId, message, 'user_1@example.com', 'presence') 239 await checkNewAbuseMessage({ ...baseParamsUser, abuseId, message, toEmail: 'user_1@example.com', checkType: 'presence' })
262 }) 240 })
263 241
264 it('Should not send a notification to the admin if sent by the admin', async function () { 242 it('Should not send a notification to the admin if sent by the admin', async function () {
265 this.timeout(10000) 243 this.timeout(10000)
266 244
267 const message = 'my super message that should not be sent to the admin' 245 const message = 'my super message that should not be sent to the admin'
268 await addAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, message) 246 await servers[0].abuses.addMessage({ abuseId, message })
269 await waitJobs(servers) 247 await waitJobs(servers)
270 248
271 await checkNewAbuseMessage(baseParamsAdmin, abuseId, message, 'admin' + servers[0].internalServerNumber + '@example.com', 'absence') 249 const toEmail = 'admin' + servers[0].internalServerNumber + '@example.com'
250 await checkNewAbuseMessage({ ...baseParamsAdmin, abuseId, message, toEmail, checkType: 'absence' })
272 }) 251 })
273 252
274 it('Should send a notification to moderators', async function () { 253 it('Should send a notification to moderators', async function () {
275 this.timeout(10000) 254 this.timeout(10000)
276 255
277 const message = 'my super message to moderators' 256 const message = 'my super message to moderators'
278 await addAbuseMessage(servers[0].url, userAccessToken, abuseId2, message) 257 await servers[0].abuses.addMessage({ token: userAccessToken, abuseId: abuseId2, message })
279 await waitJobs(servers) 258 await waitJobs(servers)
280 259
281 await checkNewAbuseMessage(baseParamsAdmin, abuseId2, message, 'admin' + servers[0].internalServerNumber + '@example.com', 'presence') 260 const toEmail = 'admin' + servers[0].internalServerNumber + '@example.com'
261 await checkNewAbuseMessage({ ...baseParamsAdmin, abuseId: abuseId2, message, toEmail, checkType: 'presence' })
282 }) 262 })
283 263
284 it('Should not send a notification to reporter if sent by the reporter', async function () { 264 it('Should not send a notification to reporter if sent by the reporter', async function () {
285 this.timeout(10000) 265 this.timeout(10000)
286 266
287 const message = 'my super message that should not be sent to reporter' 267 const message = 'my super message that should not be sent to reporter'
288 await addAbuseMessage(servers[0].url, userAccessToken, abuseId2, message) 268 await servers[0].abuses.addMessage({ token: userAccessToken, abuseId: abuseId2, message })
289 await waitJobs(servers) 269 await waitJobs(servers)
290 270
291 await checkNewAbuseMessage(baseParamsUser, abuseId2, message, 'user_1@example.com', 'absence') 271 const toEmail = 'user_1@example.com'
272 await checkNewAbuseMessage({ ...baseParamsUser, abuseId: abuseId2, message, toEmail, checkType: 'absence' })
292 }) 273 })
293 }) 274 })
294 275
@@ -308,30 +289,28 @@ describe('Test moderation notifications', function () {
308 this.timeout(10000) 289 this.timeout(10000)
309 290
310 const name = 'video for abuse ' + buildUUID() 291 const name = 'video for abuse ' + buildUUID()
311 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 292 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
312 const uuid = resVideo.body.video.uuid
313 293
314 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid) 294 await servers[0].blacklist.add({ videoId: uuid })
315 295
316 await waitJobs(servers) 296 await waitJobs(servers)
317 await checkNewBlacklistOnMyVideo(baseParams, uuid, name, 'blacklist') 297 await checkNewBlacklistOnMyVideo({ ...baseParams, shortUUID, videoName: name, blacklistType: 'blacklist' })
318 }) 298 })
319 299
320 it('Should send a notification to video owner on unblacklist', async function () { 300 it('Should send a notification to video owner on unblacklist', async function () {
321 this.timeout(10000) 301 this.timeout(10000)
322 302
323 const name = 'video for abuse ' + buildUUID() 303 const name = 'video for abuse ' + buildUUID()
324 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 304 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
325 const uuid = resVideo.body.video.uuid
326 305
327 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid) 306 await servers[0].blacklist.add({ videoId: uuid })
328 307
329 await waitJobs(servers) 308 await waitJobs(servers)
330 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, uuid) 309 await servers[0].blacklist.remove({ videoId: uuid })
331 await waitJobs(servers) 310 await waitJobs(servers)
332 311
333 await wait(500) 312 await wait(500)
334 await checkNewBlacklistOnMyVideo(baseParams, uuid, name, 'unblacklist') 313 await checkNewBlacklistOnMyVideo({ ...baseParams, shortUUID, videoName: name, blacklistType: 'unblacklist' })
335 }) 314 })
336 }) 315 })
337 316
@@ -350,14 +329,14 @@ describe('Test moderation notifications', function () {
350 it('Should send a notification only to moderators when a user registers on the instance', async function () { 329 it('Should send a notification only to moderators when a user registers on the instance', async function () {
351 this.timeout(10000) 330 this.timeout(10000)
352 331
353 await registerUser(servers[0].url, 'user_45', 'password') 332 await servers[0].users.register({ username: 'user_45' })
354 333
355 await waitJobs(servers) 334 await waitJobs(servers)
356 335
357 await checkUserRegistered(baseParams, 'user_45', 'presence') 336 await checkUserRegistered({ ...baseParams, username: 'user_45', checkType: 'presence' })
358 337
359 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } 338 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } }
360 await checkUserRegistered(immutableAssign(baseParams, userOverride), 'user_45', 'absence') 339 await checkUserRegistered({ ...baseParams, ...userOverride, username: 'user_45', checkType: 'absence' })
361 }) 340 })
362 }) 341 })
363 342
@@ -392,20 +371,20 @@ describe('Test moderation notifications', function () {
392 it('Should send a notification only to admin when there is a new instance follower', async function () { 371 it('Should send a notification only to admin when there is a new instance follower', async function () {
393 this.timeout(20000) 372 this.timeout(20000)
394 373
395 await follow(servers[2].url, [ servers[0].url ], servers[2].accessToken) 374 await servers[2].follows.follow({ hosts: [ servers[0].url ] })
396 375
397 await waitJobs(servers) 376 await waitJobs(servers)
398 377
399 await checkNewInstanceFollower(baseParams, 'localhost:' + servers[2].port, 'presence') 378 await checkNewInstanceFollower({ ...baseParams, followerHost: 'localhost:' + servers[2].port, checkType: 'presence' })
400 379
401 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } 380 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } }
402 await checkNewInstanceFollower(immutableAssign(baseParams, userOverride), 'localhost:' + servers[2].port, 'absence') 381 await checkNewInstanceFollower({ ...baseParams, ...userOverride, followerHost: 'localhost:' + servers[2].port, checkType: 'absence' })
403 }) 382 })
404 383
405 it('Should send a notification on auto follow back', async function () { 384 it('Should send a notification on auto follow back', async function () {
406 this.timeout(40000) 385 this.timeout(40000)
407 386
408 await unfollow(servers[2].url, servers[2].accessToken, servers[0]) 387 await servers[2].follows.unfollow({ target: servers[0] })
409 await waitJobs(servers) 388 await waitJobs(servers)
410 389
411 const config = { 390 const config = {
@@ -415,41 +394,41 @@ describe('Test moderation notifications', function () {
415 } 394 }
416 } 395 }
417 } 396 }
418 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 397 await servers[0].config.updateCustomSubConfig({ newConfig: config })
419 398
420 await follow(servers[2].url, [ servers[0].url ], servers[2].accessToken) 399 await servers[2].follows.follow({ hosts: [ servers[0].url ] })
421 400
422 await waitJobs(servers) 401 await waitJobs(servers)
423 402
424 const followerHost = servers[0].host 403 const followerHost = servers[0].host
425 const followingHost = servers[2].host 404 const followingHost = servers[2].host
426 await checkAutoInstanceFollowing(baseParams, followerHost, followingHost, 'presence') 405 await checkAutoInstanceFollowing({ ...baseParams, followerHost, followingHost, checkType: 'presence' })
427 406
428 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } 407 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } }
429 await checkAutoInstanceFollowing(immutableAssign(baseParams, userOverride), followerHost, followingHost, 'absence') 408 await checkAutoInstanceFollowing({ ...baseParams, ...userOverride, followerHost, followingHost, checkType: 'absence' })
430 409
431 config.followings.instance.autoFollowBack.enabled = false 410 config.followings.instance.autoFollowBack.enabled = false
432 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 411 await servers[0].config.updateCustomSubConfig({ newConfig: config })
433 await unfollow(servers[0].url, servers[0].accessToken, servers[2]) 412 await servers[0].follows.unfollow({ target: servers[2] })
434 await unfollow(servers[2].url, servers[2].accessToken, servers[0]) 413 await servers[2].follows.unfollow({ target: servers[0] })
435 }) 414 })
436 415
437 it('Should send a notification on auto instances index follow', async function () { 416 it('Should send a notification on auto instances index follow', async function () {
438 this.timeout(30000) 417 this.timeout(30000)
439 await unfollow(servers[0].url, servers[0].accessToken, servers[1]) 418 await servers[0].follows.unfollow({ target: servers[1] })
440 419
441 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 420 await servers[0].config.updateCustomSubConfig({ newConfig: config })
442 421
443 await wait(5000) 422 await wait(5000)
444 await waitJobs(servers) 423 await waitJobs(servers)
445 424
446 const followerHost = servers[0].host 425 const followerHost = servers[0].host
447 const followingHost = servers[1].host 426 const followingHost = servers[1].host
448 await checkAutoInstanceFollowing(baseParams, followerHost, followingHost, 'presence') 427 await checkAutoInstanceFollowing({ ...baseParams, followerHost, followingHost, checkType: 'presence' })
449 428
450 config.followings.instance.autoFollowIndex.enabled = false 429 config.followings.instance.autoFollowIndex.enabled = false
451 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 430 await servers[0].config.updateCustomSubConfig({ newConfig: config })
452 await unfollow(servers[0].url, servers[0].accessToken, servers[1]) 431 await servers[0].follows.unfollow({ target: servers[1] })
453 }) 432 })
454 }) 433 })
455 434
@@ -457,7 +436,8 @@ describe('Test moderation notifications', function () {
457 let userBaseParams: CheckerBaseParams 436 let userBaseParams: CheckerBaseParams
458 let adminBaseParamsServer1: CheckerBaseParams 437 let adminBaseParamsServer1: CheckerBaseParams
459 let adminBaseParamsServer2: CheckerBaseParams 438 let adminBaseParamsServer2: CheckerBaseParams
460 let videoUUID: string 439 let uuid: string
440 let shortUUID: string
461 let videoName: string 441 let videoName: string
462 let currentCustomConfig: CustomConfig 442 let currentCustomConfig: CustomConfig
463 443
@@ -484,9 +464,11 @@ describe('Test moderation notifications', function () {
484 token: userAccessToken 464 token: userAccessToken
485 } 465 }
486 466
487 const resCustomConfig = await getCustomConfig(servers[0].url, servers[0].accessToken) 467 currentCustomConfig = await servers[0].config.getCustomConfig()
488 currentCustomConfig = resCustomConfig.body 468
489 const autoBlacklistTestsCustomConfig = immutableAssign(currentCustomConfig, { 469 const autoBlacklistTestsCustomConfig = {
470 ...currentCustomConfig,
471
490 autoBlacklist: { 472 autoBlacklist: {
491 videos: { 473 videos: {
492 ofUsers: { 474 ofUsers: {
@@ -494,43 +476,44 @@ describe('Test moderation notifications', function () {
494 } 476 }
495 } 477 }
496 } 478 }
497 }) 479 }
480
498 // enable transcoding otherwise own publish notification after transcoding not expected 481 // enable transcoding otherwise own publish notification after transcoding not expected
499 autoBlacklistTestsCustomConfig.transcoding.enabled = true 482 autoBlacklistTestsCustomConfig.transcoding.enabled = true
500 await updateCustomConfig(servers[0].url, servers[0].accessToken, autoBlacklistTestsCustomConfig) 483 await servers[0].config.updateCustomConfig({ newCustomConfig: autoBlacklistTestsCustomConfig })
501
502 await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port)
503 await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port)
504 484
485 await servers[0].subscriptions.add({ targetUri: 'user_1_channel@localhost:' + servers[0].port })
486 await servers[1].subscriptions.add({ targetUri: 'user_1_channel@localhost:' + servers[0].port })
505 }) 487 })
506 488
507 it('Should send notification to moderators on new video with auto-blacklist', async function () { 489 it('Should send notification to moderators on new video with auto-blacklist', async function () {
508 this.timeout(40000) 490 this.timeout(40000)
509 491
510 videoName = 'video with auto-blacklist ' + buildUUID() 492 videoName = 'video with auto-blacklist ' + buildUUID()
511 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName }) 493 const video = await servers[0].videos.upload({ token: userAccessToken, attributes: { name: videoName } })
512 videoUUID = resVideo.body.video.uuid 494 shortUUID = video.shortUUID
495 uuid = video.uuid
513 496
514 await waitJobs(servers) 497 await waitJobs(servers)
515 await checkVideoAutoBlacklistForModerators(adminBaseParamsServer1, videoUUID, videoName, 'presence') 498 await checkVideoAutoBlacklistForModerators({ ...adminBaseParamsServer1, shortUUID, videoName, checkType: 'presence' })
516 }) 499 })
517 500
518 it('Should not send video publish notification if auto-blacklisted', async function () { 501 it('Should not send video publish notification if auto-blacklisted', async function () {
519 await checkVideoIsPublished(userBaseParams, videoName, videoUUID, 'absence') 502 await checkVideoIsPublished({ ...userBaseParams, videoName, shortUUID, checkType: 'absence' })
520 }) 503 })
521 504
522 it('Should not send a local user subscription notification if auto-blacklisted', async function () { 505 it('Should not send a local user subscription notification if auto-blacklisted', async function () {
523 await checkNewVideoFromSubscription(adminBaseParamsServer1, videoName, videoUUID, 'absence') 506 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName, shortUUID, checkType: 'absence' })
524 }) 507 })
525 508
526 it('Should not send a remote user subscription notification if auto-blacklisted', async function () { 509 it('Should not send a remote user subscription notification if auto-blacklisted', async function () {
527 await checkNewVideoFromSubscription(adminBaseParamsServer2, videoName, videoUUID, 'absence') 510 await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName, shortUUID, checkType: 'absence' })
528 }) 511 })
529 512
530 it('Should send video published and unblacklist after video unblacklisted', async function () { 513 it('Should send video published and unblacklist after video unblacklisted', async function () {
531 this.timeout(40000) 514 this.timeout(40000)
532 515
533 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, videoUUID) 516 await servers[0].blacklist.remove({ videoId: uuid })
534 517
535 await waitJobs(servers) 518 await waitJobs(servers)
536 519
@@ -541,11 +524,11 @@ describe('Test moderation notifications', function () {
541 }) 524 })
542 525
543 it('Should send a local user subscription notification after removed from blacklist', async function () { 526 it('Should send a local user subscription notification after removed from blacklist', async function () {
544 await checkNewVideoFromSubscription(adminBaseParamsServer1, videoName, videoUUID, 'presence') 527 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName, shortUUID, checkType: 'presence' })
545 }) 528 })
546 529
547 it('Should send a remote user subscription notification after removed from blacklist', async function () { 530 it('Should send a remote user subscription notification after removed from blacklist', async function () {
548 await checkNewVideoFromSubscription(adminBaseParamsServer2, videoName, videoUUID, 'presence') 531 await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName, shortUUID, checkType: 'presence' })
549 }) 532 })
550 533
551 it('Should send unblacklist but not published/subscription notes after unblacklisted if scheduled update pending', async function () { 534 it('Should send unblacklist but not published/subscription notes after unblacklisted if scheduled update pending', async function () {
@@ -555,29 +538,28 @@ describe('Test moderation notifications', function () {
555 538
556 const name = 'video with auto-blacklist and future schedule ' + buildUUID() 539 const name = 'video with auto-blacklist and future schedule ' + buildUUID()
557 540
558 const data = { 541 const attributes = {
559 name, 542 name,
560 privacy: VideoPrivacy.PRIVATE, 543 privacy: VideoPrivacy.PRIVATE,
561 scheduleUpdate: { 544 scheduleUpdate: {
562 updateAt: updateAt.toISOString(), 545 updateAt: updateAt.toISOString(),
563 privacy: VideoPrivacy.PUBLIC 546 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
564 } 547 }
565 } 548 }
566 549
567 const resVideo = await uploadVideo(servers[0].url, userAccessToken, data) 550 const { shortUUID, uuid } = await servers[0].videos.upload({ token: userAccessToken, attributes })
568 const uuid = resVideo.body.video.uuid
569 551
570 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, uuid) 552 await servers[0].blacklist.remove({ videoId: uuid })
571 553
572 await waitJobs(servers) 554 await waitJobs(servers)
573 await checkNewBlacklistOnMyVideo(userBaseParams, uuid, name, 'unblacklist') 555 await checkNewBlacklistOnMyVideo({ ...userBaseParams, shortUUID, videoName: name, blacklistType: 'unblacklist' })
574 556
575 // FIXME: Can't test absence as two notifications sent to same user and util only checks last one 557 // FIXME: Can't test absence as two notifications sent to same user and util only checks last one
576 // One notification might be better anyways 558 // One notification might be better anyways
577 // await checkVideoIsPublished(userBaseParams, name, uuid, 'absence') 559 // await checkVideoIsPublished(userBaseParams, name, uuid, 'absence')
578 560
579 await checkNewVideoFromSubscription(adminBaseParamsServer1, name, uuid, 'absence') 561 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName: name, shortUUID, checkType: 'absence' })
580 await checkNewVideoFromSubscription(adminBaseParamsServer2, name, uuid, 'absence') 562 await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName: name, shortUUID, checkType: 'absence' })
581 }) 563 })
582 564
583 it('Should not send publish/subscription notifications after scheduled update if video still auto-blacklisted', async function () { 565 it('Should not send publish/subscription notifications after scheduled update if video still auto-blacklisted', async function () {
@@ -588,22 +570,21 @@ describe('Test moderation notifications', function () {
588 570
589 const name = 'video with schedule done and still auto-blacklisted ' + buildUUID() 571 const name = 'video with schedule done and still auto-blacklisted ' + buildUUID()
590 572
591 const data = { 573 const attributes = {
592 name, 574 name,
593 privacy: VideoPrivacy.PRIVATE, 575 privacy: VideoPrivacy.PRIVATE,
594 scheduleUpdate: { 576 scheduleUpdate: {
595 updateAt: updateAt.toISOString(), 577 updateAt: updateAt.toISOString(),
596 privacy: VideoPrivacy.PUBLIC 578 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
597 } 579 }
598 } 580 }
599 581
600 const resVideo = await uploadVideo(servers[0].url, userAccessToken, data) 582 const { shortUUID } = await servers[0].videos.upload({ token: userAccessToken, attributes })
601 const uuid = resVideo.body.video.uuid
602 583
603 await wait(6000) 584 await wait(6000)
604 await checkVideoIsPublished(userBaseParams, name, uuid, 'absence') 585 await checkVideoIsPublished({ ...userBaseParams, videoName: name, shortUUID, checkType: 'absence' })
605 await checkNewVideoFromSubscription(adminBaseParamsServer1, name, uuid, 'absence') 586 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName: name, shortUUID, checkType: 'absence' })
606 await checkNewVideoFromSubscription(adminBaseParamsServer2, name, uuid, 'absence') 587 await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName: name, shortUUID, checkType: 'absence' })
607 }) 588 })
608 589
609 it('Should not send a notification to moderators on new video without auto-blacklist', async function () { 590 it('Should not send a notification to moderators on new video without auto-blacklist', async function () {
@@ -612,18 +593,17 @@ describe('Test moderation notifications', function () {
612 const name = 'video without auto-blacklist ' + buildUUID() 593 const name = 'video without auto-blacklist ' + buildUUID()
613 594
614 // admin with blacklist right will not be auto-blacklisted 595 // admin with blacklist right will not be auto-blacklisted
615 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name }) 596 const { shortUUID } = await servers[0].videos.upload({ attributes: { name } })
616 const uuid = resVideo.body.video.uuid
617 597
618 await waitJobs(servers) 598 await waitJobs(servers)
619 await checkVideoAutoBlacklistForModerators(adminBaseParamsServer1, uuid, name, 'absence') 599 await checkVideoAutoBlacklistForModerators({ ...adminBaseParamsServer1, shortUUID, videoName: name, checkType: 'absence' })
620 }) 600 })
621 601
622 after(async () => { 602 after(async () => {
623 await updateCustomConfig(servers[0].url, servers[0].accessToken, currentCustomConfig) 603 await servers[0].config.updateCustomConfig({ newCustomConfig: currentCustomConfig })
624 604
625 await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) 605 await servers[0].subscriptions.remove({ uri: 'user_1_channel@localhost:' + servers[0].port })
626 await removeUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) 606 await servers[1].subscriptions.remove({ uri: 'user_1_channel@localhost:' + servers[0].port })
627 }) 607 })
628 }) 608 })
629 609
diff --git a/server/tests/api/notifications/notifications-api.ts b/server/tests/api/notifications/notifications-api.ts
index b81995449..a529a9bf7 100644
--- a/server/tests/api/notifications/notifications-api.ts
+++ b/server/tests/api/notifications/notifications-api.ts
@@ -2,28 +2,24 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { addUserSubscription } from '@shared/extra-utils/users/user-subscriptions'
6import { cleanupTests, getMyUserInformation, immutableAssign, uploadRandomVideo, waitJobs } from '../../../../shared/extra-utils'
7import { ServerInfo } from '../../../../shared/extra-utils/index'
8import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
9import { 5import {
10 CheckerBaseParams, 6 CheckerBaseParams,
11 checkNewVideoFromSubscription, 7 checkNewVideoFromSubscription,
8 cleanupTests,
12 getAllNotificationsSettings, 9 getAllNotificationsSettings,
13 getUserNotifications, 10 MockSmtpServer,
14 markAsReadAllNotifications, 11 PeerTubeServer,
15 markAsReadNotifications,
16 prepareNotificationsTest, 12 prepareNotificationsTest,
17 updateMyNotificationSettings 13 waitJobs
18} from '../../../../shared/extra-utils/users/user-notifications' 14} from '@shared/extra-utils'
19import { User, UserNotification, UserNotificationSettingValue } from '../../../../shared/models/users' 15import { UserNotification, UserNotificationSettingValue } from '@shared/models'
20 16
21const expect = chai.expect 17const expect = chai.expect
22 18
23describe('Test notifications API', function () { 19describe('Test notifications API', function () {
24 let server: ServerInfo 20 let server: PeerTubeServer
25 let userNotifications: UserNotification[] = [] 21 let userNotifications: UserNotification[] = []
26 let userAccessToken: string 22 let userToken: string
27 let emails: object[] = [] 23 let emails: object[] = []
28 24
29 before(async function () { 25 before(async function () {
@@ -31,14 +27,14 @@ describe('Test notifications API', function () {
31 27
32 const res = await prepareNotificationsTest(1) 28 const res = await prepareNotificationsTest(1)
33 emails = res.emails 29 emails = res.emails
34 userAccessToken = res.userAccessToken 30 userToken = res.userAccessToken
35 userNotifications = res.userNotifications 31 userNotifications = res.userNotifications
36 server = res.servers[0] 32 server = res.servers[0]
37 33
38 await addUserSubscription(server.url, userAccessToken, 'root_channel@localhost:' + server.port) 34 await server.subscriptions.add({ token: userToken, targetUri: 'root_channel@localhost:' + server.port })
39 35
40 for (let i = 0; i < 10; i++) { 36 for (let i = 0; i < 10; i++) {
41 await uploadRandomVideo(server, false) 37 await server.videos.randomUpload({ wait: false })
42 } 38 }
43 39
44 await waitJobs([ server ]) 40 await waitJobs([ server ])
@@ -47,49 +43,46 @@ describe('Test notifications API', function () {
47 describe('Mark as read', function () { 43 describe('Mark as read', function () {
48 44
49 it('Should mark as read some notifications', async function () { 45 it('Should mark as read some notifications', async function () {
50 const res = await getUserNotifications(server.url, userAccessToken, 2, 3) 46 const { data } = await server.notifications.list({ token: userToken, start: 2, count: 3 })
51 const ids = res.body.data.map(n => n.id) 47 const ids = data.map(n => n.id)
52 48
53 await markAsReadNotifications(server.url, userAccessToken, ids) 49 await server.notifications.markAsRead({ token: userToken, ids })
54 }) 50 })
55 51
56 it('Should have the notifications marked as read', async function () { 52 it('Should have the notifications marked as read', async function () {
57 const res = await getUserNotifications(server.url, userAccessToken, 0, 10) 53 const { data } = await server.notifications.list({ token: userToken, start: 0, count: 10 })
58 54
59 const notifications = res.body.data as UserNotification[] 55 expect(data[0].read).to.be.false
60 expect(notifications[0].read).to.be.false 56 expect(data[1].read).to.be.false
61 expect(notifications[1].read).to.be.false 57 expect(data[2].read).to.be.true
62 expect(notifications[2].read).to.be.true 58 expect(data[3].read).to.be.true
63 expect(notifications[3].read).to.be.true 59 expect(data[4].read).to.be.true
64 expect(notifications[4].read).to.be.true 60 expect(data[5].read).to.be.false
65 expect(notifications[5].read).to.be.false
66 }) 61 })
67 62
68 it('Should only list read notifications', async function () { 63 it('Should only list read notifications', async function () {
69 const res = await getUserNotifications(server.url, userAccessToken, 0, 10, false) 64 const { data } = await server.notifications.list({ token: userToken, start: 0, count: 10, unread: false })
70 65
71 const notifications = res.body.data as UserNotification[] 66 for (const notification of data) {
72 for (const notification of notifications) {
73 expect(notification.read).to.be.true 67 expect(notification.read).to.be.true
74 } 68 }
75 }) 69 })
76 70
77 it('Should only list unread notifications', async function () { 71 it('Should only list unread notifications', async function () {
78 const res = await getUserNotifications(server.url, userAccessToken, 0, 10, true) 72 const { data } = await server.notifications.list({ token: userToken, start: 0, count: 10, unread: true })
79 73
80 const notifications = res.body.data as UserNotification[] 74 for (const notification of data) {
81 for (const notification of notifications) {
82 expect(notification.read).to.be.false 75 expect(notification.read).to.be.false
83 } 76 }
84 }) 77 })
85 78
86 it('Should mark as read all notifications', async function () { 79 it('Should mark as read all notifications', async function () {
87 await markAsReadAllNotifications(server.url, userAccessToken) 80 await server.notifications.markAsReadAll({ token: userToken })
88 81
89 const res = await getUserNotifications(server.url, userAccessToken, 0, 10, true) 82 const body = await server.notifications.list({ token: userToken, start: 0, count: 10, unread: true })
90 83
91 expect(res.body.total).to.equal(0) 84 expect(body.total).to.equal(0)
92 expect(res.body.data).to.have.lengthOf(0) 85 expect(body.data).to.have.lengthOf(0)
93 }) 86 })
94 }) 87 })
95 88
@@ -101,99 +94,102 @@ describe('Test notifications API', function () {
101 server: server, 94 server: server,
102 emails, 95 emails,
103 socketNotifications: userNotifications, 96 socketNotifications: userNotifications,
104 token: userAccessToken 97 token: userToken
105 } 98 }
106 }) 99 })
107 100
108 it('Should not have notifications', async function () { 101 it('Should not have notifications', async function () {
109 this.timeout(20000) 102 this.timeout(20000)
110 103
111 await updateMyNotificationSettings(server.url, userAccessToken, immutableAssign(getAllNotificationsSettings(), { 104 await server.notifications.updateMySettings({
112 newVideoFromSubscription: UserNotificationSettingValue.NONE 105 token: userToken,
113 })) 106 settings: { ...getAllNotificationsSettings(), newVideoFromSubscription: UserNotificationSettingValue.NONE }
107 })
114 108
115 { 109 {
116 const res = await getMyUserInformation(server.url, userAccessToken) 110 const info = await server.users.getMyInfo({ token: userToken })
117 const info = res.body as User
118 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.NONE) 111 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.NONE)
119 } 112 }
120 113
121 const { name, uuid } = await uploadRandomVideo(server) 114 const { name, shortUUID } = await server.videos.randomUpload()
122 115
123 const check = { web: true, mail: true } 116 const check = { web: true, mail: true }
124 await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'absence') 117 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'absence' })
125 }) 118 })
126 119
127 it('Should only have web notifications', async function () { 120 it('Should only have web notifications', async function () {
128 this.timeout(20000) 121 this.timeout(20000)
129 122
130 await updateMyNotificationSettings(server.url, userAccessToken, immutableAssign(getAllNotificationsSettings(), { 123 await server.notifications.updateMySettings({
131 newVideoFromSubscription: UserNotificationSettingValue.WEB 124 token: userToken,
132 })) 125 settings: { ...getAllNotificationsSettings(), newVideoFromSubscription: UserNotificationSettingValue.WEB }
126 })
133 127
134 { 128 {
135 const res = await getMyUserInformation(server.url, userAccessToken) 129 const info = await server.users.getMyInfo({ token: userToken })
136 const info = res.body as User
137 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB) 130 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB)
138 } 131 }
139 132
140 const { name, uuid } = await uploadRandomVideo(server) 133 const { name, shortUUID } = await server.videos.randomUpload()
141 134
142 { 135 {
143 const check = { mail: true, web: false } 136 const check = { mail: true, web: false }
144 await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'absence') 137 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'absence' })
145 } 138 }
146 139
147 { 140 {
148 const check = { mail: false, web: true } 141 const check = { mail: false, web: true }
149 await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'presence') 142 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'presence' })
150 } 143 }
151 }) 144 })
152 145
153 it('Should only have mail notifications', async function () { 146 it('Should only have mail notifications', async function () {
154 this.timeout(20000) 147 this.timeout(20000)
155 148
156 await updateMyNotificationSettings(server.url, userAccessToken, immutableAssign(getAllNotificationsSettings(), { 149 await server.notifications.updateMySettings({
157 newVideoFromSubscription: UserNotificationSettingValue.EMAIL 150 token: userToken,
158 })) 151 settings: { ...getAllNotificationsSettings(), newVideoFromSubscription: UserNotificationSettingValue.EMAIL }
152 })
159 153
160 { 154 {
161 const res = await getMyUserInformation(server.url, userAccessToken) 155 const info = await server.users.getMyInfo({ token: userToken })
162 const info = res.body as User
163 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.EMAIL) 156 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.EMAIL)
164 } 157 }
165 158
166 const { name, uuid } = await uploadRandomVideo(server) 159 const { name, shortUUID } = await server.videos.randomUpload()
167 160
168 { 161 {
169 const check = { mail: false, web: true } 162 const check = { mail: false, web: true }
170 await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'absence') 163 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'absence' })
171 } 164 }
172 165
173 { 166 {
174 const check = { mail: true, web: false } 167 const check = { mail: true, web: false }
175 await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'presence') 168 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'presence' })
176 } 169 }
177 }) 170 })
178 171
179 it('Should have email and web notifications', async function () { 172 it('Should have email and web notifications', async function () {
180 this.timeout(20000) 173 this.timeout(20000)
181 174
182 await updateMyNotificationSettings(server.url, userAccessToken, immutableAssign(getAllNotificationsSettings(), { 175 await server.notifications.updateMySettings({
183 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL 176 token: userToken,
184 })) 177 settings: {
178 ...getAllNotificationsSettings(),
179 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
180 }
181 })
185 182
186 { 183 {
187 const res = await getMyUserInformation(server.url, userAccessToken) 184 const info = await server.users.getMyInfo({ token: userToken })
188 const info = res.body as User
189 expect(info.notificationSettings.newVideoFromSubscription).to.equal( 185 expect(info.notificationSettings.newVideoFromSubscription).to.equal(
190 UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL 186 UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
191 ) 187 )
192 } 188 }
193 189
194 const { name, uuid } = await uploadRandomVideo(server) 190 const { name, shortUUID } = await server.videos.randomUpload()
195 191
196 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 192 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
197 }) 193 })
198 }) 194 })
199 195
diff --git a/server/tests/api/notifications/user-notifications.ts b/server/tests/api/notifications/user-notifications.ts
index e981c1718..e53ab2aa5 100644
--- a/server/tests/api/notifications/user-notifications.ts
+++ b/server/tests/api/notifications/user-notifications.ts
@@ -4,34 +4,26 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { buildUUID } from '@server/helpers/uuid' 5import { buildUUID } from '@server/helpers/uuid'
6import { 6import {
7 cleanupTests,
8 updateMyUser,
9 updateVideo,
10 updateVideoChannel,
11 uploadRandomVideoOnServers,
12 wait
13} from '../../../../shared/extra-utils'
14import { ServerInfo } from '../../../../shared/extra-utils/index'
15import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
16import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
17import {
18 CheckerBaseParams, 7 CheckerBaseParams,
19 checkMyVideoImportIsFinished, 8 checkMyVideoImportIsFinished,
20 checkNewActorFollow, 9 checkNewActorFollow,
21 checkNewVideoFromSubscription, 10 checkNewVideoFromSubscription,
22 checkVideoIsPublished, 11 checkVideoIsPublished,
23 getLastNotification, 12 cleanupTests,
24 prepareNotificationsTest 13 FIXTURE_URLS,
25} from '../../../../shared/extra-utils/users/user-notifications' 14 MockSmtpServer,
26import { addUserSubscription, removeUserSubscription } from '../../../../shared/extra-utils/users/user-subscriptions' 15 PeerTubeServer,
27import { getBadVideoUrl, getGoodVideoUrl, importVideo } from '../../../../shared/extra-utils/videos/video-imports' 16 prepareNotificationsTest,
28import { UserNotification, UserNotificationType } from '../../../../shared/models/users' 17 uploadRandomVideoOnServers,
29import { VideoPrivacy } from '../../../../shared/models/videos' 18 wait,
19 waitJobs
20} from '@shared/extra-utils'
21import { UserNotification, UserNotificationType, VideoPrivacy } from '@shared/models'
30 22
31const expect = chai.expect 23const expect = chai.expect
32 24
33describe('Test user notifications', function () { 25describe('Test user notifications', function () {
34 let servers: ServerInfo[] = [] 26 let servers: PeerTubeServer[] = []
35 let userAccessToken: string 27 let userAccessToken: string
36 let userNotifications: UserNotification[] = [] 28 let userNotifications: UserNotification[] = []
37 let adminNotifications: UserNotification[] = [] 29 let adminNotifications: UserNotification[] = []
@@ -69,7 +61,7 @@ describe('Test user notifications', function () {
69 61
70 await uploadRandomVideoOnServers(servers, 1) 62 await uploadRandomVideoOnServers(servers, 1)
71 63
72 const notification = await getLastNotification(servers[0].url, userAccessToken) 64 const notification = await servers[0].notifications.getLastest({ token: userAccessToken })
73 expect(notification).to.be.undefined 65 expect(notification).to.be.undefined
74 66
75 expect(emails).to.have.lengthOf(0) 67 expect(emails).to.have.lengthOf(0)
@@ -79,21 +71,21 @@ describe('Test user notifications', function () {
79 it('Should send a new video notification if the user follows the local video publisher', async function () { 71 it('Should send a new video notification if the user follows the local video publisher', async function () {
80 this.timeout(15000) 72 this.timeout(15000)
81 73
82 await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:' + servers[0].port) 74 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@localhost:' + servers[0].port })
83 await waitJobs(servers) 75 await waitJobs(servers)
84 76
85 const { name, uuid } = await uploadRandomVideoOnServers(servers, 1) 77 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1)
86 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 78 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
87 }) 79 })
88 80
89 it('Should send a new video notification from a remote account', async function () { 81 it('Should send a new video notification from a remote account', async function () {
90 this.timeout(150000) // Server 2 has transcoding enabled 82 this.timeout(150000) // Server 2 has transcoding enabled
91 83
92 await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:' + servers[1].port) 84 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@localhost:' + servers[1].port })
93 await waitJobs(servers) 85 await waitJobs(servers)
94 86
95 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2) 87 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2)
96 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 88 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
97 }) 89 })
98 90
99 it('Should send a new video notification on a scheduled publication', async function () { 91 it('Should send a new video notification on a scheduled publication', async function () {
@@ -106,13 +98,13 @@ describe('Test user notifications', function () {
106 privacy: VideoPrivacy.PRIVATE, 98 privacy: VideoPrivacy.PRIVATE,
107 scheduleUpdate: { 99 scheduleUpdate: {
108 updateAt: updateAt.toISOString(), 100 updateAt: updateAt.toISOString(),
109 privacy: VideoPrivacy.PUBLIC 101 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
110 } 102 }
111 } 103 }
112 const { name, uuid } = await uploadRandomVideoOnServers(servers, 1, data) 104 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
113 105
114 await wait(6000) 106 await wait(6000)
115 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 107 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
116 }) 108 })
117 109
118 it('Should send a new video notification on a remote scheduled publication', async function () { 110 it('Should send a new video notification on a remote scheduled publication', async function () {
@@ -125,14 +117,14 @@ describe('Test user notifications', function () {
125 privacy: VideoPrivacy.PRIVATE, 117 privacy: VideoPrivacy.PRIVATE,
126 scheduleUpdate: { 118 scheduleUpdate: {
127 updateAt: updateAt.toISOString(), 119 updateAt: updateAt.toISOString(),
128 privacy: VideoPrivacy.PUBLIC 120 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
129 } 121 }
130 } 122 }
131 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, data) 123 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
132 await waitJobs(servers) 124 await waitJobs(servers)
133 125
134 await wait(6000) 126 await wait(6000)
135 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 127 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
136 }) 128 })
137 129
138 it('Should not send a notification before the video is published', async function () { 130 it('Should not send a notification before the video is published', async function () {
@@ -144,64 +136,64 @@ describe('Test user notifications', function () {
144 privacy: VideoPrivacy.PRIVATE, 136 privacy: VideoPrivacy.PRIVATE,
145 scheduleUpdate: { 137 scheduleUpdate: {
146 updateAt: updateAt.toISOString(), 138 updateAt: updateAt.toISOString(),
147 privacy: VideoPrivacy.PUBLIC 139 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
148 } 140 }
149 } 141 }
150 const { name, uuid } = await uploadRandomVideoOnServers(servers, 1, data) 142 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
151 143
152 await wait(6000) 144 await wait(6000)
153 await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') 145 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
154 }) 146 })
155 147
156 it('Should send a new video notification when a video becomes public', async function () { 148 it('Should send a new video notification when a video becomes public', async function () {
157 this.timeout(50000) 149 this.timeout(50000)
158 150
159 const data = { privacy: VideoPrivacy.PRIVATE } 151 const data = { privacy: VideoPrivacy.PRIVATE }
160 const { name, uuid } = await uploadRandomVideoOnServers(servers, 1, data) 152 const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
161 153
162 await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') 154 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
163 155
164 await updateVideo(servers[0].url, servers[0].accessToken, uuid, { privacy: VideoPrivacy.PUBLIC }) 156 await servers[0].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
165 157
166 await waitJobs(servers) 158 await waitJobs(servers)
167 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 159 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
168 }) 160 })
169 161
170 it('Should send a new video notification when a remote video becomes public', async function () { 162 it('Should send a new video notification when a remote video becomes public', async function () {
171 this.timeout(50000) 163 this.timeout(50000)
172 164
173 const data = { privacy: VideoPrivacy.PRIVATE } 165 const data = { privacy: VideoPrivacy.PRIVATE }
174 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, data) 166 const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
175 167
176 await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') 168 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
177 169
178 await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.PUBLIC }) 170 await servers[1].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
179 171
180 await waitJobs(servers) 172 await waitJobs(servers)
181 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 173 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
182 }) 174 })
183 175
184 it('Should not send a new video notification when a video becomes unlisted', async function () { 176 it('Should not send a new video notification when a video becomes unlisted', async function () {
185 this.timeout(50000) 177 this.timeout(50000)
186 178
187 const data = { privacy: VideoPrivacy.PRIVATE } 179 const data = { privacy: VideoPrivacy.PRIVATE }
188 const { name, uuid } = await uploadRandomVideoOnServers(servers, 1, data) 180 const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
189 181
190 await updateVideo(servers[0].url, servers[0].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED }) 182 await servers[0].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.UNLISTED } })
191 183
192 await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') 184 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
193 }) 185 })
194 186
195 it('Should not send a new video notification when a remote video becomes unlisted', async function () { 187 it('Should not send a new video notification when a remote video becomes unlisted', async function () {
196 this.timeout(50000) 188 this.timeout(50000)
197 189
198 const data = { privacy: VideoPrivacy.PRIVATE } 190 const data = { privacy: VideoPrivacy.PRIVATE }
199 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, data) 191 const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
200 192
201 await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED }) 193 await servers[1].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.UNLISTED } })
202 194
203 await waitJobs(servers) 195 await waitJobs(servers)
204 await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') 196 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
205 }) 197 })
206 198
207 it('Should send a new video notification after a video import', async function () { 199 it('Should send a new video notification after a video import', async function () {
@@ -213,14 +205,13 @@ describe('Test user notifications', function () {
213 name, 205 name,
214 channelId, 206 channelId,
215 privacy: VideoPrivacy.PUBLIC, 207 privacy: VideoPrivacy.PUBLIC,
216 targetUrl: getGoodVideoUrl() 208 targetUrl: FIXTURE_URLS.goodVideo
217 } 209 }
218 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 210 const { video } = await servers[0].imports.importVideo({ attributes })
219 const uuid = res.body.video.uuid
220 211
221 await waitJobs(servers) 212 await waitJobs(servers)
222 213
223 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 214 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'presence' })
224 }) 215 })
225 }) 216 })
226 217
@@ -239,10 +230,10 @@ describe('Test user notifications', function () {
239 it('Should not send a notification if transcoding is not enabled', async function () { 230 it('Should not send a notification if transcoding is not enabled', async function () {
240 this.timeout(50000) 231 this.timeout(50000)
241 232
242 const { name, uuid } = await uploadRandomVideoOnServers(servers, 1) 233 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1)
243 await waitJobs(servers) 234 await waitJobs(servers)
244 235
245 await checkVideoIsPublished(baseParams, name, uuid, 'absence') 236 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
246 }) 237 })
247 238
248 it('Should not send a notification if the wait transcoding is false', async function () { 239 it('Should not send a notification if the wait transcoding is false', async function () {
@@ -251,7 +242,7 @@ describe('Test user notifications', function () {
251 await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: false }) 242 await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: false })
252 await waitJobs(servers) 243 await waitJobs(servers)
253 244
254 const notification = await getLastNotification(servers[0].url, userAccessToken) 245 const notification = await servers[0].notifications.getLastest({ token: userAccessToken })
255 if (notification) { 246 if (notification) {
256 expect(notification.type).to.not.equal(UserNotificationType.MY_VIDEO_PUBLISHED) 247 expect(notification.type).to.not.equal(UserNotificationType.MY_VIDEO_PUBLISHED)
257 } 248 }
@@ -260,19 +251,19 @@ describe('Test user notifications', function () {
260 it('Should send a notification even if the video is not transcoded in other resolutions', async function () { 251 it('Should send a notification even if the video is not transcoded in other resolutions', async function () {
261 this.timeout(50000) 252 this.timeout(50000)
262 253
263 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true, fixture: 'video_short_240p.mp4' }) 254 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true, fixture: 'video_short_240p.mp4' })
264 await waitJobs(servers) 255 await waitJobs(servers)
265 256
266 await checkVideoIsPublished(baseParams, name, uuid, 'presence') 257 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
267 }) 258 })
268 259
269 it('Should send a notification with a transcoded video', async function () { 260 it('Should send a notification with a transcoded video', async function () {
270 this.timeout(50000) 261 this.timeout(50000)
271 262
272 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true }) 263 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true })
273 await waitJobs(servers) 264 await waitJobs(servers)
274 265
275 await checkVideoIsPublished(baseParams, name, uuid, 'presence') 266 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
276 }) 267 })
277 268
278 it('Should send a notification when an imported video is transcoded', async function () { 269 it('Should send a notification when an imported video is transcoded', async function () {
@@ -284,14 +275,13 @@ describe('Test user notifications', function () {
284 name, 275 name,
285 channelId, 276 channelId,
286 privacy: VideoPrivacy.PUBLIC, 277 privacy: VideoPrivacy.PUBLIC,
287 targetUrl: getGoodVideoUrl(), 278 targetUrl: FIXTURE_URLS.goodVideo,
288 waitTranscoding: true 279 waitTranscoding: true
289 } 280 }
290 const res = await importVideo(servers[1].url, servers[1].accessToken, attributes) 281 const { video } = await servers[1].imports.importVideo({ attributes })
291 const uuid = res.body.video.uuid
292 282
293 await waitJobs(servers) 283 await waitJobs(servers)
294 await checkVideoIsPublished(baseParams, name, uuid, 'presence') 284 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'presence' })
295 }) 285 })
296 286
297 it('Should send a notification when the scheduled update has been proceeded', async function () { 287 it('Should send a notification when the scheduled update has been proceeded', async function () {
@@ -304,13 +294,13 @@ describe('Test user notifications', function () {
304 privacy: VideoPrivacy.PRIVATE, 294 privacy: VideoPrivacy.PRIVATE,
305 scheduleUpdate: { 295 scheduleUpdate: {
306 updateAt: updateAt.toISOString(), 296 updateAt: updateAt.toISOString(),
307 privacy: VideoPrivacy.PUBLIC 297 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
308 } 298 }
309 } 299 }
310 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, data) 300 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
311 301
312 await wait(6000) 302 await wait(6000)
313 await checkVideoIsPublished(baseParams, name, uuid, 'presence') 303 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
314 }) 304 })
315 305
316 it('Should not send a notification before the video is published', async function () { 306 it('Should not send a notification before the video is published', async function () {
@@ -322,13 +312,13 @@ describe('Test user notifications', function () {
322 privacy: VideoPrivacy.PRIVATE, 312 privacy: VideoPrivacy.PRIVATE,
323 scheduleUpdate: { 313 scheduleUpdate: {
324 updateAt: updateAt.toISOString(), 314 updateAt: updateAt.toISOString(),
325 privacy: VideoPrivacy.PUBLIC 315 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
326 } 316 }
327 } 317 }
328 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, data) 318 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
329 319
330 await wait(6000) 320 await wait(6000)
331 await checkVideoIsPublished(baseParams, name, uuid, 'absence') 321 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
332 }) 322 })
333 }) 323 })
334 324
@@ -353,13 +343,14 @@ describe('Test user notifications', function () {
353 name, 343 name,
354 channelId, 344 channelId,
355 privacy: VideoPrivacy.PRIVATE, 345 privacy: VideoPrivacy.PRIVATE,
356 targetUrl: getBadVideoUrl() 346 targetUrl: FIXTURE_URLS.badVideo
357 } 347 }
358 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 348 const { video: { shortUUID } } = await servers[0].imports.importVideo({ attributes })
359 const uuid = res.body.video.uuid
360 349
361 await waitJobs(servers) 350 await waitJobs(servers)
362 await checkMyVideoImportIsFinished(baseParams, name, uuid, getBadVideoUrl(), false, 'presence') 351
352 const url = FIXTURE_URLS.badVideo
353 await checkMyVideoImportIsFinished({ ...baseParams, videoName: name, shortUUID, url, success: false, checkType: 'presence' })
363 }) 354 })
364 355
365 it('Should send a notification when the video import succeeded', async function () { 356 it('Should send a notification when the video import succeeded', async function () {
@@ -371,13 +362,14 @@ describe('Test user notifications', function () {
371 name, 362 name,
372 channelId, 363 channelId,
373 privacy: VideoPrivacy.PRIVATE, 364 privacy: VideoPrivacy.PRIVATE,
374 targetUrl: getGoodVideoUrl() 365 targetUrl: FIXTURE_URLS.goodVideo
375 } 366 }
376 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 367 const { video: { shortUUID } } = await servers[0].imports.importVideo({ attributes })
377 const uuid = res.body.video.uuid
378 368
379 await waitJobs(servers) 369 await waitJobs(servers)
380 await checkMyVideoImportIsFinished(baseParams, name, uuid, getGoodVideoUrl(), true, 'presence') 370
371 const url = FIXTURE_URLS.goodVideo
372 await checkMyVideoImportIsFinished({ ...baseParams, videoName: name, shortUUID, url, success: true, checkType: 'presence' })
381 }) 373 })
382 }) 374 })
383 375
@@ -394,47 +386,56 @@ describe('Test user notifications', function () {
394 token: userAccessToken 386 token: userAccessToken
395 } 387 }
396 388
397 await updateMyUser({ 389 await servers[0].users.updateMe({ displayName: 'super root name' })
398 url: servers[0].url,
399 accessToken: servers[0].accessToken,
400 displayName: 'super root name'
401 })
402 390
403 await updateMyUser({ 391 await servers[0].users.updateMe({
404 url: servers[0].url, 392 token: userAccessToken,
405 accessToken: userAccessToken,
406 displayName: myUserName 393 displayName: myUserName
407 }) 394 })
408 395
409 await updateMyUser({ 396 await servers[1].users.updateMe({ displayName: 'super root 2 name' })
410 url: servers[1].url,
411 accessToken: servers[1].accessToken,
412 displayName: 'super root 2 name'
413 })
414 397
415 await updateVideoChannel(servers[0].url, userAccessToken, 'user_1_channel', { displayName: myChannelName }) 398 await servers[0].channels.update({
399 token: userAccessToken,
400 channelName: 'user_1_channel',
401 attributes: { displayName: myChannelName }
402 })
416 }) 403 })
417 404
418 it('Should notify when a local channel is following one of our channel', async function () { 405 it('Should notify when a local channel is following one of our channel', async function () {
419 this.timeout(50000) 406 this.timeout(50000)
420 407
421 await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) 408 await servers[0].subscriptions.add({ targetUri: 'user_1_channel@localhost:' + servers[0].port })
422 await waitJobs(servers) 409 await waitJobs(servers)
423 410
424 await checkNewActorFollow(baseParams, 'channel', 'root', 'super root name', myChannelName, 'presence') 411 await checkNewActorFollow({
412 ...baseParams,
413 followType: 'channel',
414 followerName: 'root',
415 followerDisplayName: 'super root name',
416 followingDisplayName: myChannelName,
417 checkType: 'presence'
418 })
425 419
426 await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) 420 await servers[0].subscriptions.remove({ uri: 'user_1_channel@localhost:' + servers[0].port })
427 }) 421 })
428 422
429 it('Should notify when a remote channel is following one of our channel', async function () { 423 it('Should notify when a remote channel is following one of our channel', async function () {
430 this.timeout(50000) 424 this.timeout(50000)
431 425
432 await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) 426 await servers[1].subscriptions.add({ targetUri: 'user_1_channel@localhost:' + servers[0].port })
433 await waitJobs(servers) 427 await waitJobs(servers)
434 428
435 await checkNewActorFollow(baseParams, 'channel', 'root', 'super root 2 name', myChannelName, 'presence') 429 await checkNewActorFollow({
430 ...baseParams,
431 followType: 'channel',
432 followerName: 'root',
433 followerDisplayName: 'super root 2 name',
434 followingDisplayName: myChannelName,
435 checkType: 'presence'
436 })
436 437
437 await removeUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) 438 await servers[1].subscriptions.remove({ uri: 'user_1_channel@localhost:' + servers[0].port })
438 }) 439 })
439 440
440 // PeerTube does not support accout -> account follows 441 // PeerTube does not support accout -> account follows
diff --git a/server/tests/api/redundancy/manage-redundancy.ts b/server/tests/api/redundancy/manage-redundancy.ts
index 4253124c8..5fd464ded 100644
--- a/server/tests/api/redundancy/manage-redundancy.ts
+++ b/server/tests/api/redundancy/manage-redundancy.ts
@@ -1,32 +1,30 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 createMultipleServers,
7 doubleFollow, 8 doubleFollow,
8 flushAndRunMultipleServers, 9 PeerTubeServer,
9 getLocalIdByUUID, 10 RedundancyCommand,
10 ServerInfo,
11 setAccessTokensToServers, 11 setAccessTokensToServers,
12 uploadVideo, 12 waitJobs
13 uploadVideoAndGetId, 13} from '@shared/extra-utils'
14 waitUntilLog 14import { VideoPrivacy, VideoRedundanciesTarget } from '@shared/models'
15} from '../../../../shared/extra-utils'
16import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
17import { addVideoRedundancy, listVideoRedundancies, removeVideoRedundancy, updateRedundancy } from '@shared/extra-utils/server/redundancy'
18import { VideoPrivacy, VideoRedundanciesTarget, VideoRedundancy } from '@shared/models'
19 15
20const expect = chai.expect 16const expect = chai.expect
21 17
22describe('Test manage videos redundancy', function () { 18describe('Test manage videos redundancy', function () {
23 const targets: VideoRedundanciesTarget[] = [ 'my-videos', 'remote-videos' ] 19 const targets: VideoRedundanciesTarget[] = [ 'my-videos', 'remote-videos' ]
24 20
25 let servers: ServerInfo[] 21 let servers: PeerTubeServer[]
26 let video1Server2UUID: string 22 let video1Server2UUID: string
27 let video2Server2UUID: string 23 let video2Server2UUID: string
28 let redundanciesToRemove: number[] = [] 24 let redundanciesToRemove: number[] = []
29 25
26 let commands: RedundancyCommand[]
27
30 before(async function () { 28 before(async function () {
31 this.timeout(120000) 29 this.timeout(120000)
32 30
@@ -50,40 +48,38 @@ describe('Test manage videos redundancy', function () {
50 } 48 }
51 } 49 }
52 } 50 }
53 servers = await flushAndRunMultipleServers(3, config) 51 servers = await createMultipleServers(3, config)
54 52
55 // Get the access tokens 53 // Get the access tokens
56 await setAccessTokensToServers(servers) 54 await setAccessTokensToServers(servers)
57 55
56 commands = servers.map(s => s.redundancy)
57
58 { 58 {
59 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 1 server 2' }) 59 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video 1 server 2' } })
60 video1Server2UUID = res.body.video.uuid 60 video1Server2UUID = uuid
61 } 61 }
62 62
63 { 63 {
64 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 2 server 2' }) 64 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video 2 server 2' } })
65 video2Server2UUID = res.body.video.uuid 65 video2Server2UUID = uuid
66 } 66 }
67 67
68 await waitJobs(servers) 68 await waitJobs(servers)
69 69
70 // Server 1 and server 2 follow each other 70 // Server 1 and server 2 follow each other
71 await doubleFollow(servers[0], servers[1]) 71 await doubleFollow(servers[0], servers[1])
72 await updateRedundancy(servers[0].url, servers[0].accessToken, servers[1].host, true) 72 await commands[0].updateRedundancy({ host: servers[1].host, redundancyAllowed: true })
73 73
74 await waitJobs(servers) 74 await waitJobs(servers)
75 }) 75 })
76 76
77 it('Should not have redundancies on server 3', async function () { 77 it('Should not have redundancies on server 3', async function () {
78 for (const target of targets) { 78 for (const target of targets) {
79 const res = await listVideoRedundancies({ 79 const body = await commands[2].listVideos({ target })
80 url: servers[2].url,
81 accessToken: servers[2].accessToken,
82 target
83 })
84 80
85 expect(res.body.total).to.equal(0) 81 expect(body.total).to.equal(0)
86 expect(res.body.data).to.have.lengthOf(0) 82 expect(body.data).to.have.lengthOf(0)
87 } 83 }
88 }) 84 })
89 85
@@ -91,31 +87,22 @@ describe('Test manage videos redundancy', function () {
91 this.timeout(120000) 87 this.timeout(120000)
92 88
93 await waitJobs(servers) 89 await waitJobs(servers)
94 await waitUntilLog(servers[0], 'Duplicated ', 10) 90 await servers[0].servers.waitUntilLog('Duplicated ', 10)
95 await waitJobs(servers) 91 await waitJobs(servers)
96 92
97 const res = await listVideoRedundancies({ 93 const body = await commands[1].listVideos({ target: 'remote-videos' })
98 url: servers[1].url,
99 accessToken: servers[1].accessToken,
100 target: 'remote-videos'
101 })
102 94
103 expect(res.body.total).to.equal(0) 95 expect(body.total).to.equal(0)
104 expect(res.body.data).to.have.lengthOf(0) 96 expect(body.data).to.have.lengthOf(0)
105 }) 97 })
106 98
107 it('Should have "my-videos" redundancies on server 2', async function () { 99 it('Should have "my-videos" redundancies on server 2', async function () {
108 this.timeout(120000) 100 this.timeout(120000)
109 101
110 const res = await listVideoRedundancies({ 102 const body = await commands[1].listVideos({ target: 'my-videos' })
111 url: servers[1].url, 103 expect(body.total).to.equal(2)
112 accessToken: servers[1].accessToken,
113 target: 'my-videos'
114 })
115
116 expect(res.body.total).to.equal(2)
117 104
118 const videos = res.body.data as VideoRedundancy[] 105 const videos = body.data
119 expect(videos).to.have.lengthOf(2) 106 expect(videos).to.have.lengthOf(2)
120 107
121 const videos1 = videos.find(v => v.uuid === video1Server2UUID) 108 const videos1 = videos.find(v => v.uuid === video1Server2UUID)
@@ -139,28 +126,19 @@ describe('Test manage videos redundancy', function () {
139 }) 126 })
140 127
141 it('Should not have "my-videos" redundancies on server 1', async function () { 128 it('Should not have "my-videos" redundancies on server 1', async function () {
142 const res = await listVideoRedundancies({ 129 const body = await commands[0].listVideos({ target: 'my-videos' })
143 url: servers[0].url,
144 accessToken: servers[0].accessToken,
145 target: 'my-videos'
146 })
147 130
148 expect(res.body.total).to.equal(0) 131 expect(body.total).to.equal(0)
149 expect(res.body.data).to.have.lengthOf(0) 132 expect(body.data).to.have.lengthOf(0)
150 }) 133 })
151 134
152 it('Should have "remote-videos" redundancies on server 1', async function () { 135 it('Should have "remote-videos" redundancies on server 1', async function () {
153 this.timeout(120000) 136 this.timeout(120000)
154 137
155 const res = await listVideoRedundancies({ 138 const body = await commands[0].listVideos({ target: 'remote-videos' })
156 url: servers[0].url, 139 expect(body.total).to.equal(2)
157 accessToken: servers[0].accessToken,
158 target: 'remote-videos'
159 })
160 140
161 expect(res.body.total).to.equal(2) 141 const videos = body.data
162
163 const videos = res.body.data as VideoRedundancy[]
164 expect(videos).to.have.lengthOf(2) 142 expect(videos).to.have.lengthOf(2)
165 143
166 const videos1 = videos.find(v => v.uuid === video1Server2UUID) 144 const videos1 = videos.find(v => v.uuid === video1Server2UUID)
@@ -185,81 +163,67 @@ describe('Test manage videos redundancy', function () {
185 163
186 it('Should correctly paginate and sort results', async function () { 164 it('Should correctly paginate and sort results', async function () {
187 { 165 {
188 const res = await listVideoRedundancies({ 166 const body = await commands[0].listVideos({
189 url: servers[0].url,
190 accessToken: servers[0].accessToken,
191 target: 'remote-videos', 167 target: 'remote-videos',
192 sort: 'name', 168 sort: 'name',
193 start: 0, 169 start: 0,
194 count: 2 170 count: 2
195 }) 171 })
196 172
197 const videos = res.body.data 173 const videos = body.data
198 expect(videos[0].name).to.equal('video 1 server 2') 174 expect(videos[0].name).to.equal('video 1 server 2')
199 expect(videos[1].name).to.equal('video 2 server 2') 175 expect(videos[1].name).to.equal('video 2 server 2')
200 } 176 }
201 177
202 { 178 {
203 const res = await listVideoRedundancies({ 179 const body = await commands[0].listVideos({
204 url: servers[0].url,
205 accessToken: servers[0].accessToken,
206 target: 'remote-videos', 180 target: 'remote-videos',
207 sort: '-name', 181 sort: '-name',
208 start: 0, 182 start: 0,
209 count: 2 183 count: 2
210 }) 184 })
211 185
212 const videos = res.body.data 186 const videos = body.data
213 expect(videos[0].name).to.equal('video 2 server 2') 187 expect(videos[0].name).to.equal('video 2 server 2')
214 expect(videos[1].name).to.equal('video 1 server 2') 188 expect(videos[1].name).to.equal('video 1 server 2')
215 } 189 }
216 190
217 { 191 {
218 const res = await listVideoRedundancies({ 192 const body = await commands[0].listVideos({
219 url: servers[0].url,
220 accessToken: servers[0].accessToken,
221 target: 'remote-videos', 193 target: 'remote-videos',
222 sort: '-name', 194 sort: '-name',
223 start: 1, 195 start: 1,
224 count: 1 196 count: 1
225 }) 197 })
226 198
227 const videos = res.body.data 199 expect(body.data[0].name).to.equal('video 1 server 2')
228 expect(videos[0].name).to.equal('video 1 server 2')
229 } 200 }
230 }) 201 })
231 202
232 it('Should manually add a redundancy and list it', async function () { 203 it('Should manually add a redundancy and list it', async function () {
233 this.timeout(120000) 204 this.timeout(120000)
234 205
235 const uuid = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 3 server 2', privacy: VideoPrivacy.UNLISTED })).uuid 206 const uuid = (await servers[1].videos.quickUpload({ name: 'video 3 server 2', privacy: VideoPrivacy.UNLISTED })).uuid
236 await waitJobs(servers) 207 await waitJobs(servers)
237 const videoId = await getLocalIdByUUID(servers[0].url, uuid) 208 const videoId = await servers[0].videos.getId({ uuid })
238 209
239 await addVideoRedundancy({ 210 await commands[0].addVideo({ videoId })
240 url: servers[0].url,
241 accessToken: servers[0].accessToken,
242 videoId
243 })
244 211
245 await waitJobs(servers) 212 await waitJobs(servers)
246 await waitUntilLog(servers[0], 'Duplicated ', 15) 213 await servers[0].servers.waitUntilLog('Duplicated ', 15)
247 await waitJobs(servers) 214 await waitJobs(servers)
248 215
249 { 216 {
250 const res = await listVideoRedundancies({ 217 const body = await commands[0].listVideos({
251 url: servers[0].url,
252 accessToken: servers[0].accessToken,
253 target: 'remote-videos', 218 target: 'remote-videos',
254 sort: '-name', 219 sort: '-name',
255 start: 0, 220 start: 0,
256 count: 5 221 count: 5
257 }) 222 })
258 223
259 const videos = res.body.data 224 const video = body.data[0]
260 expect(videos[0].name).to.equal('video 3 server 2')
261 225
262 const video = videos[0] 226 expect(video.name).to.equal('video 3 server 2')
263 expect(video.redundancies.files).to.have.lengthOf(4) 227 expect(video.redundancies.files).to.have.lengthOf(4)
264 expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1) 228 expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1)
265 229
@@ -276,19 +240,15 @@ describe('Test manage videos redundancy', function () {
276 } 240 }
277 } 241 }
278 242
279 const res = await listVideoRedundancies({ 243 const body = await commands[1].listVideos({
280 url: servers[1].url,
281 accessToken: servers[1].accessToken,
282 target: 'my-videos', 244 target: 'my-videos',
283 sort: '-name', 245 sort: '-name',
284 start: 0, 246 start: 0,
285 count: 5 247 count: 5
286 }) 248 })
287 249
288 const videos = res.body.data 250 const video = body.data[0]
289 expect(videos[0].name).to.equal('video 3 server 2') 251 expect(video.name).to.equal('video 3 server 2')
290
291 const video = videos[0]
292 expect(video.redundancies.files).to.have.lengthOf(4) 252 expect(video.redundancies.files).to.have.lengthOf(4)
293 expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1) 253 expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1)
294 254
@@ -307,64 +267,47 @@ describe('Test manage videos redundancy', function () {
307 this.timeout(120000) 267 this.timeout(120000)
308 268
309 for (const redundancyId of redundanciesToRemove) { 269 for (const redundancyId of redundanciesToRemove) {
310 await removeVideoRedundancy({ 270 await commands[0].removeVideo({ redundancyId })
311 url: servers[0].url,
312 accessToken: servers[0].accessToken,
313 redundancyId
314 })
315 } 271 }
316 272
317 { 273 {
318 const res = await listVideoRedundancies({ 274 const body = await commands[0].listVideos({
319 url: servers[0].url,
320 accessToken: servers[0].accessToken,
321 target: 'remote-videos', 275 target: 'remote-videos',
322 sort: '-name', 276 sort: '-name',
323 start: 0, 277 start: 0,
324 count: 5 278 count: 5
325 }) 279 })
326 280
327 const videos = res.body.data 281 const videos = body.data
328 expect(videos).to.have.lengthOf(2)
329 282
330 expect(videos[0].name).to.equal('video 2 server 2') 283 expect(videos).to.have.lengthOf(2)
331 284
332 redundanciesToRemove = []
333 const video = videos[0] 285 const video = videos[0]
286 expect(video.name).to.equal('video 2 server 2')
334 expect(video.redundancies.files).to.have.lengthOf(4) 287 expect(video.redundancies.files).to.have.lengthOf(4)
335 expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1) 288 expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1)
336 289
337 const redundancies = video.redundancies.files.concat(video.redundancies.streamingPlaylists) 290 const redundancies = video.redundancies.files.concat(video.redundancies.streamingPlaylists)
338 291
339 for (const r of redundancies) { 292 redundanciesToRemove = redundancies.map(r => r.id)
340 redundanciesToRemove.push(r.id)
341 }
342 } 293 }
343 }) 294 })
344 295
345 it('Should remove another (auto) redundancy', async function () { 296 it('Should remove another (auto) redundancy', async function () {
346 { 297 for (const redundancyId of redundanciesToRemove) {
347 for (const redundancyId of redundanciesToRemove) { 298 await commands[0].removeVideo({ redundancyId })
348 await removeVideoRedundancy({ 299 }
349 url: servers[0].url,
350 accessToken: servers[0].accessToken,
351 redundancyId
352 })
353 }
354 300
355 const res = await listVideoRedundancies({ 301 const body = await commands[0].listVideos({
356 url: servers[0].url, 302 target: 'remote-videos',
357 accessToken: servers[0].accessToken, 303 sort: '-name',
358 target: 'remote-videos', 304 start: 0,
359 sort: '-name', 305 count: 5
360 start: 0, 306 })
361 count: 5
362 })
363 307
364 const videos = res.body.data 308 const videos = body.data
365 expect(videos[0].name).to.equal('video 1 server 2') 309 expect(videos).to.have.lengthOf(1)
366 expect(videos).to.have.lengthOf(1) 310 expect(videos[0].name).to.equal('video 1 server 2')
367 }
368 }) 311 })
369 312
370 after(async function () { 313 after(async function () {
diff --git a/server/tests/api/redundancy/redundancy-constraints.ts b/server/tests/api/redundancy/redundancy-constraints.ts
index 1cb1603bc..933a2c776 100644
--- a/server/tests/api/redundancy/redundancy-constraints.ts
+++ b/server/tests/api/redundancy/redundancy-constraints.ts
@@ -1,29 +1,14 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import { expect } from 'chai'
5import { listVideoRedundancies, updateRedundancy } from '@shared/extra-utils/server/redundancy' 5import { cleanupTests, createSingleServer, killallServers, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
6import { VideoPrivacy } from '@shared/models' 6import { VideoPrivacy } from '@shared/models'
7import {
8 cleanupTests,
9 flushAndRunServer,
10 follow,
11 killallServers,
12 reRunServer,
13 ServerInfo,
14 setAccessTokensToServers,
15 updateVideo,
16 uploadVideo,
17 waitUntilLog
18} from '../../../../shared/extra-utils'
19import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
20
21const expect = chai.expect
22 7
23describe('Test redundancy constraints', function () { 8describe('Test redundancy constraints', function () {
24 let remoteServer: ServerInfo 9 let remoteServer: PeerTubeServer
25 let localServer: ServerInfo 10 let localServer: PeerTubeServer
26 let servers: ServerInfo[] 11 let servers: PeerTubeServer[]
27 12
28 const remoteServerConfig = { 13 const remoteServerConfig = {
29 redundancy: { 14 redundancy: {
@@ -43,38 +28,30 @@ describe('Test redundancy constraints', function () {
43 28
44 async function uploadWrapper (videoName: string) { 29 async function uploadWrapper (videoName: string) {
45 // Wait for transcoding 30 // Wait for transcoding
46 const res = await uploadVideo(localServer.url, localServer.accessToken, { name: 'to transcode', privacy: VideoPrivacy.PRIVATE }) 31 const { id } = await localServer.videos.upload({ attributes: { name: 'to transcode', privacy: VideoPrivacy.PRIVATE } })
47 await waitJobs([ localServer ]) 32 await waitJobs([ localServer ])
48 33
49 // Update video to schedule a federation 34 // Update video to schedule a federation
50 await updateVideo(localServer.url, localServer.accessToken, res.body.video.id, { name: videoName, privacy: VideoPrivacy.PUBLIC }) 35 await localServer.videos.update({ id, attributes: { name: videoName, privacy: VideoPrivacy.PUBLIC } })
51 } 36 }
52 37
53 async function getTotalRedundanciesLocalServer () { 38 async function getTotalRedundanciesLocalServer () {
54 const res = await listVideoRedundancies({ 39 const body = await localServer.redundancy.listVideos({ target: 'my-videos' })
55 url: localServer.url,
56 accessToken: localServer.accessToken,
57 target: 'my-videos'
58 })
59 40
60 return res.body.total 41 return body.total
61 } 42 }
62 43
63 async function getTotalRedundanciesRemoteServer () { 44 async function getTotalRedundanciesRemoteServer () {
64 const res = await listVideoRedundancies({ 45 const body = await remoteServer.redundancy.listVideos({ target: 'remote-videos' })
65 url: remoteServer.url,
66 accessToken: remoteServer.accessToken,
67 target: 'remote-videos'
68 })
69 46
70 return res.body.total 47 return body.total
71 } 48 }
72 49
73 before(async function () { 50 before(async function () {
74 this.timeout(120000) 51 this.timeout(120000)
75 52
76 { 53 {
77 remoteServer = await flushAndRunServer(1, remoteServerConfig) 54 remoteServer = await createSingleServer(1, remoteServerConfig)
78 } 55 }
79 56
80 { 57 {
@@ -85,7 +62,7 @@ describe('Test redundancy constraints', function () {
85 } 62 }
86 } 63 }
87 } 64 }
88 localServer = await flushAndRunServer(2, config) 65 localServer = await createSingleServer(2, config)
89 } 66 }
90 67
91 servers = [ remoteServer, localServer ] 68 servers = [ remoteServer, localServer ]
@@ -93,14 +70,14 @@ describe('Test redundancy constraints', function () {
93 // Get the access tokens 70 // Get the access tokens
94 await setAccessTokensToServers(servers) 71 await setAccessTokensToServers(servers)
95 72
96 await uploadVideo(localServer.url, localServer.accessToken, { name: 'video 1 server 2' }) 73 await localServer.videos.upload({ attributes: { name: 'video 1 server 2' } })
97 74
98 await waitJobs(servers) 75 await waitJobs(servers)
99 76
100 // Server 1 and server 2 follow each other 77 // Server 1 and server 2 follow each other
101 await follow(remoteServer.url, [ localServer.url ], remoteServer.accessToken) 78 await remoteServer.follows.follow({ hosts: [ localServer.url ] })
102 await waitJobs(servers) 79 await waitJobs(servers)
103 await updateRedundancy(remoteServer.url, remoteServer.accessToken, localServer.host, true) 80 await remoteServer.redundancy.updateRedundancy({ host: localServer.host, redundancyAllowed: true })
104 81
105 await waitJobs(servers) 82 await waitJobs(servers)
106 }) 83 })
@@ -109,7 +86,7 @@ describe('Test redundancy constraints', function () {
109 this.timeout(120000) 86 this.timeout(120000)
110 87
111 await waitJobs(servers) 88 await waitJobs(servers)
112 await waitUntilLog(remoteServer, 'Duplicated ', 5) 89 await remoteServer.servers.waitUntilLog('Duplicated ', 5)
113 await waitJobs(servers) 90 await waitJobs(servers)
114 91
115 { 92 {
@@ -134,11 +111,11 @@ describe('Test redundancy constraints', function () {
134 } 111 }
135 } 112 }
136 await killallServers([ localServer ]) 113 await killallServers([ localServer ])
137 await reRunServer(localServer, config) 114 await localServer.run(config)
138 115
139 await uploadWrapper('video 2 server 2') 116 await uploadWrapper('video 2 server 2')
140 117
141 await waitUntilLog(remoteServer, 'Duplicated ', 10) 118 await remoteServer.servers.waitUntilLog('Duplicated ', 10)
142 await waitJobs(servers) 119 await waitJobs(servers)
143 120
144 { 121 {
@@ -163,11 +140,11 @@ describe('Test redundancy constraints', function () {
163 } 140 }
164 } 141 }
165 await killallServers([ localServer ]) 142 await killallServers([ localServer ])
166 await reRunServer(localServer, config) 143 await localServer.run(config)
167 144
168 await uploadWrapper('video 3 server 2') 145 await uploadWrapper('video 3 server 2')
169 146
170 await waitUntilLog(remoteServer, 'Duplicated ', 15) 147 await remoteServer.servers.waitUntilLog('Duplicated ', 15)
171 await waitJobs(servers) 148 await waitJobs(servers)
172 149
173 { 150 {
@@ -184,11 +161,11 @@ describe('Test redundancy constraints', function () {
184 it('Should have redundancy on server 1 and on server 2 with followings filter now server 2 follows server 1', async function () { 161 it('Should have redundancy on server 1 and on server 2 with followings filter now server 2 follows server 1', async function () {
185 this.timeout(120000) 162 this.timeout(120000)
186 163
187 await follow(localServer.url, [ remoteServer.url ], localServer.accessToken) 164 await localServer.follows.follow({ hosts: [ remoteServer.url ] })
188 await waitJobs(servers) 165 await waitJobs(servers)
189 166
190 await uploadWrapper('video 4 server 2') 167 await uploadWrapper('video 4 server 2')
191 await waitUntilLog(remoteServer, 'Duplicated ', 20) 168 await remoteServer.servers.waitUntilLog('Duplicated ', 20)
192 await waitJobs(servers) 169 await waitJobs(servers)
193 170
194 { 171 {
diff --git a/server/tests/api/redundancy/redundancy.ts b/server/tests/api/redundancy/redundancy.ts
index 0e0a73b9d..e1a12f5f8 100644
--- a/server/tests/api/redundancy/redundancy.ts
+++ b/server/tests/api/redundancy/redundancy.ts
@@ -4,72 +4,63 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { readdir } from 'fs-extra' 5import { readdir } from 'fs-extra'
6import * as magnetUtil from 'magnet-uri' 6import * as magnetUtil from 'magnet-uri'
7import { join } from 'path' 7import { basename, join } from 'path'
8import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
9import { 8import {
10 checkSegmentHash, 9 checkSegmentHash,
11 checkVideoFilesWereRemoved, 10 checkVideoFilesWereRemoved,
12 cleanupTests, 11 cleanupTests,
12 createMultipleServers,
13 doubleFollow, 13 doubleFollow,
14 flushAndRunMultipleServers,
15 getFollowingListPaginationAndSort,
16 getVideo,
17 getVideoWithToken,
18 immutableAssign,
19 killallServers, 14 killallServers,
20 makeGetRequest, 15 makeRawRequest,
21 removeVideo, 16 PeerTubeServer,
22 reRunServer,
23 root, 17 root,
24 ServerInfo, 18 saveVideoInServers,
25 setAccessTokensToServers, 19 setAccessTokensToServers,
26 unfollow,
27 updateVideo,
28 uploadVideo,
29 viewVideo,
30 wait, 20 wait,
31 waitUntilLog 21 waitJobs
32} from '../../../../shared/extra-utils' 22} from '@shared/extra-utils'
33import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
34import { 23import {
35 addVideoRedundancy, 24 HttpStatusCode,
36 listVideoRedundancies, 25 VideoDetails,
37 removeVideoRedundancy, 26 VideoFile,
38 updateRedundancy 27 VideoPrivacy,
39} from '../../../../shared/extra-utils/server/redundancy' 28 VideoRedundancyStrategy,
40import { getStats } from '../../../../shared/extra-utils/server/stats' 29 VideoRedundancyStrategyWithManual
41import { ActorFollow } from '../../../../shared/models/actors' 30} from '@shared/models'
42import { VideoRedundancy, VideoRedundancyStrategy, VideoRedundancyStrategyWithManual } from '../../../../shared/models/redundancy'
43import { ServerStats } from '../../../../shared/models/server/server-stats.model'
44import { VideoDetails, VideoPrivacy } from '../../../../shared/models/videos'
45 31
46const expect = chai.expect 32const expect = chai.expect
47 33
48let servers: ServerInfo[] = [] 34let servers: PeerTubeServer[] = []
49let video1Server2UUID: string 35let video1Server2: VideoDetails
50let video1Server2Id: number
51 36
52function checkMagnetWebseeds (file: { magnetUri: string, resolution: { id: number } }, baseWebseeds: string[], server: ServerInfo) { 37async function checkMagnetWebseeds (file: VideoFile, baseWebseeds: string[], server: PeerTubeServer) {
53 const parsed = magnetUtil.decode(file.magnetUri) 38 const parsed = magnetUtil.decode(file.magnetUri)
54 39
55 for (const ws of baseWebseeds) { 40 for (const ws of baseWebseeds) {
56 const found = parsed.urlList.find(url => url === `${ws}-${file.resolution.id}.mp4`) 41 const found = parsed.urlList.find(url => url === `${ws}${basename(file.fileUrl)}`)
57 expect(found, `Webseed ${ws} not found in ${file.magnetUri} on server ${server.url}`).to.not.be.undefined 42 expect(found, `Webseed ${ws} not found in ${file.magnetUri} on server ${server.url}`).to.not.be.undefined
58 } 43 }
59 44
60 expect(parsed.urlList).to.have.lengthOf(baseWebseeds.length) 45 expect(parsed.urlList).to.have.lengthOf(baseWebseeds.length)
46
47 for (const url of parsed.urlList) {
48 await makeRawRequest(url, HttpStatusCode.OK_200)
49 }
61} 50}
62 51
63async function flushAndRunServers (strategy: VideoRedundancyStrategy | null, additionalParams: any = {}, withWebtorrent = true) { 52async function createSingleServers (strategy: VideoRedundancyStrategy | null, additionalParams: any = {}, withWebtorrent = true) {
64 const strategies: any[] = [] 53 const strategies: any[] = []
65 54
66 if (strategy !== null) { 55 if (strategy !== null) {
67 strategies.push( 56 strategies.push(
68 immutableAssign({ 57 {
69 min_lifetime: '1 hour', 58 min_lifetime: '1 hour',
70 strategy: strategy, 59 strategy: strategy,
71 size: '400KB' 60 size: '400KB',
72 }, additionalParams) 61
62 ...additionalParams
63 }
73 ) 64 )
74 } 65 }
75 66
@@ -90,17 +81,16 @@ async function flushAndRunServers (strategy: VideoRedundancyStrategy | null, add
90 } 81 }
91 } 82 }
92 83
93 servers = await flushAndRunMultipleServers(3, config) 84 servers = await createMultipleServers(3, config)
94 85
95 // Get the access tokens 86 // Get the access tokens
96 await setAccessTokensToServers(servers) 87 await setAccessTokensToServers(servers)
97 88
98 { 89 {
99 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 1 server 2' }) 90 const { id } = await servers[1].videos.upload({ attributes: { name: 'video 1 server 2' } })
100 video1Server2UUID = res.body.video.uuid 91 video1Server2 = await servers[1].videos.get({ id })
101 video1Server2Id = res.body.video.id
102 92
103 await viewVideo(servers[1].url, video1Server2UUID) 93 await servers[1].videos.view({ id })
104 } 94 }
105 95
106 await waitJobs(servers) 96 await waitJobs(servers)
@@ -115,55 +105,65 @@ async function flushAndRunServers (strategy: VideoRedundancyStrategy | null, add
115 await waitJobs(servers) 105 await waitJobs(servers)
116} 106}
117 107
108async function ensureSameFilenames (videoUUID: string) {
109 let webtorrentFilenames: string[]
110 let hlsFilenames: string[]
111
112 for (const server of servers) {
113 const video = await server.videos.getWithToken({ id: videoUUID })
114
115 // Ensure we use the same filenames that the origin
116
117 const localWebtorrentFilenames = video.files.map(f => basename(f.fileUrl)).sort()
118 const localHLSFilenames = video.streamingPlaylists[0].files.map(f => basename(f.fileUrl)).sort()
119
120 if (webtorrentFilenames) expect(webtorrentFilenames).to.deep.equal(localWebtorrentFilenames)
121 else webtorrentFilenames = localWebtorrentFilenames
122
123 if (hlsFilenames) expect(hlsFilenames).to.deep.equal(localHLSFilenames)
124 else hlsFilenames = localHLSFilenames
125 }
126
127 return { webtorrentFilenames, hlsFilenames }
128}
129
118async function check1WebSeed (videoUUID?: string) { 130async function check1WebSeed (videoUUID?: string) {
119 if (!videoUUID) videoUUID = video1Server2UUID 131 if (!videoUUID) videoUUID = video1Server2.uuid
120 132
121 const webseeds = [ 133 const webseeds = [
122 `http://localhost:${servers[1].port}/static/webseed/${videoUUID}` 134 `http://localhost:${servers[1].port}/static/webseed/`
123 ] 135 ]
124 136
125 for (const server of servers) { 137 for (const server of servers) {
126 // With token to avoid issues with video follow constraints 138 // With token to avoid issues with video follow constraints
127 const res = await getVideoWithToken(server.url, server.accessToken, videoUUID) 139 const video = await server.videos.getWithToken({ id: videoUUID })
128 140
129 const video: VideoDetails = res.body
130 for (const f of video.files) { 141 for (const f of video.files) {
131 checkMagnetWebseeds(f, webseeds, server) 142 await checkMagnetWebseeds(f, webseeds, server)
132 } 143 }
133 } 144 }
145
146 await ensureSameFilenames(videoUUID)
134} 147}
135 148
136async function check2Webseeds (videoUUID?: string) { 149async function check2Webseeds (videoUUID?: string) {
137 if (!videoUUID) videoUUID = video1Server2UUID 150 if (!videoUUID) videoUUID = video1Server2.uuid
138 151
139 const webseeds = [ 152 const webseeds = [
140 `http://localhost:${servers[0].port}/static/redundancy/${videoUUID}`, 153 `http://localhost:${servers[0].port}/static/redundancy/`,
141 `http://localhost:${servers[1].port}/static/webseed/${videoUUID}` 154 `http://localhost:${servers[1].port}/static/webseed/`
142 ] 155 ]
143 156
144 for (const server of servers) { 157 for (const server of servers) {
145 const res = await getVideo(server.url, videoUUID) 158 const video = await server.videos.get({ id: videoUUID })
146
147 const video: VideoDetails = res.body
148 159
149 for (const file of video.files) { 160 for (const file of video.files) {
150 checkMagnetWebseeds(file, webseeds, server) 161 await checkMagnetWebseeds(file, webseeds, server)
151
152 await makeGetRequest({
153 url: servers[0].url,
154 statusCodeExpected: HttpStatusCode.OK_200,
155 path: '/static/redundancy/' + `${videoUUID}-${file.resolution.id}.mp4`,
156 contentType: null
157 })
158 await makeGetRequest({
159 url: servers[1].url,
160 statusCodeExpected: HttpStatusCode.OK_200,
161 path: `/static/webseed/${videoUUID}-${file.resolution.id}.mp4`,
162 contentType: null
163 })
164 } 162 }
165 } 163 }
166 164
165 const { webtorrentFilenames } = await ensureSameFilenames(videoUUID)
166
167 const directories = [ 167 const directories = [
168 'test' + servers[0].internalServerNumber + '/redundancy', 168 'test' + servers[0].internalServerNumber + '/redundancy',
169 'test' + servers[1].internalServerNumber + '/videos' 169 'test' + servers[1].internalServerNumber + '/videos'
@@ -173,32 +173,31 @@ async function check2Webseeds (videoUUID?: string) {
173 const files = await readdir(join(root(), directory)) 173 const files = await readdir(join(root(), directory))
174 expect(files).to.have.length.at.least(4) 174 expect(files).to.have.length.at.least(4)
175 175
176 for (const resolution of [ 240, 360, 480, 720 ]) { 176 // Ensure we files exist on disk
177 expect(files.find(f => f === `${videoUUID}-${resolution}.mp4`)).to.not.be.undefined 177 expect(files.find(f => webtorrentFilenames.includes(f))).to.exist
178 }
179 } 178 }
180} 179}
181 180
182async function check0PlaylistRedundancies (videoUUID?: string) { 181async function check0PlaylistRedundancies (videoUUID?: string) {
183 if (!videoUUID) videoUUID = video1Server2UUID 182 if (!videoUUID) videoUUID = video1Server2.uuid
184 183
185 for (const server of servers) { 184 for (const server of servers) {
186 // With token to avoid issues with video follow constraints 185 // With token to avoid issues with video follow constraints
187 const res = await getVideoWithToken(server.url, server.accessToken, videoUUID) 186 const video = await server.videos.getWithToken({ id: videoUUID })
188 const video: VideoDetails = res.body
189 187
190 expect(video.streamingPlaylists).to.be.an('array') 188 expect(video.streamingPlaylists).to.be.an('array')
191 expect(video.streamingPlaylists).to.have.lengthOf(1) 189 expect(video.streamingPlaylists).to.have.lengthOf(1)
192 expect(video.streamingPlaylists[0].redundancies).to.have.lengthOf(0) 190 expect(video.streamingPlaylists[0].redundancies).to.have.lengthOf(0)
193 } 191 }
192
193 await ensureSameFilenames(videoUUID)
194} 194}
195 195
196async function check1PlaylistRedundancies (videoUUID?: string) { 196async function check1PlaylistRedundancies (videoUUID?: string) {
197 if (!videoUUID) videoUUID = video1Server2UUID 197 if (!videoUUID) videoUUID = video1Server2.uuid
198 198
199 for (const server of servers) { 199 for (const server of servers) {
200 const res = await getVideo(server.url, videoUUID) 200 const video = await server.videos.get({ id: videoUUID })
201 const video: VideoDetails = res.body
202 201
203 expect(video.streamingPlaylists).to.have.lengthOf(1) 202 expect(video.streamingPlaylists).to.have.lengthOf(1)
204 expect(video.streamingPlaylists[0].redundancies).to.have.lengthOf(1) 203 expect(video.streamingPlaylists[0].redundancies).to.have.lengthOf(1)
@@ -211,13 +210,15 @@ async function check1PlaylistRedundancies (videoUUID?: string) {
211 const baseUrlPlaylist = servers[1].url + '/static/streaming-playlists/hls' 210 const baseUrlPlaylist = servers[1].url + '/static/streaming-playlists/hls'
212 const baseUrlSegment = servers[0].url + '/static/redundancy/hls' 211 const baseUrlSegment = servers[0].url + '/static/redundancy/hls'
213 212
214 const res = await getVideo(servers[0].url, videoUUID) 213 const video = await servers[0].videos.get({ id: videoUUID })
215 const hlsPlaylist = (res.body as VideoDetails).streamingPlaylists[0] 214 const hlsPlaylist = video.streamingPlaylists[0]
216 215
217 for (const resolution of [ 240, 360, 480, 720 ]) { 216 for (const resolution of [ 240, 360, 480, 720 ]) {
218 await checkSegmentHash(baseUrlPlaylist, baseUrlSegment, videoUUID, resolution, hlsPlaylist) 217 await checkSegmentHash({ server: servers[1], baseUrlPlaylist, baseUrlSegment, videoUUID, resolution, hlsPlaylist })
219 } 218 }
220 219
220 const { hlsFilenames } = await ensureSameFilenames(videoUUID)
221
221 const directories = [ 222 const directories = [
222 'test' + servers[0].internalServerNumber + '/redundancy/hls', 223 'test' + servers[0].internalServerNumber + '/redundancy/hls',
223 'test' + servers[1].internalServerNumber + '/streaming-playlists/hls' 224 'test' + servers[1].internalServerNumber + '/streaming-playlists/hls'
@@ -227,11 +228,8 @@ async function check1PlaylistRedundancies (videoUUID?: string) {
227 const files = await readdir(join(root(), directory, videoUUID)) 228 const files = await readdir(join(root(), directory, videoUUID))
228 expect(files).to.have.length.at.least(4) 229 expect(files).to.have.length.at.least(4)
229 230
230 for (const resolution of [ 240, 360, 480, 720 ]) { 231 // Ensure we files exist on disk
231 const filename = `${videoUUID}-${resolution}-fragmented.mp4` 232 expect(files.find(f => hlsFilenames.includes(f))).to.exist
232
233 expect(files.find(f => f === filename)).to.not.be.undefined
234 }
235 } 233 }
236} 234}
237 235
@@ -244,9 +242,7 @@ async function checkStatsGlobal (strategy: VideoRedundancyStrategyWithManual) {
244 statsLength = 2 242 statsLength = 2
245 } 243 }
246 244
247 const res = await getStats(servers[0].url) 245 const data = await servers[0].stats.get()
248 const data: ServerStats = res.body
249
250 expect(data.videosRedundancy).to.have.lengthOf(statsLength) 246 expect(data.videosRedundancy).to.have.lengthOf(statsLength)
251 247
252 const stat = data.videosRedundancy[0] 248 const stat = data.videosRedundancy[0]
@@ -272,14 +268,20 @@ async function checkStatsWithoutRedundancy (strategy: VideoRedundancyStrategyWit
272 expect(stat.totalVideos).to.equal(0) 268 expect(stat.totalVideos).to.equal(0)
273} 269}
274 270
275async function enableRedundancyOnServer1 () { 271async function findServerFollows () {
276 await updateRedundancy(servers[0].url, servers[0].accessToken, servers[1].host, true) 272 const body = await servers[0].follows.getFollowings({ start: 0, count: 5, sort: '-createdAt' })
277 273 const follows = body.data
278 const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: '-createdAt' })
279 const follows: ActorFollow[] = res.body.data
280 const server2 = follows.find(f => f.following.host === `localhost:${servers[1].port}`) 274 const server2 = follows.find(f => f.following.host === `localhost:${servers[1].port}`)
281 const server3 = follows.find(f => f.following.host === `localhost:${servers[2].port}`) 275 const server3 = follows.find(f => f.following.host === `localhost:${servers[2].port}`)
282 276
277 return { server2, server3 }
278}
279
280async function enableRedundancyOnServer1 () {
281 await servers[0].redundancy.updateRedundancy({ host: servers[1].host, redundancyAllowed: true })
282
283 const { server2, server3 } = await findServerFollows()
284
283 expect(server3).to.not.be.undefined 285 expect(server3).to.not.be.undefined
284 expect(server3.following.hostRedundancyAllowed).to.be.false 286 expect(server3.following.hostRedundancyAllowed).to.be.false
285 287
@@ -288,12 +290,9 @@ async function enableRedundancyOnServer1 () {
288} 290}
289 291
290async function disableRedundancyOnServer1 () { 292async function disableRedundancyOnServer1 () {
291 await updateRedundancy(servers[0].url, servers[0].accessToken, servers[1].host, false) 293 await servers[0].redundancy.updateRedundancy({ host: servers[1].host, redundancyAllowed: false })
292 294
293 const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: '-createdAt' }) 295 const { server2, server3 } = await findServerFollows()
294 const follows: ActorFollow[] = res.body.data
295 const server2 = follows.find(f => f.following.host === `localhost:${servers[1].port}`)
296 const server3 = follows.find(f => f.following.host === `localhost:${servers[2].port}`)
297 296
298 expect(server3).to.not.be.undefined 297 expect(server3).to.not.be.undefined
299 expect(server3.following.hostRedundancyAllowed).to.be.false 298 expect(server3.following.hostRedundancyAllowed).to.be.false
@@ -310,7 +309,7 @@ describe('Test videos redundancy', function () {
310 before(function () { 309 before(function () {
311 this.timeout(120000) 310 this.timeout(120000)
312 311
313 return flushAndRunServers(strategy) 312 return createSingleServers(strategy)
314 }) 313 })
315 314
316 it('Should have 1 webseed on the first video', async function () { 315 it('Should have 1 webseed on the first video', async function () {
@@ -327,7 +326,7 @@ describe('Test videos redundancy', function () {
327 this.timeout(80000) 326 this.timeout(80000)
328 327
329 await waitJobs(servers) 328 await waitJobs(servers)
330 await waitUntilLog(servers[0], 'Duplicated ', 5) 329 await servers[0].servers.waitUntilLog('Duplicated ', 5)
331 await waitJobs(servers) 330 await waitJobs(servers)
332 331
333 await check2Webseeds() 332 await check2Webseeds()
@@ -346,7 +345,7 @@ describe('Test videos redundancy', function () {
346 await check1WebSeed() 345 await check1WebSeed()
347 await check0PlaylistRedundancies() 346 await check0PlaylistRedundancies()
348 347
349 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].internalServerNumber, [ 'videos', join('playlists', 'hls') ]) 348 await checkVideoFilesWereRemoved({ server: servers[0], video: video1Server2, onlyVideoFiles: true })
350 }) 349 })
351 350
352 after(async function () { 351 after(async function () {
@@ -360,7 +359,7 @@ describe('Test videos redundancy', function () {
360 before(function () { 359 before(function () {
361 this.timeout(120000) 360 this.timeout(120000)
362 361
363 return flushAndRunServers(strategy) 362 return createSingleServers(strategy)
364 }) 363 })
365 364
366 it('Should have 1 webseed on the first video', async function () { 365 it('Should have 1 webseed on the first video', async function () {
@@ -377,7 +376,7 @@ describe('Test videos redundancy', function () {
377 this.timeout(80000) 376 this.timeout(80000)
378 377
379 await waitJobs(servers) 378 await waitJobs(servers)
380 await waitUntilLog(servers[0], 'Duplicated ', 5) 379 await servers[0].servers.waitUntilLog('Duplicated ', 5)
381 await waitJobs(servers) 380 await waitJobs(servers)
382 381
383 await check2Webseeds() 382 await check2Webseeds()
@@ -388,7 +387,7 @@ describe('Test videos redundancy', function () {
388 it('Should unfollow on server 1 and remove duplicated videos', async function () { 387 it('Should unfollow on server 1 and remove duplicated videos', async function () {
389 this.timeout(80000) 388 this.timeout(80000)
390 389
391 await unfollow(servers[0].url, servers[0].accessToken, servers[1]) 390 await servers[0].follows.unfollow({ target: servers[1] })
392 391
393 await waitJobs(servers) 392 await waitJobs(servers)
394 await wait(5000) 393 await wait(5000)
@@ -396,7 +395,7 @@ describe('Test videos redundancy', function () {
396 await check1WebSeed() 395 await check1WebSeed()
397 await check0PlaylistRedundancies() 396 await check0PlaylistRedundancies()
398 397
399 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].internalServerNumber, [ 'videos' ]) 398 await checkVideoFilesWereRemoved({ server: servers[0], video: video1Server2, onlyVideoFiles: true })
400 }) 399 })
401 400
402 after(async function () { 401 after(async function () {
@@ -410,7 +409,7 @@ describe('Test videos redundancy', function () {
410 before(function () { 409 before(function () {
411 this.timeout(120000) 410 this.timeout(120000)
412 411
413 return flushAndRunServers(strategy, { min_views: 3 }) 412 return createSingleServers(strategy, { min_views: 3 })
414 }) 413 })
415 414
416 it('Should have 1 webseed on the first video', async function () { 415 it('Should have 1 webseed on the first video', async function () {
@@ -438,8 +437,8 @@ describe('Test videos redundancy', function () {
438 it('Should view 2 times the first video to have > min_views config', async function () { 437 it('Should view 2 times the first video to have > min_views config', async function () {
439 this.timeout(80000) 438 this.timeout(80000)
440 439
441 await viewVideo(servers[0].url, video1Server2UUID) 440 await servers[0].videos.view({ id: video1Server2.uuid })
442 await viewVideo(servers[2].url, video1Server2UUID) 441 await servers[2].videos.view({ id: video1Server2.uuid })
443 442
444 await wait(10000) 443 await wait(10000)
445 await waitJobs(servers) 444 await waitJobs(servers)
@@ -449,7 +448,7 @@ describe('Test videos redundancy', function () {
449 this.timeout(80000) 448 this.timeout(80000)
450 449
451 await waitJobs(servers) 450 await waitJobs(servers)
452 await waitUntilLog(servers[0], 'Duplicated ', 5) 451 await servers[0].servers.waitUntilLog('Duplicated ', 5)
453 await waitJobs(servers) 452 await waitJobs(servers)
454 453
455 await check2Webseeds() 454 await check2Webseeds()
@@ -460,12 +459,13 @@ describe('Test videos redundancy', function () {
460 it('Should remove the video and the redundancy files', async function () { 459 it('Should remove the video and the redundancy files', async function () {
461 this.timeout(20000) 460 this.timeout(20000)
462 461
463 await removeVideo(servers[1].url, servers[1].accessToken, video1Server2UUID) 462 await saveVideoInServers(servers, video1Server2.uuid)
463 await servers[1].videos.remove({ id: video1Server2.uuid })
464 464
465 await waitJobs(servers) 465 await waitJobs(servers)
466 466
467 for (const server of servers) { 467 for (const server of servers) {
468 await checkVideoFilesWereRemoved(video1Server2UUID, server.internalServerNumber) 468 await checkVideoFilesWereRemoved({ server, video: server.store.videoDetails })
469 } 469 }
470 }) 470 })
471 471
@@ -480,7 +480,7 @@ describe('Test videos redundancy', function () {
480 before(async function () { 480 before(async function () {
481 this.timeout(120000) 481 this.timeout(120000)
482 482
483 await flushAndRunServers(strategy, { min_views: 3 }, false) 483 await createSingleServers(strategy, { min_views: 3 }, false)
484 }) 484 })
485 485
486 it('Should have 0 playlist redundancy on the first video', async function () { 486 it('Should have 0 playlist redundancy on the first video', async function () {
@@ -506,14 +506,14 @@ describe('Test videos redundancy', function () {
506 it('Should have 1 redundancy on the first video', async function () { 506 it('Should have 1 redundancy on the first video', async function () {
507 this.timeout(160000) 507 this.timeout(160000)
508 508
509 await viewVideo(servers[0].url, video1Server2UUID) 509 await servers[0].videos.view({ id: video1Server2.uuid })
510 await viewVideo(servers[2].url, video1Server2UUID) 510 await servers[2].videos.view({ id: video1Server2.uuid })
511 511
512 await wait(10000) 512 await wait(10000)
513 await waitJobs(servers) 513 await waitJobs(servers)
514 514
515 await waitJobs(servers) 515 await waitJobs(servers)
516 await waitUntilLog(servers[0], 'Duplicated ', 1) 516 await servers[0].servers.waitUntilLog('Duplicated ', 1)
517 await waitJobs(servers) 517 await waitJobs(servers)
518 518
519 await check1PlaylistRedundancies() 519 await check1PlaylistRedundancies()
@@ -523,12 +523,13 @@ describe('Test videos redundancy', function () {
523 it('Should remove the video and the redundancy files', async function () { 523 it('Should remove the video and the redundancy files', async function () {
524 this.timeout(20000) 524 this.timeout(20000)
525 525
526 await removeVideo(servers[1].url, servers[1].accessToken, video1Server2UUID) 526 await saveVideoInServers(servers, video1Server2.uuid)
527 await servers[1].videos.remove({ id: video1Server2.uuid })
527 528
528 await waitJobs(servers) 529 await waitJobs(servers)
529 530
530 for (const server of servers) { 531 for (const server of servers) {
531 await checkVideoFilesWereRemoved(video1Server2UUID, server.internalServerNumber) 532 await checkVideoFilesWereRemoved({ server, video: server.store.videoDetails })
532 } 533 }
533 }) 534 })
534 535
@@ -541,7 +542,7 @@ describe('Test videos redundancy', function () {
541 before(function () { 542 before(function () {
542 this.timeout(120000) 543 this.timeout(120000)
543 544
544 return flushAndRunServers(null) 545 return createSingleServers(null)
545 }) 546 })
546 547
547 it('Should have 1 webseed on the first video', async function () { 548 it('Should have 1 webseed on the first video', async function () {
@@ -551,18 +552,14 @@ describe('Test videos redundancy', function () {
551 }) 552 })
552 553
553 it('Should create a redundancy on first video', async function () { 554 it('Should create a redundancy on first video', async function () {
554 await addVideoRedundancy({ 555 await servers[0].redundancy.addVideo({ videoId: video1Server2.id })
555 url: servers[0].url,
556 accessToken: servers[0].accessToken,
557 videoId: video1Server2Id
558 })
559 }) 556 })
560 557
561 it('Should have 2 webseeds on the first video', async function () { 558 it('Should have 2 webseeds on the first video', async function () {
562 this.timeout(80000) 559 this.timeout(80000)
563 560
564 await waitJobs(servers) 561 await waitJobs(servers)
565 await waitUntilLog(servers[0], 'Duplicated ', 5) 562 await servers[0].servers.waitUntilLog('Duplicated ', 5)
566 await waitJobs(servers) 563 await waitJobs(servers)
567 564
568 await check2Webseeds() 565 await check2Webseeds()
@@ -573,22 +570,15 @@ describe('Test videos redundancy', function () {
573 it('Should manually remove redundancies on server 1 and remove duplicated videos', async function () { 570 it('Should manually remove redundancies on server 1 and remove duplicated videos', async function () {
574 this.timeout(80000) 571 this.timeout(80000)
575 572
576 const res = await listVideoRedundancies({ 573 const body = await servers[0].redundancy.listVideos({ target: 'remote-videos' })
577 url: servers[0].url,
578 accessToken: servers[0].accessToken,
579 target: 'remote-videos'
580 })
581 574
582 const videos = res.body.data as VideoRedundancy[] 575 const videos = body.data
583 expect(videos).to.have.lengthOf(1) 576 expect(videos).to.have.lengthOf(1)
584 577
585 const video = videos[0] 578 const video = videos[0]
579
586 for (const r of video.redundancies.files.concat(video.redundancies.streamingPlaylists)) { 580 for (const r of video.redundancies.files.concat(video.redundancies.streamingPlaylists)) {
587 await removeVideoRedundancy({ 581 await servers[0].redundancy.removeVideo({ redundancyId: r.id })
588 url: servers[0].url,
589 accessToken: servers[0].accessToken,
590 redundancyId: r.id
591 })
592 } 582 }
593 583
594 await waitJobs(servers) 584 await waitJobs(servers)
@@ -597,7 +587,7 @@ describe('Test videos redundancy', function () {
597 await check1WebSeed() 587 await check1WebSeed()
598 await check0PlaylistRedundancies() 588 await check0PlaylistRedundancies()
599 589
600 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].serverNumber, [ 'videos' ]) 590 await checkVideoFilesWereRemoved({ server: servers[0], video: video1Server2, onlyVideoFiles: true })
601 }) 591 })
602 592
603 after(async function () { 593 after(async function () {
@@ -608,10 +598,9 @@ describe('Test videos redundancy', function () {
608 describe('Test expiration', function () { 598 describe('Test expiration', function () {
609 const strategy = 'recently-added' 599 const strategy = 'recently-added'
610 600
611 async function checkContains (servers: ServerInfo[], str: string) { 601 async function checkContains (servers: PeerTubeServer[], str: string) {
612 for (const server of servers) { 602 for (const server of servers) {
613 const res = await getVideo(server.url, video1Server2UUID) 603 const video = await server.videos.get({ id: video1Server2.uuid })
614 const video: VideoDetails = res.body
615 604
616 for (const f of video.files) { 605 for (const f of video.files) {
617 expect(f.magnetUri).to.contain(str) 606 expect(f.magnetUri).to.contain(str)
@@ -619,10 +608,9 @@ describe('Test videos redundancy', function () {
619 } 608 }
620 } 609 }
621 610
622 async function checkNotContains (servers: ServerInfo[], str: string) { 611 async function checkNotContains (servers: PeerTubeServer[], str: string) {
623 for (const server of servers) { 612 for (const server of servers) {
624 const res = await getVideo(server.url, video1Server2UUID) 613 const video = await server.videos.get({ id: video1Server2.uuid })
625 const video: VideoDetails = res.body
626 614
627 for (const f of video.files) { 615 for (const f of video.files) {
628 expect(f.magnetUri).to.not.contain(str) 616 expect(f.magnetUri).to.not.contain(str)
@@ -633,7 +621,7 @@ describe('Test videos redundancy', function () {
633 before(async function () { 621 before(async function () {
634 this.timeout(120000) 622 this.timeout(120000)
635 623
636 await flushAndRunServers(strategy, { min_lifetime: '7 seconds', min_views: 0 }) 624 await createSingleServers(strategy, { min_lifetime: '7 seconds', min_views: 0 })
637 625
638 await enableRedundancyOnServer1() 626 await enableRedundancyOnServer1()
639 }) 627 })
@@ -656,7 +644,7 @@ describe('Test videos redundancy', function () {
656 it('Should stop server 1 and expire video redundancy', async function () { 644 it('Should stop server 1 and expire video redundancy', async function () {
657 this.timeout(80000) 645 this.timeout(80000)
658 646
659 killallServers([ servers[0] ]) 647 await killallServers([ servers[0] ])
660 648
661 await wait(15000) 649 await wait(15000)
662 650
@@ -675,25 +663,25 @@ describe('Test videos redundancy', function () {
675 before(async function () { 663 before(async function () {
676 this.timeout(120000) 664 this.timeout(120000)
677 665
678 await flushAndRunServers(strategy, { min_lifetime: '7 seconds', min_views: 0 }) 666 await createSingleServers(strategy, { min_lifetime: '7 seconds', min_views: 0 })
679 667
680 await enableRedundancyOnServer1() 668 await enableRedundancyOnServer1()
681 669
682 await waitJobs(servers) 670 await waitJobs(servers)
683 await waitUntilLog(servers[0], 'Duplicated ', 5) 671 await servers[0].servers.waitUntilLog('Duplicated ', 5)
684 await waitJobs(servers) 672 await waitJobs(servers)
685 673
686 await check2Webseeds(video1Server2UUID) 674 await check2Webseeds()
687 await check1PlaylistRedundancies(video1Server2UUID) 675 await check1PlaylistRedundancies()
688 await checkStatsWith1Redundancy(strategy) 676 await checkStatsWith1Redundancy(strategy)
689 677
690 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 2 server 2', privacy: VideoPrivacy.PRIVATE }) 678 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video 2 server 2', privacy: VideoPrivacy.PRIVATE } })
691 video2Server2UUID = res.body.video.uuid 679 video2Server2UUID = uuid
692 680
693 // Wait transcoding before federation 681 // Wait transcoding before federation
694 await waitJobs(servers) 682 await waitJobs(servers)
695 683
696 await updateVideo(servers[1].url, servers[1].accessToken, video2Server2UUID, { privacy: VideoPrivacy.PUBLIC }) 684 await servers[1].videos.update({ id: video2Server2UUID, attributes: { privacy: VideoPrivacy.PUBLIC } })
697 }) 685 })
698 686
699 it('Should cache video 2 webseeds on the first video', async function () { 687 it('Should cache video 2 webseeds on the first video', async function () {
@@ -707,8 +695,8 @@ describe('Test videos redundancy', function () {
707 await wait(1000) 695 await wait(1000)
708 696
709 try { 697 try {
710 await check1WebSeed(video1Server2UUID) 698 await check1WebSeed()
711 await check0PlaylistRedundancies(video1Server2UUID) 699 await check0PlaylistRedundancies()
712 700
713 await check2Webseeds(video2Server2UUID) 701 await check2Webseeds(video2Server2UUID)
714 await check1PlaylistRedundancies(video2Server2UUID) 702 await check1PlaylistRedundancies(video2Server2UUID)
@@ -725,8 +713,8 @@ describe('Test videos redundancy', function () {
725 713
726 await waitJobs(servers) 714 await waitJobs(servers)
727 715
728 killallServers([ servers[0] ]) 716 await killallServers([ servers[0] ])
729 await reRunServer(servers[0], { 717 await servers[0].run({
730 redundancy: { 718 redundancy: {
731 videos: { 719 videos: {
732 check_interval: '1 second', 720 check_interval: '1 second',
@@ -737,7 +725,7 @@ describe('Test videos redundancy', function () {
737 725
738 await waitJobs(servers) 726 await waitJobs(servers)
739 727
740 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].internalServerNumber, [ join('redundancy', 'hls') ]) 728 await checkVideoFilesWereRemoved({ server: servers[0], video: video1Server2, onlyVideoFiles: true })
741 }) 729 })
742 730
743 after(async function () { 731 after(async function () {
diff --git a/server/tests/api/search/search-activitypub-video-channels.ts b/server/tests/api/search/search-activitypub-video-channels.ts
index e83eb7171..426cbc8e1 100644
--- a/server/tests/api/search/search-activitypub-video-channels.ts
+++ b/server/tests/api/search/search-activitypub-video-channels.ts
@@ -1,69 +1,63 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 addVideoChannel,
7 cleanupTests, 6 cleanupTests,
8 createUser, 7 createMultipleServers,
9 deleteVideoChannel, 8 PeerTubeServer,
10 flushAndRunMultipleServers, 9 SearchCommand,
11 getVideoChannelsList,
12 getVideoChannelVideos,
13 ServerInfo,
14 setAccessTokensToServers, 10 setAccessTokensToServers,
15 updateMyUser, 11 wait,
16 updateVideo, 12 waitJobs
17 updateVideoChannel, 13} from '@shared/extra-utils'
18 uploadVideo, 14import { VideoChannel } from '@shared/models'
19 userLogin,
20 wait
21} from '../../../../shared/extra-utils'
22import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
23import { VideoChannel } from '../../../../shared/models/videos'
24import { searchVideoChannel } from '../../../../shared/extra-utils/search/video-channels'
25 15
26const expect = chai.expect 16const expect = chai.expect
27 17
28describe('Test ActivityPub video channels search', function () { 18describe('Test ActivityPub video channels search', function () {
29 let servers: ServerInfo[] 19 let servers: PeerTubeServer[]
30 let userServer2Token: string 20 let userServer2Token: string
31 let videoServer2UUID: string 21 let videoServer2UUID: string
32 let channelIdServer2: number 22 let channelIdServer2: number
23 let command: SearchCommand
33 24
34 before(async function () { 25 before(async function () {
35 this.timeout(120000) 26 this.timeout(120000)
36 27
37 servers = await flushAndRunMultipleServers(2) 28 servers = await createMultipleServers(2)
38 29
39 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
40 31
41 { 32 {
42 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: 'user1_server1', password: 'password' }) 33 await servers[0].users.create({ username: 'user1_server1', password: 'password' })
43 const channel = { 34 const channel = {
44 name: 'channel1_server1', 35 name: 'channel1_server1',
45 displayName: 'Channel 1 server 1' 36 displayName: 'Channel 1 server 1'
46 } 37 }
47 await addVideoChannel(servers[0].url, servers[0].accessToken, channel) 38 await servers[0].channels.create({ attributes: channel })
48 } 39 }
49 40
50 { 41 {
51 const user = { username: 'user1_server2', password: 'password' } 42 const user = { username: 'user1_server2', password: 'password' }
52 await createUser({ url: servers[1].url, accessToken: servers[1].accessToken, username: user.username, password: user.password }) 43 await servers[1].users.create({ username: user.username, password: user.password })
53 userServer2Token = await userLogin(servers[1], user) 44 userServer2Token = await servers[1].login.getAccessToken(user)
54 45
55 const channel = { 46 const channel = {
56 name: 'channel1_server2', 47 name: 'channel1_server2',
57 displayName: 'Channel 1 server 2' 48 displayName: 'Channel 1 server 2'
58 } 49 }
59 const resChannel = await addVideoChannel(servers[1].url, userServer2Token, channel) 50 const created = await servers[1].channels.create({ token: userServer2Token, attributes: channel })
60 channelIdServer2 = resChannel.body.videoChannel.id 51 channelIdServer2 = created.id
61 52
62 const res = await uploadVideo(servers[1].url, userServer2Token, { name: 'video 1 server 2', channelId: channelIdServer2 }) 53 const attributes = { name: 'video 1 server 2', channelId: channelIdServer2 }
63 videoServer2UUID = res.body.video.uuid 54 const { uuid } = await servers[1].videos.upload({ token: userServer2Token, attributes })
55 videoServer2UUID = uuid
64 } 56 }
65 57
66 await waitJobs(servers) 58 await waitJobs(servers)
59
60 command = servers[0].search
67 }) 61 })
68 62
69 it('Should not find a remote video channel', async function () { 63 it('Should not find a remote video channel', async function () {
@@ -71,21 +65,21 @@ describe('Test ActivityPub video channels search', function () {
71 65
72 { 66 {
73 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server3' 67 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server3'
74 const res = await searchVideoChannel(servers[0].url, search, servers[0].accessToken) 68 const body = await command.searchChannels({ search, token: servers[0].accessToken })
75 69
76 expect(res.body.total).to.equal(0) 70 expect(body.total).to.equal(0)
77 expect(res.body.data).to.be.an('array') 71 expect(body.data).to.be.an('array')
78 expect(res.body.data).to.have.lengthOf(0) 72 expect(body.data).to.have.lengthOf(0)
79 } 73 }
80 74
81 { 75 {
82 // Without token 76 // Without token
83 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2' 77 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2'
84 const res = await searchVideoChannel(servers[0].url, search) 78 const body = await command.searchChannels({ search })
85 79
86 expect(res.body.total).to.equal(0) 80 expect(body.total).to.equal(0)
87 expect(res.body.data).to.be.an('array') 81 expect(body.data).to.be.an('array')
88 expect(res.body.data).to.have.lengthOf(0) 82 expect(body.data).to.have.lengthOf(0)
89 } 83 }
90 }) 84 })
91 85
@@ -96,13 +90,13 @@ describe('Test ActivityPub video channels search', function () {
96 ] 90 ]
97 91
98 for (const search of searches) { 92 for (const search of searches) {
99 const res = await searchVideoChannel(servers[0].url, search) 93 const body = await command.searchChannels({ search })
100 94
101 expect(res.body.total).to.equal(1) 95 expect(body.total).to.equal(1)
102 expect(res.body.data).to.be.an('array') 96 expect(body.data).to.be.an('array')
103 expect(res.body.data).to.have.lengthOf(1) 97 expect(body.data).to.have.lengthOf(1)
104 expect(res.body.data[0].name).to.equal('channel1_server1') 98 expect(body.data[0].name).to.equal('channel1_server1')
105 expect(res.body.data[0].displayName).to.equal('Channel 1 server 1') 99 expect(body.data[0].displayName).to.equal('Channel 1 server 1')
106 } 100 }
107 }) 101 })
108 102
@@ -110,13 +104,13 @@ describe('Test ActivityPub video channels search', function () {
110 const search = 'http://localhost:' + servers[0].port + '/c/channel1_server1' 104 const search = 'http://localhost:' + servers[0].port + '/c/channel1_server1'
111 105
112 for (const token of [ undefined, servers[0].accessToken ]) { 106 for (const token of [ undefined, servers[0].accessToken ]) {
113 const res = await searchVideoChannel(servers[0].url, search, token) 107 const body = await command.searchChannels({ search, token })
114 108
115 expect(res.body.total).to.equal(1) 109 expect(body.total).to.equal(1)
116 expect(res.body.data).to.be.an('array') 110 expect(body.data).to.be.an('array')
117 expect(res.body.data).to.have.lengthOf(1) 111 expect(body.data).to.have.lengthOf(1)
118 expect(res.body.data[0].name).to.equal('channel1_server1') 112 expect(body.data[0].name).to.equal('channel1_server1')
119 expect(res.body.data[0].displayName).to.equal('Channel 1 server 1') 113 expect(body.data[0].displayName).to.equal('Channel 1 server 1')
120 } 114 }
121 }) 115 })
122 116
@@ -129,23 +123,23 @@ describe('Test ActivityPub video channels search', function () {
129 ] 123 ]
130 124
131 for (const search of searches) { 125 for (const search of searches) {
132 const res = await searchVideoChannel(servers[0].url, search, servers[0].accessToken) 126 const body = await command.searchChannels({ search, token: servers[0].accessToken })
133 127
134 expect(res.body.total).to.equal(1) 128 expect(body.total).to.equal(1)
135 expect(res.body.data).to.be.an('array') 129 expect(body.data).to.be.an('array')
136 expect(res.body.data).to.have.lengthOf(1) 130 expect(body.data).to.have.lengthOf(1)
137 expect(res.body.data[0].name).to.equal('channel1_server2') 131 expect(body.data[0].name).to.equal('channel1_server2')
138 expect(res.body.data[0].displayName).to.equal('Channel 1 server 2') 132 expect(body.data[0].displayName).to.equal('Channel 1 server 2')
139 } 133 }
140 }) 134 })
141 135
142 it('Should not list this remote video channel', async function () { 136 it('Should not list this remote video channel', async function () {
143 const res = await getVideoChannelsList(servers[0].url, 0, 5) 137 const body = await servers[0].channels.list()
144 expect(res.body.total).to.equal(3) 138 expect(body.total).to.equal(3)
145 expect(res.body.data).to.have.lengthOf(3) 139 expect(body.data).to.have.lengthOf(3)
146 expect(res.body.data[0].name).to.equal('channel1_server1') 140 expect(body.data[0].name).to.equal('channel1_server1')
147 expect(res.body.data[1].name).to.equal('user1_server1_channel') 141 expect(body.data[1].name).to.equal('user1_server1_channel')
148 expect(res.body.data[2].name).to.equal('root_channel') 142 expect(body.data[2].name).to.equal('root_channel')
149 }) 143 })
150 144
151 it('Should list video channel videos of server 2 without token', async function () { 145 it('Should list video channel videos of server 2 without token', async function () {
@@ -153,34 +147,43 @@ describe('Test ActivityPub video channels search', function () {
153 147
154 await waitJobs(servers) 148 await waitJobs(servers)
155 149
156 const res = await getVideoChannelVideos(servers[0].url, null, 'channel1_server2@localhost:' + servers[1].port, 0, 5) 150 const { total, data } = await servers[0].videos.listByChannel({
157 expect(res.body.total).to.equal(0) 151 token: null,
158 expect(res.body.data).to.have.lengthOf(0) 152 handle: 'channel1_server2@localhost:' + servers[1].port
153 })
154 expect(total).to.equal(0)
155 expect(data).to.have.lengthOf(0)
159 }) 156 })
160 157
161 it('Should list video channel videos of server 2 with token', async function () { 158 it('Should list video channel videos of server 2 with token', async function () {
162 const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, 'channel1_server2@localhost:' + servers[1].port, 0, 5) 159 const { total, data } = await servers[0].videos.listByChannel({
160 handle: 'channel1_server2@localhost:' + servers[1].port
161 })
163 162
164 expect(res.body.total).to.equal(1) 163 expect(total).to.equal(1)
165 expect(res.body.data[0].name).to.equal('video 1 server 2') 164 expect(data[0].name).to.equal('video 1 server 2')
166 }) 165 })
167 166
168 it('Should update video channel of server 2, and refresh it on server 1', async function () { 167 it('Should update video channel of server 2, and refresh it on server 1', async function () {
169 this.timeout(60000) 168 this.timeout(60000)
170 169
171 await updateVideoChannel(servers[1].url, userServer2Token, 'channel1_server2', { displayName: 'channel updated' }) 170 await servers[1].channels.update({
172 await updateMyUser({ url: servers[1].url, accessToken: userServer2Token, displayName: 'user updated' }) 171 token: userServer2Token,
172 channelName: 'channel1_server2',
173 attributes: { displayName: 'channel updated' }
174 })
175 await servers[1].users.updateMe({ token: userServer2Token, displayName: 'user updated' })
173 176
174 await waitJobs(servers) 177 await waitJobs(servers)
175 // Expire video channel 178 // Expire video channel
176 await wait(10000) 179 await wait(10000)
177 180
178 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2' 181 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2'
179 const res = await searchVideoChannel(servers[0].url, search, servers[0].accessToken) 182 const body = await command.searchChannels({ search, token: servers[0].accessToken })
180 expect(res.body.total).to.equal(1) 183 expect(body.total).to.equal(1)
181 expect(res.body.data).to.have.lengthOf(1) 184 expect(body.data).to.have.lengthOf(1)
182 185
183 const videoChannel: VideoChannel = res.body.data[0] 186 const videoChannel: VideoChannel = body.data[0]
184 expect(videoChannel.displayName).to.equal('channel updated') 187 expect(videoChannel.displayName).to.equal('channel updated')
185 188
186 // We don't return the owner account for now 189 // We don't return the owner account for now
@@ -190,8 +193,8 @@ describe('Test ActivityPub video channels search', function () {
190 it('Should update and add a video on server 2, and update it on server 1 after a search', async function () { 193 it('Should update and add a video on server 2, and update it on server 1 after a search', async function () {
191 this.timeout(60000) 194 this.timeout(60000)
192 195
193 await updateVideo(servers[1].url, userServer2Token, videoServer2UUID, { name: 'video 1 updated' }) 196 await servers[1].videos.update({ token: userServer2Token, id: videoServer2UUID, attributes: { name: 'video 1 updated' } })
194 await uploadVideo(servers[1].url, userServer2Token, { name: 'video 2 server 2', channelId: channelIdServer2 }) 197 await servers[1].videos.upload({ token: userServer2Token, attributes: { name: 'video 2 server 2', channelId: channelIdServer2 } })
195 198
196 await waitJobs(servers) 199 await waitJobs(servers)
197 200
@@ -199,31 +202,31 @@ describe('Test ActivityPub video channels search', function () {
199 await wait(10000) 202 await wait(10000)
200 203
201 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2' 204 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2'
202 await searchVideoChannel(servers[0].url, search, servers[0].accessToken) 205 await command.searchChannels({ search, token: servers[0].accessToken })
203 206
204 await waitJobs(servers) 207 await waitJobs(servers)
205 208
206 const videoChannelName = 'channel1_server2@localhost:' + servers[1].port 209 const handle = 'channel1_server2@localhost:' + servers[1].port
207 const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, videoChannelName, 0, 5, '-createdAt') 210 const { total, data } = await servers[0].videos.listByChannel({ handle, sort: '-createdAt' })
208 211
209 expect(res.body.total).to.equal(2) 212 expect(total).to.equal(2)
210 expect(res.body.data[0].name).to.equal('video 2 server 2') 213 expect(data[0].name).to.equal('video 2 server 2')
211 expect(res.body.data[1].name).to.equal('video 1 updated') 214 expect(data[1].name).to.equal('video 1 updated')
212 }) 215 })
213 216
214 it('Should delete video channel of server 2, and delete it on server 1', async function () { 217 it('Should delete video channel of server 2, and delete it on server 1', async function () {
215 this.timeout(60000) 218 this.timeout(60000)
216 219
217 await deleteVideoChannel(servers[1].url, userServer2Token, 'channel1_server2') 220 await servers[1].channels.delete({ token: userServer2Token, channelName: 'channel1_server2' })
218 221
219 await waitJobs(servers) 222 await waitJobs(servers)
220 // Expire video 223 // Expire video
221 await wait(10000) 224 await wait(10000)
222 225
223 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2' 226 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2'
224 const res = await searchVideoChannel(servers[0].url, search, servers[0].accessToken) 227 const body = await command.searchChannels({ search, token: servers[0].accessToken })
225 expect(res.body.total).to.equal(0) 228 expect(body.total).to.equal(0)
226 expect(res.body.data).to.have.lengthOf(0) 229 expect(body.data).to.have.lengthOf(0)
227 }) 230 })
228 231
229 after(async function () { 232 after(async function () {
diff --git a/server/tests/api/search/search-activitypub-video-playlists.ts b/server/tests/api/search/search-activitypub-video-playlists.ts
index 4c08e9548..33ca7be12 100644
--- a/server/tests/api/search/search-activitypub-video-playlists.ts
+++ b/server/tests/api/search/search-activitypub-video-playlists.ts
@@ -3,113 +3,102 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 addVideoInPlaylist,
7 cleanupTests, 6 cleanupTests,
8 createVideoPlaylist, 7 createMultipleServers,
9 deleteVideoPlaylist, 8 PeerTubeServer,
10 flushAndRunMultipleServers, 9 SearchCommand,
11 getVideoPlaylistsList,
12 searchVideoPlaylists,
13 ServerInfo,
14 setAccessTokensToServers, 10 setAccessTokensToServers,
15 setDefaultVideoChannel, 11 setDefaultVideoChannel,
16 uploadVideoAndGetId, 12 wait,
17 wait 13 waitJobs
18} from '../../../../shared/extra-utils' 14} from '@shared/extra-utils'
19import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 15import { VideoPlaylistPrivacy } from '@shared/models'
20import { VideoPlaylist, VideoPlaylistPrivacy } from '../../../../shared/models/videos'
21 16
22const expect = chai.expect 17const expect = chai.expect
23 18
24describe('Test ActivityPub playlists search', function () { 19describe('Test ActivityPub playlists search', function () {
25 let servers: ServerInfo[] 20 let servers: PeerTubeServer[]
26 let playlistServer1UUID: string 21 let playlistServer1UUID: string
27 let playlistServer2UUID: string 22 let playlistServer2UUID: string
28 let video2Server2: string 23 let video2Server2: string
29 24
25 let command: SearchCommand
26
30 before(async function () { 27 before(async function () {
31 this.timeout(120000) 28 this.timeout(120000)
32 29
33 servers = await flushAndRunMultipleServers(2) 30 servers = await createMultipleServers(2)
34 31
35 await setAccessTokensToServers(servers) 32 await setAccessTokensToServers(servers)
36 await setDefaultVideoChannel(servers) 33 await setDefaultVideoChannel(servers)
37 34
38 { 35 {
39 const video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 1' })).uuid 36 const video1 = (await servers[0].videos.quickUpload({ name: 'video 1' })).uuid
40 const video2 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 2' })).uuid 37 const video2 = (await servers[0].videos.quickUpload({ name: 'video 2' })).uuid
41 38
42 const attributes = { 39 const attributes = {
43 displayName: 'playlist 1 on server 1', 40 displayName: 'playlist 1 on server 1',
44 privacy: VideoPlaylistPrivacy.PUBLIC, 41 privacy: VideoPlaylistPrivacy.PUBLIC,
45 videoChannelId: servers[0].videoChannel.id 42 videoChannelId: servers[0].store.channel.id
46 } 43 }
47 const res = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs: attributes }) 44 const created = await servers[0].playlists.create({ attributes })
48 playlistServer1UUID = res.body.videoPlaylist.uuid 45 playlistServer1UUID = created.uuid
49 46
50 for (const videoId of [ video1, video2 ]) { 47 for (const videoId of [ video1, video2 ]) {
51 await addVideoInPlaylist({ 48 await servers[0].playlists.addElement({ playlistId: playlistServer1UUID, attributes: { videoId } })
52 url: servers[0].url,
53 token: servers[0].accessToken,
54 playlistId: playlistServer1UUID,
55 elementAttrs: { videoId }
56 })
57 } 49 }
58 } 50 }
59 51
60 { 52 {
61 const videoId = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 1' })).uuid 53 const videoId = (await servers[1].videos.quickUpload({ name: 'video 1' })).uuid
62 video2Server2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 2' })).uuid 54 video2Server2 = (await servers[1].videos.quickUpload({ name: 'video 2' })).uuid
63 55
64 const attributes = { 56 const attributes = {
65 displayName: 'playlist 1 on server 2', 57 displayName: 'playlist 1 on server 2',
66 privacy: VideoPlaylistPrivacy.PUBLIC, 58 privacy: VideoPlaylistPrivacy.PUBLIC,
67 videoChannelId: servers[1].videoChannel.id 59 videoChannelId: servers[1].store.channel.id
68 } 60 }
69 const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs: attributes }) 61 const created = await servers[1].playlists.create({ attributes })
70 playlistServer2UUID = res.body.videoPlaylist.uuid 62 playlistServer2UUID = created.uuid
71 63
72 await addVideoInPlaylist({ 64 await servers[1].playlists.addElement({ playlistId: playlistServer2UUID, attributes: { videoId } })
73 url: servers[1].url,
74 token: servers[1].accessToken,
75 playlistId: playlistServer2UUID,
76 elementAttrs: { videoId }
77 })
78 } 65 }
79 66
80 await waitJobs(servers) 67 await waitJobs(servers)
68
69 command = servers[0].search
81 }) 70 })
82 71
83 it('Should not find a remote playlist', async function () { 72 it('Should not find a remote playlist', async function () {
84 { 73 {
85 const search = 'http://localhost:' + servers[1].port + '/video-playlists/43' 74 const search = 'http://localhost:' + servers[1].port + '/video-playlists/43'
86 const res = await searchVideoPlaylists(servers[0].url, search, servers[0].accessToken) 75 const body = await command.searchPlaylists({ search, token: servers[0].accessToken })
87 76
88 expect(res.body.total).to.equal(0) 77 expect(body.total).to.equal(0)
89 expect(res.body.data).to.be.an('array') 78 expect(body.data).to.be.an('array')
90 expect(res.body.data).to.have.lengthOf(0) 79 expect(body.data).to.have.lengthOf(0)
91 } 80 }
92 81
93 { 82 {
94 // Without token 83 // Without token
95 const search = 'http://localhost:' + servers[1].port + '/video-playlists/' + playlistServer2UUID 84 const search = 'http://localhost:' + servers[1].port + '/video-playlists/' + playlistServer2UUID
96 const res = await searchVideoPlaylists(servers[0].url, search) 85 const body = await command.searchPlaylists({ search })
97 86
98 expect(res.body.total).to.equal(0) 87 expect(body.total).to.equal(0)
99 expect(res.body.data).to.be.an('array') 88 expect(body.data).to.be.an('array')
100 expect(res.body.data).to.have.lengthOf(0) 89 expect(body.data).to.have.lengthOf(0)
101 } 90 }
102 }) 91 })
103 92
104 it('Should search a local playlist', async function () { 93 it('Should search a local playlist', async function () {
105 const search = 'http://localhost:' + servers[0].port + '/video-playlists/' + playlistServer1UUID 94 const search = 'http://localhost:' + servers[0].port + '/video-playlists/' + playlistServer1UUID
106 const res = await searchVideoPlaylists(servers[0].url, search) 95 const body = await command.searchPlaylists({ search })
107 96
108 expect(res.body.total).to.equal(1) 97 expect(body.total).to.equal(1)
109 expect(res.body.data).to.be.an('array') 98 expect(body.data).to.be.an('array')
110 expect(res.body.data).to.have.lengthOf(1) 99 expect(body.data).to.have.lengthOf(1)
111 expect(res.body.data[0].displayName).to.equal('playlist 1 on server 1') 100 expect(body.data[0].displayName).to.equal('playlist 1 on server 1')
112 expect(res.body.data[0].videosLength).to.equal(2) 101 expect(body.data[0].videosLength).to.equal(2)
113 }) 102 })
114 103
115 it('Should search a local playlist with an alternative URL', async function () { 104 it('Should search a local playlist with an alternative URL', async function () {
@@ -120,13 +109,13 @@ describe('Test ActivityPub playlists search', function () {
120 109
121 for (const search of searches) { 110 for (const search of searches) {
122 for (const token of [ undefined, servers[0].accessToken ]) { 111 for (const token of [ undefined, servers[0].accessToken ]) {
123 const res = await searchVideoPlaylists(servers[0].url, search, token) 112 const body = await command.searchPlaylists({ search, token })
124 113
125 expect(res.body.total).to.equal(1) 114 expect(body.total).to.equal(1)
126 expect(res.body.data).to.be.an('array') 115 expect(body.data).to.be.an('array')
127 expect(res.body.data).to.have.lengthOf(1) 116 expect(body.data).to.have.lengthOf(1)
128 expect(res.body.data[0].displayName).to.equal('playlist 1 on server 1') 117 expect(body.data[0].displayName).to.equal('playlist 1 on server 1')
129 expect(res.body.data[0].videosLength).to.equal(2) 118 expect(body.data[0].videosLength).to.equal(2)
130 } 119 }
131 } 120 }
132 }) 121 })
@@ -139,32 +128,27 @@ describe('Test ActivityPub playlists search', function () {
139 ] 128 ]
140 129
141 for (const search of searches) { 130 for (const search of searches) {
142 const res = await searchVideoPlaylists(servers[0].url, search, servers[0].accessToken) 131 const body = await command.searchPlaylists({ search, token: servers[0].accessToken })
143 132
144 expect(res.body.total).to.equal(1) 133 expect(body.total).to.equal(1)
145 expect(res.body.data).to.be.an('array') 134 expect(body.data).to.be.an('array')
146 expect(res.body.data).to.have.lengthOf(1) 135 expect(body.data).to.have.lengthOf(1)
147 expect(res.body.data[0].displayName).to.equal('playlist 1 on server 2') 136 expect(body.data[0].displayName).to.equal('playlist 1 on server 2')
148 expect(res.body.data[0].videosLength).to.equal(1) 137 expect(body.data[0].videosLength).to.equal(1)
149 } 138 }
150 }) 139 })
151 140
152 it('Should not list this remote playlist', async function () { 141 it('Should not list this remote playlist', async function () {
153 const res = await getVideoPlaylistsList(servers[0].url, 0, 10) 142 const body = await servers[0].playlists.list({ start: 0, count: 10 })
154 expect(res.body.total).to.equal(1) 143 expect(body.total).to.equal(1)
155 expect(res.body.data).to.have.lengthOf(1) 144 expect(body.data).to.have.lengthOf(1)
156 expect(res.body.data[0].displayName).to.equal('playlist 1 on server 1') 145 expect(body.data[0].displayName).to.equal('playlist 1 on server 1')
157 }) 146 })
158 147
159 it('Should update the playlist of server 2, and refresh it on server 1', async function () { 148 it('Should update the playlist of server 2, and refresh it on server 1', async function () {
160 this.timeout(60000) 149 this.timeout(60000)
161 150
162 await addVideoInPlaylist({ 151 await servers[1].playlists.addElement({ playlistId: playlistServer2UUID, attributes: { videoId: video2Server2 } })
163 url: servers[1].url,
164 token: servers[1].accessToken,
165 playlistId: playlistServer2UUID,
166 elementAttrs: { videoId: video2Server2 }
167 })
168 152
169 await waitJobs(servers) 153 await waitJobs(servers)
170 // Expire playlist 154 // Expire playlist
@@ -172,23 +156,23 @@ describe('Test ActivityPub playlists search', function () {
172 156
173 // Will run refresh async 157 // Will run refresh async
174 const search = 'http://localhost:' + servers[1].port + '/video-playlists/' + playlistServer2UUID 158 const search = 'http://localhost:' + servers[1].port + '/video-playlists/' + playlistServer2UUID
175 await searchVideoPlaylists(servers[0].url, search, servers[0].accessToken) 159 await command.searchPlaylists({ search, token: servers[0].accessToken })
176 160
177 // Wait refresh 161 // Wait refresh
178 await wait(5000) 162 await wait(5000)
179 163
180 const res = await searchVideoPlaylists(servers[0].url, search, servers[0].accessToken) 164 const body = await command.searchPlaylists({ search, token: servers[0].accessToken })
181 expect(res.body.total).to.equal(1) 165 expect(body.total).to.equal(1)
182 expect(res.body.data).to.have.lengthOf(1) 166 expect(body.data).to.have.lengthOf(1)
183 167
184 const playlist: VideoPlaylist = res.body.data[0] 168 const playlist = body.data[0]
185 expect(playlist.videosLength).to.equal(2) 169 expect(playlist.videosLength).to.equal(2)
186 }) 170 })
187 171
188 it('Should delete playlist of server 2, and delete it on server 1', async function () { 172 it('Should delete playlist of server 2, and delete it on server 1', async function () {
189 this.timeout(60000) 173 this.timeout(60000)
190 174
191 await deleteVideoPlaylist(servers[1].url, servers[1].accessToken, playlistServer2UUID) 175 await servers[1].playlists.delete({ playlistId: playlistServer2UUID })
192 176
193 await waitJobs(servers) 177 await waitJobs(servers)
194 // Expiration 178 // Expiration
@@ -196,14 +180,14 @@ describe('Test ActivityPub playlists search', function () {
196 180
197 // Will run refresh async 181 // Will run refresh async
198 const search = 'http://localhost:' + servers[1].port + '/video-playlists/' + playlistServer2UUID 182 const search = 'http://localhost:' + servers[1].port + '/video-playlists/' + playlistServer2UUID
199 await searchVideoPlaylists(servers[0].url, search, servers[0].accessToken) 183 await command.searchPlaylists({ search, token: servers[0].accessToken })
200 184
201 // Wait refresh 185 // Wait refresh
202 await wait(5000) 186 await wait(5000)
203 187
204 const res = await searchVideoPlaylists(servers[0].url, search, servers[0].accessToken) 188 const body = await command.searchPlaylists({ search, token: servers[0].accessToken })
205 expect(res.body.total).to.equal(0) 189 expect(body.total).to.equal(0)
206 expect(res.body.data).to.have.lengthOf(0) 190 expect(body.data).to.have.lengthOf(0)
207 }) 191 })
208 192
209 after(async function () { 193 after(async function () {
diff --git a/server/tests/api/search/search-activitypub-videos.ts b/server/tests/api/search/search-activitypub-videos.ts
index e9b4978da..b3cfcacca 100644
--- a/server/tests/api/search/search-activitypub-videos.ts
+++ b/server/tests/api/search/search-activitypub-videos.ts
@@ -1,92 +1,90 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 addVideoChannel,
7 cleanupTests, 6 cleanupTests,
8 flushAndRunMultipleServers, 7 createMultipleServers,
9 getVideosList, 8 PeerTubeServer,
10 removeVideo, 9 SearchCommand,
11 searchVideo,
12 searchVideoWithToken,
13 ServerInfo,
14 setAccessTokensToServers, 10 setAccessTokensToServers,
15 updateVideo, 11 wait,
16 uploadVideo, 12 waitJobs
17 wait 13} from '@shared/extra-utils'
18} from '../../../../shared/extra-utils' 14import { VideoPrivacy } from '@shared/models'
19import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
20import { Video, VideoPrivacy } from '../../../../shared/models/videos'
21 15
22const expect = chai.expect 16const expect = chai.expect
23 17
24describe('Test ActivityPub videos search', function () { 18describe('Test ActivityPub videos search', function () {
25 let servers: ServerInfo[] 19 let servers: PeerTubeServer[]
26 let videoServer1UUID: string 20 let videoServer1UUID: string
27 let videoServer2UUID: string 21 let videoServer2UUID: string
28 22
23 let command: SearchCommand
24
29 before(async function () { 25 before(async function () {
30 this.timeout(120000) 26 this.timeout(120000)
31 27
32 servers = await flushAndRunMultipleServers(2) 28 servers = await createMultipleServers(2)
33 29
34 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
35 31
36 { 32 {
37 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video 1 on server 1' }) 33 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video 1 on server 1' } })
38 videoServer1UUID = res.body.video.uuid 34 videoServer1UUID = uuid
39 } 35 }
40 36
41 { 37 {
42 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 1 on server 2' }) 38 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video 1 on server 2' } })
43 videoServer2UUID = res.body.video.uuid 39 videoServer2UUID = uuid
44 } 40 }
45 41
46 await waitJobs(servers) 42 await waitJobs(servers)
43
44 command = servers[0].search
47 }) 45 })
48 46
49 it('Should not find a remote video', async function () { 47 it('Should not find a remote video', async function () {
50 { 48 {
51 const search = 'http://localhost:' + servers[1].port + '/videos/watch/43' 49 const search = 'http://localhost:' + servers[1].port + '/videos/watch/43'
52 const res = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) 50 const body = await command.searchVideos({ search, token: servers[0].accessToken })
53 51
54 expect(res.body.total).to.equal(0) 52 expect(body.total).to.equal(0)
55 expect(res.body.data).to.be.an('array') 53 expect(body.data).to.be.an('array')
56 expect(res.body.data).to.have.lengthOf(0) 54 expect(body.data).to.have.lengthOf(0)
57 } 55 }
58 56
59 { 57 {
60 // Without token 58 // Without token
61 const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID 59 const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID
62 const res = await searchVideo(servers[0].url, search) 60 const body = await command.searchVideos({ search })
63 61
64 expect(res.body.total).to.equal(0) 62 expect(body.total).to.equal(0)
65 expect(res.body.data).to.be.an('array') 63 expect(body.data).to.be.an('array')
66 expect(res.body.data).to.have.lengthOf(0) 64 expect(body.data).to.have.lengthOf(0)
67 } 65 }
68 }) 66 })
69 67
70 it('Should search a local video', async function () { 68 it('Should search a local video', async function () {
71 const search = 'http://localhost:' + servers[0].port + '/videos/watch/' + videoServer1UUID 69 const search = 'http://localhost:' + servers[0].port + '/videos/watch/' + videoServer1UUID
72 const res = await searchVideo(servers[0].url, search) 70 const body = await command.searchVideos({ search })
73 71
74 expect(res.body.total).to.equal(1) 72 expect(body.total).to.equal(1)
75 expect(res.body.data).to.be.an('array') 73 expect(body.data).to.be.an('array')
76 expect(res.body.data).to.have.lengthOf(1) 74 expect(body.data).to.have.lengthOf(1)
77 expect(res.body.data[0].name).to.equal('video 1 on server 1') 75 expect(body.data[0].name).to.equal('video 1 on server 1')
78 }) 76 })
79 77
80 it('Should search a local video with an alternative URL', async function () { 78 it('Should search a local video with an alternative URL', async function () {
81 const search = 'http://localhost:' + servers[0].port + '/w/' + videoServer1UUID 79 const search = 'http://localhost:' + servers[0].port + '/w/' + videoServer1UUID
82 const res1 = await searchVideo(servers[0].url, search) 80 const body1 = await command.searchVideos({ search })
83 const res2 = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) 81 const body2 = await command.searchVideos({ search, token: servers[0].accessToken })
84 82
85 for (const res of [ res1, res2 ]) { 83 for (const body of [ body1, body2 ]) {
86 expect(res.body.total).to.equal(1) 84 expect(body.total).to.equal(1)
87 expect(res.body.data).to.be.an('array') 85 expect(body.data).to.be.an('array')
88 expect(res.body.data).to.have.lengthOf(1) 86 expect(body.data).to.have.lengthOf(1)
89 expect(res.body.data[0].name).to.equal('video 1 on server 1') 87 expect(body.data[0].name).to.equal('video 1 on server 1')
90 } 88 }
91 }) 89 })
92 90
@@ -97,20 +95,20 @@ describe('Test ActivityPub videos search', function () {
97 ] 95 ]
98 96
99 for (const search of searches) { 97 for (const search of searches) {
100 const res = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) 98 const body = await command.searchVideos({ search, token: servers[0].accessToken })
101 99
102 expect(res.body.total).to.equal(1) 100 expect(body.total).to.equal(1)
103 expect(res.body.data).to.be.an('array') 101 expect(body.data).to.be.an('array')
104 expect(res.body.data).to.have.lengthOf(1) 102 expect(body.data).to.have.lengthOf(1)
105 expect(res.body.data[0].name).to.equal('video 1 on server 2') 103 expect(body.data[0].name).to.equal('video 1 on server 2')
106 } 104 }
107 }) 105 })
108 106
109 it('Should not list this remote video', async function () { 107 it('Should not list this remote video', async function () {
110 const res = await getVideosList(servers[0].url) 108 const { total, data } = await servers[0].videos.list()
111 expect(res.body.total).to.equal(1) 109 expect(total).to.equal(1)
112 expect(res.body.data).to.have.lengthOf(1) 110 expect(data).to.have.lengthOf(1)
113 expect(res.body.data[0].name).to.equal('video 1 on server 1') 111 expect(data[0].name).to.equal('video 1 on server 1')
114 }) 112 })
115 113
116 it('Should update video of server 2, and refresh it on server 1', async function () { 114 it('Should update video of server 2, and refresh it on server 1', async function () {
@@ -120,8 +118,8 @@ describe('Test ActivityPub videos search', function () {
120 name: 'super_channel', 118 name: 'super_channel',
121 displayName: 'super channel' 119 displayName: 'super channel'
122 } 120 }
123 const resChannel = await addVideoChannel(servers[1].url, servers[1].accessToken, channelAttributes) 121 const created = await servers[1].channels.create({ attributes: channelAttributes })
124 const videoChannelId = resChannel.body.videoChannel.id 122 const videoChannelId = created.id
125 123
126 const attributes = { 124 const attributes = {
127 name: 'updated', 125 name: 'updated',
@@ -129,7 +127,7 @@ describe('Test ActivityPub videos search', function () {
129 privacy: VideoPrivacy.UNLISTED, 127 privacy: VideoPrivacy.UNLISTED,
130 channelId: videoChannelId 128 channelId: videoChannelId
131 } 129 }
132 await updateVideo(servers[1].url, servers[1].accessToken, videoServer2UUID, attributes) 130 await servers[1].videos.update({ id: videoServer2UUID, attributes })
133 131
134 await waitJobs(servers) 132 await waitJobs(servers)
135 // Expire video 133 // Expire video
@@ -137,16 +135,16 @@ describe('Test ActivityPub videos search', function () {
137 135
138 // Will run refresh async 136 // Will run refresh async
139 const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID 137 const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID
140 await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) 138 await command.searchVideos({ search, token: servers[0].accessToken })
141 139
142 // Wait refresh 140 // Wait refresh
143 await wait(5000) 141 await wait(5000)
144 142
145 const res = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) 143 const body = await command.searchVideos({ search, token: servers[0].accessToken })
146 expect(res.body.total).to.equal(1) 144 expect(body.total).to.equal(1)
147 expect(res.body.data).to.have.lengthOf(1) 145 expect(body.data).to.have.lengthOf(1)
148 146
149 const video: Video = res.body.data[0] 147 const video = body.data[0]
150 expect(video.name).to.equal('updated') 148 expect(video.name).to.equal('updated')
151 expect(video.channel.name).to.equal('super_channel') 149 expect(video.channel.name).to.equal('super_channel')
152 expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED) 150 expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED)
@@ -155,7 +153,7 @@ describe('Test ActivityPub videos search', function () {
155 it('Should delete video of server 2, and delete it on server 1', async function () { 153 it('Should delete video of server 2, and delete it on server 1', async function () {
156 this.timeout(120000) 154 this.timeout(120000)
157 155
158 await removeVideo(servers[1].url, servers[1].accessToken, videoServer2UUID) 156 await servers[1].videos.remove({ id: videoServer2UUID })
159 157
160 await waitJobs(servers) 158 await waitJobs(servers)
161 // Expire video 159 // Expire video
@@ -163,14 +161,14 @@ describe('Test ActivityPub videos search', function () {
163 161
164 // Will run refresh async 162 // Will run refresh async
165 const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID 163 const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID
166 await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) 164 await command.searchVideos({ search, token: servers[0].accessToken })
167 165
168 // Wait refresh 166 // Wait refresh
169 await wait(5000) 167 await wait(5000)
170 168
171 const res = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) 169 const body = await command.searchVideos({ search, token: servers[0].accessToken })
172 expect(res.body.total).to.equal(0) 170 expect(body.total).to.equal(0)
173 expect(res.body.data).to.have.lengthOf(0) 171 expect(body.data).to.have.lengthOf(0)
174 }) 172 })
175 173
176 after(async function () { 174 after(async function () {
diff --git a/server/tests/api/search/search-channels.ts b/server/tests/api/search/search-channels.ts
index daca2aebe..8a01aff90 100644
--- a/server/tests/api/search/search-channels.ts
+++ b/server/tests/api/search/search-channels.ts
@@ -2,44 +2,65 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { searchVideoChannel, advancedVideoChannelSearch } from '@shared/extra-utils/search/video-channels'
6import { 5import {
7 addVideoChannel,
8 cleanupTests, 6 cleanupTests,
9 createUser, 7 createSingleServer,
10 flushAndRunServer, 8 doubleFollow,
11 ServerInfo, 9 PeerTubeServer,
10 SearchCommand,
12 setAccessTokensToServers 11 setAccessTokensToServers
13} from '../../../../shared/extra-utils' 12} from '@shared/extra-utils'
14import { VideoChannel } from '@shared/models' 13import { VideoChannel } from '@shared/models'
15 14
16const expect = chai.expect 15const expect = chai.expect
17 16
18describe('Test channels search', function () { 17describe('Test channels search', function () {
19 let server: ServerInfo = null 18 let server: PeerTubeServer
19 let remoteServer: PeerTubeServer
20 let command: SearchCommand
20 21
21 before(async function () { 22 before(async function () {
22 this.timeout(30000) 23 this.timeout(120000)
23 24
24 server = await flushAndRunServer(1) 25 const servers = await Promise.all([
26 createSingleServer(1),
27 createSingleServer(2, { transcoding: { enabled: false } })
28 ])
29 server = servers[0]
30 remoteServer = servers[1]
25 31
26 await setAccessTokensToServers([ server ]) 32 await setAccessTokensToServers([ server, remoteServer ])
27 33
28 { 34 {
29 await createUser({ url: server.url, accessToken: server.accessToken, username: 'user1', password: 'password' }) 35 await server.users.create({ username: 'user1' })
30 const channel = { 36 const channel = {
31 name: 'squall_channel', 37 name: 'squall_channel',
32 displayName: 'Squall channel' 38 displayName: 'Squall channel'
33 } 39 }
34 await addVideoChannel(server.url, server.accessToken, channel) 40 await server.channels.create({ attributes: channel })
35 } 41 }
42
43 {
44 await remoteServer.users.create({ username: 'user1' })
45 const channel = {
46 name: 'zell_channel',
47 displayName: 'Zell channel'
48 }
49 const { id } = await remoteServer.channels.create({ attributes: channel })
50
51 await remoteServer.videos.upload({ attributes: { channelId: id } })
52 }
53
54 await doubleFollow(server, remoteServer)
55
56 command = server.search
36 }) 57 })
37 58
38 it('Should make a simple search and not have results', async function () { 59 it('Should make a simple search and not have results', async function () {
39 const res = await searchVideoChannel(server.url, 'abc') 60 const body = await command.searchChannels({ search: 'abc' })
40 61
41 expect(res.body.total).to.equal(0) 62 expect(body.total).to.equal(0)
42 expect(res.body.data).to.have.lengthOf(0) 63 expect(body.data).to.have.lengthOf(0)
43 }) 64 })
44 65
45 it('Should make a search and have results', async function () { 66 it('Should make a search and have results', async function () {
@@ -49,11 +70,11 @@ describe('Test channels search', function () {
49 start: 0, 70 start: 0,
50 count: 1 71 count: 1
51 } 72 }
52 const res = await advancedVideoChannelSearch(server.url, search) 73 const body = await command.advancedChannelSearch({ search })
53 expect(res.body.total).to.equal(1) 74 expect(body.total).to.equal(1)
54 expect(res.body.data).to.have.lengthOf(1) 75 expect(body.data).to.have.lengthOf(1)
55 76
56 const channel: VideoChannel = res.body.data[0] 77 const channel: VideoChannel = body.data[0]
57 expect(channel.name).to.equal('squall_channel') 78 expect(channel.name).to.equal('squall_channel')
58 expect(channel.displayName).to.equal('Squall channel') 79 expect(channel.displayName).to.equal('Squall channel')
59 } 80 }
@@ -65,15 +86,64 @@ describe('Test channels search', function () {
65 count: 1 86 count: 1
66 } 87 }
67 88
68 const res = await advancedVideoChannelSearch(server.url, search) 89 const body = await command.advancedChannelSearch({ search })
90 expect(body.total).to.equal(1)
91 expect(body.data).to.have.lengthOf(0)
92 }
93 })
94
95 it('Should filter by host', async function () {
96 {
97 const search = { search: 'channel', host: remoteServer.host }
69 98
70 expect(res.body.total).to.equal(1) 99 const body = await command.advancedChannelSearch({ search })
100 expect(body.total).to.equal(1)
101 expect(body.data).to.have.lengthOf(1)
102 expect(body.data[0].displayName).to.equal('Zell channel')
103 }
104
105 {
106 const search = { search: 'Sq', host: server.host }
71 107
72 expect(res.body.data).to.have.lengthOf(0) 108 const body = await command.advancedChannelSearch({ search })
109 expect(body.total).to.equal(1)
110 expect(body.data).to.have.lengthOf(1)
111 expect(body.data[0].displayName).to.equal('Squall channel')
112 }
113
114 {
115 const search = { search: 'Squall', host: 'example.com' }
116
117 const body = await command.advancedChannelSearch({ search })
118 expect(body.total).to.equal(0)
119 expect(body.data).to.have.lengthOf(0)
120 }
121 })
122
123 it('Should filter by names', async function () {
124 {
125 const body = await command.advancedChannelSearch({ search: { handles: [ 'squall_channel', 'zell_channel' ] } })
126 expect(body.total).to.equal(1)
127 expect(body.data).to.have.lengthOf(1)
128 expect(body.data[0].displayName).to.equal('Squall channel')
129 }
130
131 {
132 const body = await command.advancedChannelSearch({ search: { handles: [ 'chocobozzz_channel' ] } })
133 expect(body.total).to.equal(0)
134 expect(body.data).to.have.lengthOf(0)
135 }
136
137 {
138 const body = await command.advancedChannelSearch({ search: { handles: [ 'squall_channel', 'zell_channel@' + remoteServer.host ] } })
139 expect(body.total).to.equal(2)
140 expect(body.data).to.have.lengthOf(2)
141 expect(body.data[0].displayName).to.equal('Squall channel')
142 expect(body.data[1].displayName).to.equal('Zell channel')
73 } 143 }
74 }) 144 })
75 145
76 after(async function () { 146 after(async function () {
77 await cleanupTests([ server ]) 147 await cleanupTests([ server, remoteServer ])
78 }) 148 })
79}) 149})
diff --git a/server/tests/api/search/search-index.ts b/server/tests/api/search/search-index.ts
index 00f79232a..4c8b1f608 100644
--- a/server/tests/api/search/search-index.ts
+++ b/server/tests/api/search/search-index.ts
@@ -2,36 +2,34 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { advancedVideoChannelSearch, searchVideoChannel } from '@shared/extra-utils/search/video-channels' 5import { cleanupTests, createSingleServer, PeerTubeServer, SearchCommand, setAccessTokensToServers } from '@shared/extra-utils'
6import { Video, VideoChannel, VideoPlaylist, VideoPlaylistPrivacy, VideoPlaylistType, VideosSearchQuery } from '@shared/models'
7import { 6import {
8 advancedVideoPlaylistSearch, 7 BooleanBothQuery,
9 advancedVideosSearch, 8 VideoChannelsSearchQuery,
10 cleanupTests, 9 VideoPlaylistPrivacy,
11 flushAndRunServer, 10 VideoPlaylistsSearchQuery,
12 immutableAssign, 11 VideoPlaylistType,
13 searchVideo, 12 VideosSearchQuery
14 searchVideoPlaylists, 13} from '@shared/models'
15 ServerInfo,
16 setAccessTokensToServers,
17 updateCustomSubConfig,
18 uploadVideo
19} from '../../../../shared/extra-utils'
20 14
21const expect = chai.expect 15const expect = chai.expect
22 16
23describe('Test videos search', function () { 17describe('Test videos search', function () {
24 let server: ServerInfo = null
25 const localVideoName = 'local video' + new Date().toISOString() 18 const localVideoName = 'local video' + new Date().toISOString()
26 19
20 let server: PeerTubeServer = null
21 let command: SearchCommand
22
27 before(async function () { 23 before(async function () {
28 this.timeout(30000) 24 this.timeout(30000)
29 25
30 server = await flushAndRunServer(1) 26 server = await createSingleServer(1)
31 27
32 await setAccessTokensToServers([ server ]) 28 await setAccessTokensToServers([ server ])
33 29
34 await uploadVideo(server.url, server.accessToken, { name: localVideoName }) 30 await server.videos.upload({ attributes: { name: localVideoName } })
31
32 command = server.search
35 }) 33 })
36 34
37 describe('Default search', async function () { 35 describe('Default search', async function () {
@@ -39,163 +37,213 @@ describe('Test videos search', function () {
39 it('Should make a local videos search by default', async function () { 37 it('Should make a local videos search by default', async function () {
40 this.timeout(10000) 38 this.timeout(10000)
41 39
42 await updateCustomSubConfig(server.url, server.accessToken, { 40 await server.config.updateCustomSubConfig({
43 search: { 41 newConfig: {
44 searchIndex: { 42 search: {
45 enabled: true, 43 searchIndex: {
46 isDefaultSearch: false, 44 enabled: true,
47 disableLocalSearch: false 45 isDefaultSearch: false,
46 disableLocalSearch: false
47 }
48 } 48 }
49 } 49 }
50 }) 50 })
51 51
52 const res = await searchVideo(server.url, 'local video') 52 const body = await command.searchVideos({ search: 'local video' })
53 53
54 expect(res.body.total).to.equal(1) 54 expect(body.total).to.equal(1)
55 expect(res.body.data[0].name).to.equal(localVideoName) 55 expect(body.data[0].name).to.equal(localVideoName)
56 }) 56 })
57 57
58 it('Should make a local channels search by default', async function () { 58 it('Should make a local channels search by default', async function () {
59 const res = await searchVideoChannel(server.url, 'root') 59 const body = await command.searchChannels({ search: 'root' })
60 60
61 expect(res.body.total).to.equal(1) 61 expect(body.total).to.equal(1)
62 expect(res.body.data[0].name).to.equal('root_channel') 62 expect(body.data[0].name).to.equal('root_channel')
63 expect(res.body.data[0].host).to.equal('localhost:' + server.port) 63 expect(body.data[0].host).to.equal('localhost:' + server.port)
64 }) 64 })
65 65
66 it('Should make an index videos search by default', async function () { 66 it('Should make an index videos search by default', async function () {
67 await updateCustomSubConfig(server.url, server.accessToken, { 67 await server.config.updateCustomSubConfig({
68 search: { 68 newConfig: {
69 searchIndex: { 69 search: {
70 enabled: true, 70 searchIndex: {
71 isDefaultSearch: true, 71 enabled: true,
72 disableLocalSearch: false 72 isDefaultSearch: true,
73 disableLocalSearch: false
74 }
73 } 75 }
74 } 76 }
75 }) 77 })
76 78
77 const res = await searchVideo(server.url, 'local video') 79 const body = await command.searchVideos({ search: 'local video' })
78 expect(res.body.total).to.be.greaterThan(2) 80 expect(body.total).to.be.greaterThan(2)
79 }) 81 })
80 82
81 it('Should make an index channels search by default', async function () { 83 it('Should make an index channels search by default', async function () {
82 const res = await searchVideoChannel(server.url, 'root') 84 const body = await command.searchChannels({ search: 'root' })
83 expect(res.body.total).to.be.greaterThan(2) 85 expect(body.total).to.be.greaterThan(2)
84 }) 86 })
85 87
86 it('Should make an index videos search if local search is disabled', async function () { 88 it('Should make an index videos search if local search is disabled', async function () {
87 await updateCustomSubConfig(server.url, server.accessToken, { 89 await server.config.updateCustomSubConfig({
88 search: { 90 newConfig: {
89 searchIndex: { 91 search: {
90 enabled: true, 92 searchIndex: {
91 isDefaultSearch: false, 93 enabled: true,
92 disableLocalSearch: true 94 isDefaultSearch: false,
95 disableLocalSearch: true
96 }
93 } 97 }
94 } 98 }
95 }) 99 })
96 100
97 const res = await searchVideo(server.url, 'local video') 101 const body = await command.searchVideos({ search: 'local video' })
98 expect(res.body.total).to.be.greaterThan(2) 102 expect(body.total).to.be.greaterThan(2)
99 }) 103 })
100 104
101 it('Should make an index channels search if local search is disabled', async function () { 105 it('Should make an index channels search if local search is disabled', async function () {
102 const res = await searchVideoChannel(server.url, 'root') 106 const body = await command.searchChannels({ search: 'root' })
103 expect(res.body.total).to.be.greaterThan(2) 107 expect(body.total).to.be.greaterThan(2)
104 }) 108 })
105 }) 109 })
106 110
107 describe('Videos search', async function () { 111 describe('Videos search', async function () {
108 112
113 async function check (search: VideosSearchQuery, exists = true) {
114 const body = await command.advancedVideoSearch({ search })
115
116 if (exists === false) {
117 expect(body.total).to.equal(0)
118 expect(body.data).to.have.lengthOf(0)
119 return
120 }
121
122 expect(body.total).to.equal(1)
123 expect(body.data).to.have.lengthOf(1)
124
125 const video = body.data[0]
126
127 expect(video.name).to.equal('What is PeerTube?')
128 expect(video.category.label).to.equal('Science & Technology')
129 expect(video.licence.label).to.equal('Attribution - Share Alike')
130 expect(video.privacy.label).to.equal('Public')
131 expect(video.duration).to.equal(113)
132 expect(video.thumbnailUrl.startsWith('https://framatube.org/static/thumbnails')).to.be.true
133
134 expect(video.account.host).to.equal('framatube.org')
135 expect(video.account.name).to.equal('framasoft')
136 expect(video.account.url).to.equal('https://framatube.org/accounts/framasoft')
137 expect(video.account.avatar).to.exist
138
139 expect(video.channel.host).to.equal('framatube.org')
140 expect(video.channel.name).to.equal('bf54d359-cfad-4935-9d45-9d6be93f63e8')
141 expect(video.channel.url).to.equal('https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8')
142 expect(video.channel.avatar).to.exist
143 }
144
145 const baseSearch: VideosSearchQuery = {
146 search: 'what is peertube',
147 start: 0,
148 count: 2,
149 categoryOneOf: [ 15 ],
150 licenceOneOf: [ 2 ],
151 tagsAllOf: [ 'framasoft', 'peertube' ],
152 startDate: '2018-10-01T10:50:46.396Z',
153 endDate: '2018-10-01T10:55:46.396Z'
154 }
155
109 it('Should make a simple search and not have results', async function () { 156 it('Should make a simple search and not have results', async function () {
110 const res = await searchVideo(server.url, 'djidane'.repeat(50)) 157 const body = await command.searchVideos({ search: 'djidane'.repeat(50) })
111 158
112 expect(res.body.total).to.equal(0) 159 expect(body.total).to.equal(0)
113 expect(res.body.data).to.have.lengthOf(0) 160 expect(body.data).to.have.lengthOf(0)
114 }) 161 })
115 162
116 it('Should make a simple search and have results', async function () { 163 it('Should make a simple search and have results', async function () {
117 const res = await searchVideo(server.url, 'What is PeerTube') 164 const body = await command.searchVideos({ search: 'What is PeerTube' })
118 165
119 expect(res.body.total).to.be.greaterThan(1) 166 expect(body.total).to.be.greaterThan(1)
120 }) 167 })
121 168
122 it('Should make a complex search', async function () { 169 it('Should make a simple search', async function () {
123 170 await check(baseSearch)
124 async function check (search: VideosSearchQuery, exists = true) { 171 })
125 const res = await advancedVideosSearch(server.url, search)
126
127 if (exists === false) {
128 expect(res.body.total).to.equal(0)
129 expect(res.body.data).to.have.lengthOf(0)
130 return
131 }
132
133 expect(res.body.total).to.equal(1)
134 expect(res.body.data).to.have.lengthOf(1)
135
136 const video: Video = res.body.data[0]
137
138 expect(video.name).to.equal('What is PeerTube?')
139 expect(video.category.label).to.equal('Science & Technology')
140 expect(video.licence.label).to.equal('Attribution - Share Alike')
141 expect(video.privacy.label).to.equal('Public')
142 expect(video.duration).to.equal(113)
143 expect(video.thumbnailUrl.startsWith('https://framatube.org/static/thumbnails')).to.be.true
144 172
145 expect(video.account.host).to.equal('framatube.org') 173 it('Should search by start date', async function () {
146 expect(video.account.name).to.equal('framasoft') 174 const search = { ...baseSearch, startDate: '2018-10-01T10:54:46.396Z' }
147 expect(video.account.url).to.equal('https://framatube.org/accounts/framasoft') 175 await check(search, false)
148 expect(video.account.avatar).to.exist 176 })
149 177
150 expect(video.channel.host).to.equal('framatube.org') 178 it('Should search by tags', async function () {
151 expect(video.channel.name).to.equal('bf54d359-cfad-4935-9d45-9d6be93f63e8') 179 const search = { ...baseSearch, tagsAllOf: [ 'toto', 'framasoft' ] }
152 expect(video.channel.url).to.equal('https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8') 180 await check(search, false)
153 expect(video.channel.avatar).to.exist 181 })
154 }
155 182
156 const baseSearch: VideosSearchQuery = { 183 it('Should search by duration', async function () {
157 search: 'what is peertube', 184 const search = { ...baseSearch, durationMin: 2000 }
158 start: 0, 185 await check(search, false)
159 count: 2, 186 })
160 categoryOneOf: [ 15 ],
161 licenceOneOf: [ 2 ],
162 tagsAllOf: [ 'framasoft', 'peertube' ],
163 startDate: '2018-10-01T10:50:46.396Z',
164 endDate: '2018-10-01T10:55:46.396Z'
165 }
166 187
188 it('Should search by nsfw attribute', async function () {
167 { 189 {
168 await check(baseSearch) 190 const search = { ...baseSearch, nsfw: 'true' as BooleanBothQuery }
191 await check(search, false)
169 } 192 }
170 193
171 { 194 {
172 const search = immutableAssign(baseSearch, { startDate: '2018-10-01T10:54:46.396Z' }) 195 const search = { ...baseSearch, nsfw: 'false' as BooleanBothQuery }
173 await check(search, false) 196 await check(search, true)
174 } 197 }
175 198
176 { 199 {
177 const search = immutableAssign(baseSearch, { tagsAllOf: [ 'toto', 'framasoft' ] }) 200 const search = { ...baseSearch, nsfw: 'both' as BooleanBothQuery }
178 await check(search, false) 201 await check(search, true)
179 } 202 }
203 })
180 204
205 it('Should search by host', async function () {
181 { 206 {
182 const search = immutableAssign(baseSearch, { durationMin: 2000 }) 207 const search = { ...baseSearch, host: 'example.com' }
183 await check(search, false) 208 await check(search, false)
184 } 209 }
185 210
186 { 211 {
187 const search = immutableAssign(baseSearch, { nsfw: 'true' }) 212 const search = { ...baseSearch, host: 'framatube.org' }
188 await check(search, false) 213 await check(search, true)
189 } 214 }
215 })
216
217 it('Should search by uuids', async function () {
218 const goodUUID = '9c9de5e8-0a1e-484a-b099-e80766180a6d'
219 const goodShortUUID = 'kkGMgK9ZtnKfYAgnEtQxbv'
220 const badUUID = 'c29c5b77-4a04-493d-96a9-2e9267e308f0'
221 const badShortUUID = 'rP5RgUeX9XwTSrspCdkDej'
190 222
191 { 223 {
192 const search = immutableAssign(baseSearch, { nsfw: 'false' }) 224 const uuidsMatrix = [
193 await check(search, true) 225 [ goodUUID ],
226 [ goodUUID, badShortUUID ],
227 [ badShortUUID, goodShortUUID ],
228 [ goodUUID, goodShortUUID ]
229 ]
230
231 for (const uuids of uuidsMatrix) {
232 const search = { ...baseSearch, uuids }
233 await check(search, true)
234 }
194 } 235 }
195 236
196 { 237 {
197 const search = immutableAssign(baseSearch, { nsfw: 'both' }) 238 const uuidsMatrix = [
198 await check(search, true) 239 [ badUUID ],
240 [ badShortUUID ]
241 ]
242
243 for (const uuids of uuidsMatrix) {
244 const search = { ...baseSearch, uuids }
245 await check(search, false)
246 }
199 } 247 }
200 }) 248 })
201 249
@@ -206,37 +254,44 @@ describe('Test videos search', function () {
206 count: 5 254 count: 5
207 } 255 }
208 256
209 const res = await advancedVideosSearch(server.url, search) 257 const body = await command.advancedVideoSearch({ search })
210 258
211 expect(res.body.total).to.be.greaterThan(5) 259 expect(body.total).to.be.greaterThan(5)
212 expect(res.body.data).to.have.lengthOf(5) 260 expect(body.data).to.have.lengthOf(5)
213 }) 261 })
214 262
215 it('Should use the nsfw instance policy as default', async function () { 263 it('Should use the nsfw instance policy as default', async function () {
216 let nsfwUUID: string 264 let nsfwUUID: string
217 265
218 { 266 {
219 await updateCustomSubConfig(server.url, server.accessToken, { instance: { defaultNSFWPolicy: 'display' } }) 267 await server.config.updateCustomSubConfig({
268 newConfig: {
269 instance: { defaultNSFWPolicy: 'display' }
270 }
271 })
220 272
221 const res = await searchVideo(server.url, 'NSFW search index', '-match') 273 const body = await command.searchVideos({ search: 'NSFW search index', sort: '-match' })
222 const video = res.body.data[0] as Video 274 expect(body.data).to.have.length.greaterThan(0)
223 275
224 expect(res.body.data).to.have.length.greaterThan(0) 276 const video = body.data[0]
225 expect(video.nsfw).to.be.true 277 expect(video.nsfw).to.be.true
226 278
227 nsfwUUID = video.uuid 279 nsfwUUID = video.uuid
228 } 280 }
229 281
230 { 282 {
231 await updateCustomSubConfig(server.url, server.accessToken, { instance: { defaultNSFWPolicy: 'do_not_list' } }) 283 await server.config.updateCustomSubConfig({
284 newConfig: {
285 instance: { defaultNSFWPolicy: 'do_not_list' }
286 }
287 })
232 288
233 const res = await searchVideo(server.url, 'NSFW search index', '-match') 289 const body = await command.searchVideos({ search: 'NSFW search index', sort: '-match' })
234 290
235 try { 291 try {
236 expect(res.body.data).to.have.lengthOf(0) 292 expect(body.data).to.have.lengthOf(0)
237 } catch (err) { 293 } catch {
238 // 294 const video = body.data[0]
239 const video = res.body.data[0] as Video
240 295
241 expect(video.uuid).not.equal(nsfwUUID) 296 expect(video.uuid).not.equal(nsfwUUID)
242 } 297 }
@@ -246,20 +301,19 @@ describe('Test videos search', function () {
246 301
247 describe('Channels search', async function () { 302 describe('Channels search', async function () {
248 303
249 it('Should make a simple search and not have results', async function () { 304 async function check (search: VideoChannelsSearchQuery, exists = true) {
250 const res = await searchVideoChannel(server.url, 'a'.repeat(500)) 305 const body = await command.advancedChannelSearch({ search })
251 306
252 expect(res.body.total).to.equal(0) 307 if (exists === false) {
253 expect(res.body.data).to.have.lengthOf(0) 308 expect(body.total).to.equal(0)
254 }) 309 expect(body.data).to.have.lengthOf(0)
255 310 return
256 it('Should make a search and have results', async function () { 311 }
257 const res = await advancedVideoChannelSearch(server.url, { search: 'Framasoft', sort: 'createdAt' })
258 312
259 expect(res.body.total).to.be.greaterThan(0) 313 expect(body.total).to.be.greaterThan(0)
260 expect(res.body.data).to.have.length.greaterThan(0) 314 expect(body.data).to.have.length.greaterThan(0)
261 315
262 const videoChannel: VideoChannel = res.body.data[0] 316 const videoChannel = body.data[0]
263 expect(videoChannel.url).to.equal('https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8') 317 expect(videoChannel.url).to.equal('https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8')
264 expect(videoChannel.host).to.equal('framatube.org') 318 expect(videoChannel.host).to.equal('framatube.org')
265 expect(videoChannel.avatar).to.exist 319 expect(videoChannel.avatar).to.exist
@@ -269,32 +323,53 @@ describe('Test videos search', function () {
269 expect(videoChannel.ownerAccount.name).to.equal('framasoft') 323 expect(videoChannel.ownerAccount.name).to.equal('framasoft')
270 expect(videoChannel.ownerAccount.host).to.equal('framatube.org') 324 expect(videoChannel.ownerAccount.host).to.equal('framatube.org')
271 expect(videoChannel.ownerAccount.avatar).to.exist 325 expect(videoChannel.ownerAccount.avatar).to.exist
326 }
327
328 it('Should make a simple search and not have results', async function () {
329 const body = await command.searchChannels({ search: 'a'.repeat(500) })
330
331 expect(body.total).to.equal(0)
332 expect(body.data).to.have.lengthOf(0)
333 })
334
335 it('Should make a search and have results', async function () {
336 await check({ search: 'Framasoft', sort: 'createdAt' }, true)
337 })
338
339 it('Should make host search and have appropriate results', async function () {
340 await check({ search: 'Framasoft', host: 'example.com' }, false)
341 await check({ search: 'Framasoft', host: 'framatube.org' }, true)
342 })
343
344 it('Should make handles search and have appropriate results', async function () {
345 await check({ handles: [ 'bf54d359-cfad-4935-9d45-9d6be93f63e8@framatube.org' ] }, true)
346 await check({ handles: [ 'jeanine', 'bf54d359-cfad-4935-9d45-9d6be93f63e8@framatube.org' ] }, true)
347 await check({ handles: [ 'jeanine', 'chocobozzz_channel2@peertube2.cpy.re' ] }, false)
272 }) 348 })
273 349
274 it('Should have a correct pagination', async function () { 350 it('Should have a correct pagination', async function () {
275 const res = await advancedVideoChannelSearch(server.url, { search: 'root', start: 0, count: 2 }) 351 const body = await command.advancedChannelSearch({ search: { search: 'root', start: 0, count: 2 } })
276 352
277 expect(res.body.total).to.be.greaterThan(2) 353 expect(body.total).to.be.greaterThan(2)
278 expect(res.body.data).to.have.lengthOf(2) 354 expect(body.data).to.have.lengthOf(2)
279 }) 355 })
280 }) 356 })
281 357
282 describe('Playlists search', async function () { 358 describe('Playlists search', async function () {
283 359
284 it('Should make a simple search and not have results', async function () { 360 async function check (search: VideoPlaylistsSearchQuery, exists = true) {
285 const res = await searchVideoPlaylists(server.url, 'a'.repeat(500)) 361 const body = await command.advancedPlaylistSearch({ search })
286 362
287 expect(res.body.total).to.equal(0) 363 if (exists === false) {
288 expect(res.body.data).to.have.lengthOf(0) 364 expect(body.total).to.equal(0)
289 }) 365 expect(body.data).to.have.lengthOf(0)
290 366 return
291 it('Should make a search and have results', async function () { 367 }
292 const res = await advancedVideoPlaylistSearch(server.url, { search: 'E2E playlist', sort: '-match' })
293 368
294 expect(res.body.total).to.be.greaterThan(0) 369 expect(body.total).to.be.greaterThan(0)
295 expect(res.body.data).to.have.length.greaterThan(0) 370 expect(body.data).to.have.length.greaterThan(0)
296 371
297 const videoPlaylist: VideoPlaylist = res.body.data[0] 372 const videoPlaylist = body.data[0]
298 373
299 expect(videoPlaylist.url).to.equal('https://peertube2.cpy.re/videos/watch/playlist/73804a40-da9a-40c2-b1eb-2c6d9eec8f0a') 374 expect(videoPlaylist.url).to.equal('https://peertube2.cpy.re/videos/watch/playlist/73804a40-da9a-40c2-b1eb-2c6d9eec8f0a')
300 expect(videoPlaylist.thumbnailUrl).to.exist 375 expect(videoPlaylist.thumbnailUrl).to.exist
@@ -319,13 +394,62 @@ describe('Test videos search', function () {
319 expect(videoPlaylist.videoChannel.name).to.equal('chocobozzz_channel') 394 expect(videoPlaylist.videoChannel.name).to.equal('chocobozzz_channel')
320 expect(videoPlaylist.videoChannel.host).to.equal('peertube2.cpy.re') 395 expect(videoPlaylist.videoChannel.host).to.equal('peertube2.cpy.re')
321 expect(videoPlaylist.videoChannel.avatar).to.exist 396 expect(videoPlaylist.videoChannel.avatar).to.exist
397 }
398
399 it('Should make a simple search and not have results', async function () {
400 const body = await command.searchPlaylists({ search: 'a'.repeat(500) })
401
402 expect(body.total).to.equal(0)
403 expect(body.data).to.have.lengthOf(0)
404 })
405
406 it('Should make a search and have results', async function () {
407 await check({ search: 'E2E playlist', sort: '-match' }, true)
408 })
409
410 it('Should make host search and have appropriate results', async function () {
411 await check({ search: 'E2E playlist', host: 'example.com' }, false)
412 await check({ search: 'E2E playlist', host: 'peertube2.cpy.re', sort: '-match' }, true)
413 })
414
415 it('Should make a search by uuids and have appropriate results', async function () {
416 const goodUUID = '73804a40-da9a-40c2-b1eb-2c6d9eec8f0a'
417 const goodShortUUID = 'fgei1ws1oa6FCaJ2qZPG29'
418 const badUUID = 'c29c5b77-4a04-493d-96a9-2e9267e308f0'
419 const badShortUUID = 'rP5RgUeX9XwTSrspCdkDej'
420
421 {
422 const uuidsMatrix = [
423 [ goodUUID ],
424 [ goodUUID, badShortUUID ],
425 [ badShortUUID, goodShortUUID ],
426 [ goodUUID, goodShortUUID ]
427 ]
428
429 for (const uuids of uuidsMatrix) {
430 const search = { search: 'E2E playlist', sort: '-match', uuids }
431 await check(search, true)
432 }
433 }
434
435 {
436 const uuidsMatrix = [
437 [ badUUID ],
438 [ badShortUUID ]
439 ]
440
441 for (const uuids of uuidsMatrix) {
442 const search = { search: 'E2E playlist', sort: '-match', uuids }
443 await check(search, false)
444 }
445 }
322 }) 446 })
323 447
324 it('Should have a correct pagination', async function () { 448 it('Should have a correct pagination', async function () {
325 const res = await advancedVideoChannelSearch(server.url, { search: 'root', start: 0, count: 2 }) 449 const body = await command.advancedChannelSearch({ search: { search: 'root', start: 0, count: 2 } })
326 450
327 expect(res.body.total).to.be.greaterThan(2) 451 expect(body.total).to.be.greaterThan(2)
328 expect(res.body.data).to.have.lengthOf(2) 452 expect(body.data).to.have.lengthOf(2)
329 }) 453 })
330 }) 454 })
331 455
diff --git a/server/tests/api/search/search-playlists.ts b/server/tests/api/search/search-playlists.ts
index ab17d55e9..15aac029a 100644
--- a/server/tests/api/search/search-playlists.ts
+++ b/server/tests/api/search/search-playlists.ts
@@ -2,82 +2,86 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoPlaylist, VideoPlaylistPrivacy } from '@shared/models'
6import { 5import {
7 addVideoInPlaylist,
8 advancedVideoPlaylistSearch,
9 cleanupTests, 6 cleanupTests,
10 createVideoPlaylist, 7 createSingleServer,
11 flushAndRunServer, 8 doubleFollow,
12 searchVideoPlaylists, 9 PeerTubeServer,
13 ServerInfo, 10 SearchCommand,
14 setAccessTokensToServers, 11 setAccessTokensToServers,
15 setDefaultVideoChannel, 12 setDefaultVideoChannel
16 uploadVideoAndGetId 13} from '@shared/extra-utils'
17} from '../../../../shared/extra-utils' 14import { VideoPlaylistPrivacy } from '@shared/models'
18 15
19const expect = chai.expect 16const expect = chai.expect
20 17
21describe('Test playlists search', function () { 18describe('Test playlists search', function () {
22 let server: ServerInfo = null 19 let server: PeerTubeServer
20 let remoteServer: PeerTubeServer
21 let command: SearchCommand
22 let playlistUUID: string
23 let playlistShortUUID: string
23 24
24 before(async function () { 25 before(async function () {
25 this.timeout(30000) 26 this.timeout(120000)
26 27
27 server = await flushAndRunServer(1) 28 const servers = await Promise.all([
29 createSingleServer(1),
30 createSingleServer(2, { transcoding: { enabled: false } })
31 ])
32 server = servers[0]
33 remoteServer = servers[1]
28 34
29 await setAccessTokensToServers([ server ]) 35 await setAccessTokensToServers([ remoteServer, server ])
30 await setDefaultVideoChannel([ server ]) 36 await setDefaultVideoChannel([ remoteServer, server ])
31
32 const videoId = (await uploadVideoAndGetId({ server: server, videoName: 'video' })).uuid
33 37
34 { 38 {
39 const videoId = (await server.videos.upload()).uuid
40
35 const attributes = { 41 const attributes = {
36 displayName: 'Dr. Kenzo Tenma hospital videos', 42 displayName: 'Dr. Kenzo Tenma hospital videos',
37 privacy: VideoPlaylistPrivacy.PUBLIC, 43 privacy: VideoPlaylistPrivacy.PUBLIC,
38 videoChannelId: server.videoChannel.id 44 videoChannelId: server.store.channel.id
39 } 45 }
40 const res = await createVideoPlaylist({ url: server.url, token: server.accessToken, playlistAttrs: attributes }) 46 const created = await server.playlists.create({ attributes })
41 47 playlistUUID = created.uuid
42 await addVideoInPlaylist({ 48 playlistShortUUID = created.shortUUID
43 url: server.url, 49
44 token: server.accessToken, 50 await server.playlists.addElement({ playlistId: created.id, attributes: { videoId } })
45 playlistId: res.body.videoPlaylist.id,
46 elementAttrs: { videoId }
47 })
48 } 51 }
49 52
50 { 53 {
54 const videoId = (await remoteServer.videos.upload()).uuid
55
51 const attributes = { 56 const attributes = {
52 displayName: 'Johan & Anna Libert musics', 57 displayName: 'Johan & Anna Libert music videos',
53 privacy: VideoPlaylistPrivacy.PUBLIC, 58 privacy: VideoPlaylistPrivacy.PUBLIC,
54 videoChannelId: server.videoChannel.id 59 videoChannelId: remoteServer.store.channel.id
55 } 60 }
56 const res = await createVideoPlaylist({ url: server.url, token: server.accessToken, playlistAttrs: attributes }) 61 const created = await remoteServer.playlists.create({ attributes })
57 62
58 await addVideoInPlaylist({ 63 await remoteServer.playlists.addElement({ playlistId: created.id, attributes: { videoId } })
59 url: server.url,
60 token: server.accessToken,
61 playlistId: res.body.videoPlaylist.id,
62 elementAttrs: { videoId }
63 })
64 } 64 }
65 65
66 { 66 {
67 const attributes = { 67 const attributes = {
68 displayName: 'Inspector Lunge playlist', 68 displayName: 'Inspector Lunge playlist',
69 privacy: VideoPlaylistPrivacy.PUBLIC, 69 privacy: VideoPlaylistPrivacy.PUBLIC,
70 videoChannelId: server.videoChannel.id 70 videoChannelId: server.store.channel.id
71 } 71 }
72 await createVideoPlaylist({ url: server.url, token: server.accessToken, playlistAttrs: attributes }) 72 await server.playlists.create({ attributes })
73 } 73 }
74
75 await doubleFollow(server, remoteServer)
76
77 command = server.search
74 }) 78 })
75 79
76 it('Should make a simple search and not have results', async function () { 80 it('Should make a simple search and not have results', async function () {
77 const res = await searchVideoPlaylists(server.url, 'abc') 81 const body = await command.searchPlaylists({ search: 'abc' })
78 82
79 expect(res.body.total).to.equal(0) 83 expect(body.total).to.equal(0)
80 expect(res.body.data).to.have.lengthOf(0) 84 expect(body.data).to.have.lengthOf(0)
81 }) 85 })
82 86
83 it('Should make a search and have results', async function () { 87 it('Should make a search and have results', async function () {
@@ -87,27 +91,72 @@ describe('Test playlists search', function () {
87 start: 0, 91 start: 0,
88 count: 1 92 count: 1
89 } 93 }
90 const res = await advancedVideoPlaylistSearch(server.url, search) 94 const body = await command.advancedPlaylistSearch({ search })
91 expect(res.body.total).to.equal(1) 95 expect(body.total).to.equal(1)
92 expect(res.body.data).to.have.lengthOf(1) 96 expect(body.data).to.have.lengthOf(1)
93 97
94 const playlist: VideoPlaylist = res.body.data[0] 98 const playlist = body.data[0]
95 expect(playlist.displayName).to.equal('Dr. Kenzo Tenma hospital videos') 99 expect(playlist.displayName).to.equal('Dr. Kenzo Tenma hospital videos')
96 expect(playlist.url).to.equal(server.url + '/video-playlists/' + playlist.uuid) 100 expect(playlist.url).to.equal(server.url + '/video-playlists/' + playlist.uuid)
97 } 101 }
98 102
99 { 103 {
100 const search = { 104 const search = {
101 search: 'Anna Livert', 105 search: 'Anna Livert music',
102 start: 0, 106 start: 0,
103 count: 1 107 count: 1
104 } 108 }
105 const res = await advancedVideoPlaylistSearch(server.url, search) 109 const body = await command.advancedPlaylistSearch({ search })
106 expect(res.body.total).to.equal(1) 110 expect(body.total).to.equal(1)
107 expect(res.body.data).to.have.lengthOf(1) 111 expect(body.data).to.have.lengthOf(1)
112
113 const playlist = body.data[0]
114 expect(playlist.displayName).to.equal('Johan & Anna Libert music videos')
115 }
116 })
117
118 it('Should filter by host', async function () {
119 {
120 const search = { search: 'tenma', host: server.host }
121 const body = await command.advancedPlaylistSearch({ search })
122 expect(body.total).to.equal(1)
123 expect(body.data).to.have.lengthOf(1)
124
125 const playlist = body.data[0]
126 expect(playlist.displayName).to.equal('Dr. Kenzo Tenma hospital videos')
127 }
128
129 {
130 const search = { search: 'Anna', host: 'example.com' }
131 const body = await command.advancedPlaylistSearch({ search })
132 expect(body.total).to.equal(0)
133 expect(body.data).to.have.lengthOf(0)
134 }
135
136 {
137 const search = { search: 'video', host: remoteServer.host }
138 const body = await command.advancedPlaylistSearch({ search })
139 expect(body.total).to.equal(1)
140 expect(body.data).to.have.lengthOf(1)
141
142 const playlist = body.data[0]
143 expect(playlist.displayName).to.equal('Johan & Anna Libert music videos')
144 }
145 })
146
147 it('Should filter by UUIDs', async function () {
148 for (const uuid of [ playlistUUID, playlistShortUUID ]) {
149 const body = await command.advancedPlaylistSearch({ search: { uuids: [ uuid ] } })
150
151 expect(body.total).to.equal(1)
152 expect(body.data[0].displayName).to.equal('Dr. Kenzo Tenma hospital videos')
153 }
154
155 {
156 const body = await command.advancedPlaylistSearch({ search: { uuids: [ 'dfd70b83-639f-4980-94af-304a56ab4b35' ] } })
108 157
109 const playlist: VideoPlaylist = res.body.data[0] 158 expect(body.total).to.equal(0)
110 expect(playlist.displayName).to.equal('Johan & Anna Libert musics') 159 expect(body.data).to.have.lengthOf(0)
111 } 160 }
112 }) 161 })
113 162
@@ -117,12 +166,12 @@ describe('Test playlists search', function () {
117 start: 0, 166 start: 0,
118 count: 1 167 count: 1
119 } 168 }
120 const res = await advancedVideoPlaylistSearch(server.url, search) 169 const body = await command.advancedPlaylistSearch({ search })
121 expect(res.body.total).to.equal(0) 170 expect(body.total).to.equal(0)
122 expect(res.body.data).to.have.lengthOf(0) 171 expect(body.data).to.have.lengthOf(0)
123 }) 172 })
124 173
125 after(async function () { 174 after(async function () {
126 await cleanupTests([ server ]) 175 await cleanupTests([ server, remoteServer ])
127 }) 176 })
128}) 177})
diff --git a/server/tests/api/search/search-videos.ts b/server/tests/api/search/search-videos.ts
index 5b8907961..bd1e4d266 100644
--- a/server/tests/api/search/search-videos.ts
+++ b/server/tests/api/search/search-videos.ts
@@ -2,40 +2,42 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoPrivacy } from '@shared/models'
6import { 5import {
7 advancedVideosSearch,
8 cleanupTests, 6 cleanupTests,
9 createLive, 7 createSingleServer,
10 flushAndRunServer, 8 doubleFollow,
11 immutableAssign, 9 PeerTubeServer,
12 searchVideo, 10 SearchCommand,
13 sendRTMPStreamInVideo,
14 ServerInfo,
15 setAccessTokensToServers, 11 setAccessTokensToServers,
16 setDefaultVideoChannel, 12 setDefaultVideoChannel,
17 stopFfmpeg, 13 stopFfmpeg,
18 updateCustomSubConfig, 14 wait
19 uploadVideo, 15} from '@shared/extra-utils'
20 wait, 16import { VideoPrivacy } from '@shared/models'
21 waitUntilLivePublished
22} from '../../../../shared/extra-utils'
23import { createVideoCaption } from '../../../../shared/extra-utils/videos/video-captions'
24 17
25const expect = chai.expect 18const expect = chai.expect
26 19
27describe('Test videos search', function () { 20describe('Test videos search', function () {
28 let server: ServerInfo = null 21 let server: PeerTubeServer
22 let remoteServer: PeerTubeServer
29 let startDate: string 23 let startDate: string
30 let videoUUID: string 24 let videoUUID: string
25 let videoShortUUID: string
26
27 let command: SearchCommand
31 28
32 before(async function () { 29 before(async function () {
33 this.timeout(60000) 30 this.timeout(120000)
34 31
35 server = await flushAndRunServer(1) 32 const servers = await Promise.all([
33 createSingleServer(1),
34 createSingleServer(2)
35 ])
36 server = servers[0]
37 remoteServer = servers[1]
36 38
37 await setAccessTokensToServers([ server ]) 39 await setAccessTokensToServers([ server, remoteServer ])
38 await setDefaultVideoChannel([ server ]) 40 await setDefaultVideoChannel([ server, remoteServer ])
39 41
40 { 42 {
41 const attributes1 = { 43 const attributes1 = {
@@ -46,57 +48,50 @@ describe('Test videos search', function () {
46 nsfw: false, 48 nsfw: false,
47 language: 'fr' 49 language: 'fr'
48 } 50 }
49 await uploadVideo(server.url, server.accessToken, attributes1) 51 await server.videos.upload({ attributes: attributes1 })
50 52
51 const attributes2 = immutableAssign(attributes1, { name: attributes1.name + ' - 2', fixture: 'video_short.mp4' }) 53 const attributes2 = { ...attributes1, name: attributes1.name + ' - 2', fixture: 'video_short.mp4' }
52 await uploadVideo(server.url, server.accessToken, attributes2) 54 await server.videos.upload({ attributes: attributes2 })
53 55
54 { 56 {
55 const attributes3 = immutableAssign(attributes1, { name: attributes1.name + ' - 3', language: undefined }) 57 const attributes3 = { ...attributes1, name: attributes1.name + ' - 3', language: undefined }
56 const res = await uploadVideo(server.url, server.accessToken, attributes3) 58 const { id, uuid, shortUUID } = await server.videos.upload({ attributes: attributes3 })
57 const videoId = res.body.video.id 59 videoUUID = uuid
58 videoUUID = res.body.video.uuid 60 videoShortUUID = shortUUID
59 61
60 await createVideoCaption({ 62 await server.captions.add({
61 url: server.url,
62 accessToken: server.accessToken,
63 language: 'en', 63 language: 'en',
64 videoId, 64 videoId: id,
65 fixture: 'subtitle-good2.vtt', 65 fixture: 'subtitle-good2.vtt',
66 mimeType: 'application/octet-stream' 66 mimeType: 'application/octet-stream'
67 }) 67 })
68 68
69 await createVideoCaption({ 69 await server.captions.add({
70 url: server.url,
71 accessToken: server.accessToken,
72 language: 'aa', 70 language: 'aa',
73 videoId, 71 videoId: id,
74 fixture: 'subtitle-good2.vtt', 72 fixture: 'subtitle-good2.vtt',
75 mimeType: 'application/octet-stream' 73 mimeType: 'application/octet-stream'
76 }) 74 })
77 } 75 }
78 76
79 const attributes4 = immutableAssign(attributes1, { name: attributes1.name + ' - 4', language: 'pl', nsfw: true }) 77 const attributes4 = { ...attributes1, name: attributes1.name + ' - 4', language: 'pl', nsfw: true }
80 await uploadVideo(server.url, server.accessToken, attributes4) 78 await server.videos.upload({ attributes: attributes4 })
81 79
82 await wait(1000) 80 await wait(1000)
83 81
84 startDate = new Date().toISOString() 82 startDate = new Date().toISOString()
85 83
86 const attributes5 = immutableAssign(attributes1, { name: attributes1.name + ' - 5', licence: 2, language: undefined }) 84 const attributes5 = { ...attributes1, name: attributes1.name + ' - 5', licence: 2, language: undefined }
87 await uploadVideo(server.url, server.accessToken, attributes5) 85 await server.videos.upload({ attributes: attributes5 })
88 86
89 const attributes6 = immutableAssign(attributes1, { name: attributes1.name + ' - 6', tags: [ 't1', 't2' ] }) 87 const attributes6 = { ...attributes1, name: attributes1.name + ' - 6', tags: [ 't1', 't2' ] }
90 await uploadVideo(server.url, server.accessToken, attributes6) 88 await server.videos.upload({ attributes: attributes6 })
91 89
92 const attributes7 = immutableAssign(attributes1, { 90 const attributes7 = { ...attributes1, name: attributes1.name + ' - 7', originallyPublishedAt: '2019-02-12T09:58:08.286Z' }
93 name: attributes1.name + ' - 7', 91 await server.videos.upload({ attributes: attributes7 })
94 originallyPublishedAt: '2019-02-12T09:58:08.286Z'
95 })
96 await uploadVideo(server.url, server.accessToken, attributes7)
97 92
98 const attributes8 = immutableAssign(attributes1, { name: attributes1.name + ' - 8', licence: 4 }) 93 const attributes8 = { ...attributes1, name: attributes1.name + ' - 8', licence: 4 }
99 await uploadVideo(server.url, server.accessToken, attributes8) 94 await server.videos.upload({ attributes: attributes8 })
100 } 95 }
101 96
102 { 97 {
@@ -107,9 +102,9 @@ describe('Test videos search', function () {
107 licence: 2, 102 licence: 2,
108 language: 'en' 103 language: 'en'
109 } 104 }
110 await uploadVideo(server.url, server.accessToken, attributes) 105 await server.videos.upload({ attributes: attributes })
111 106
112 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes, { name: attributes.name + ' duplicate' })) 107 await server.videos.upload({ attributes: { ...attributes, name: attributes.name + ' duplicate' } })
113 } 108 }
114 109
115 { 110 {
@@ -120,7 +115,7 @@ describe('Test videos search', function () {
120 licence: 3, 115 licence: 3,
121 language: 'pl' 116 language: 'pl'
122 } 117 }
123 await uploadVideo(server.url, server.accessToken, attributes) 118 await server.videos.upload({ attributes: attributes })
124 } 119 }
125 120
126 { 121 {
@@ -129,11 +124,11 @@ describe('Test videos search', function () {
129 tags: [ 'aaaa', 'bbbb', 'cccc' ], 124 tags: [ 'aaaa', 'bbbb', 'cccc' ],
130 category: 1 125 category: 1
131 } 126 }
132 await uploadVideo(server.url, server.accessToken, attributes1) 127 await server.videos.upload({ attributes: attributes1 })
133 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { category: 2 })) 128 await server.videos.upload({ attributes: { ...attributes1, category: 2 } })
134 129
135 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { tags: [ 'cccc', 'dddd' ] })) 130 await server.videos.upload({ attributes: { ...attributes1, tags: [ 'cccc', 'dddd' ] } })
136 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { tags: [ 'eeee', 'ffff' ] })) 131 await server.videos.upload({ attributes: { ...attributes1, tags: [ 'eeee', 'ffff' ] } })
137 } 132 }
138 133
139 { 134 {
@@ -141,24 +136,33 @@ describe('Test videos search', function () {
141 name: 'aaaa 2', 136 name: 'aaaa 2',
142 category: 1 137 category: 1
143 } 138 }
144 await uploadVideo(server.url, server.accessToken, attributes1) 139 await server.videos.upload({ attributes: attributes1 })
145 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { category: 2 })) 140 await server.videos.upload({ attributes: { ...attributes1, category: 2 } })
141 }
142
143 {
144 await remoteServer.videos.upload({ attributes: { name: 'remote video 1' } })
145 await remoteServer.videos.upload({ attributes: { name: 'remote video 2' } })
146 } 146 }
147
148 await doubleFollow(server, remoteServer)
149
150 command = server.search
147 }) 151 })
148 152
149 it('Should make a simple search and not have results', async function () { 153 it('Should make a simple search and not have results', async function () {
150 const res = await searchVideo(server.url, 'abc') 154 const body = await command.searchVideos({ search: 'abc' })
151 155
152 expect(res.body.total).to.equal(0) 156 expect(body.total).to.equal(0)
153 expect(res.body.data).to.have.lengthOf(0) 157 expect(body.data).to.have.lengthOf(0)
154 }) 158 })
155 159
156 it('Should make a simple search and have results', async function () { 160 it('Should make a simple search and have results', async function () {
157 const res = await searchVideo(server.url, '4444 5555 duplicate') 161 const body = await command.searchVideos({ search: '4444 5555 duplicate' })
158 162
159 expect(res.body.total).to.equal(2) 163 expect(body.total).to.equal(2)
160 164
161 const videos = res.body.data 165 const videos = body.data
162 expect(videos).to.have.lengthOf(2) 166 expect(videos).to.have.lengthOf(2)
163 167
164 // bestmatch 168 // bestmatch
@@ -167,15 +171,15 @@ describe('Test videos search', function () {
167 }) 171 })
168 172
169 it('Should make a search on tags too, and have results', async function () { 173 it('Should make a search on tags too, and have results', async function () {
170 const query = { 174 const search = {
171 search: 'aaaa', 175 search: 'aaaa',
172 categoryOneOf: [ 1 ] 176 categoryOneOf: [ 1 ]
173 } 177 }
174 const res = await advancedVideosSearch(server.url, query) 178 const body = await command.advancedVideoSearch({ search })
175 179
176 expect(res.body.total).to.equal(2) 180 expect(body.total).to.equal(2)
177 181
178 const videos = res.body.data 182 const videos = body.data
179 expect(videos).to.have.lengthOf(2) 183 expect(videos).to.have.lengthOf(2)
180 184
181 // bestmatch 185 // bestmatch
@@ -184,14 +188,14 @@ describe('Test videos search', function () {
184 }) 188 })
185 189
186 it('Should filter on tags without a search', async function () { 190 it('Should filter on tags without a search', async function () {
187 const query = { 191 const search = {
188 tagsAllOf: [ 'bbbb' ] 192 tagsAllOf: [ 'bbbb' ]
189 } 193 }
190 const res = await advancedVideosSearch(server.url, query) 194 const body = await command.advancedVideoSearch({ search })
191 195
192 expect(res.body.total).to.equal(2) 196 expect(body.total).to.equal(2)
193 197
194 const videos = res.body.data 198 const videos = body.data
195 expect(videos).to.have.lengthOf(2) 199 expect(videos).to.have.lengthOf(2)
196 200
197 expect(videos[0].name).to.equal('9999') 201 expect(videos[0].name).to.equal('9999')
@@ -199,14 +203,14 @@ describe('Test videos search', function () {
199 }) 203 })
200 204
201 it('Should filter on category without a search', async function () { 205 it('Should filter on category without a search', async function () {
202 const query = { 206 const search = {
203 categoryOneOf: [ 3 ] 207 categoryOneOf: [ 3 ]
204 } 208 }
205 const res = await advancedVideosSearch(server.url, query) 209 const body = await command.advancedVideoSearch({ search: search })
206 210
207 expect(res.body.total).to.equal(1) 211 expect(body.total).to.equal(1)
208 212
209 const videos = res.body.data 213 const videos = body.data
210 expect(videos).to.have.lengthOf(1) 214 expect(videos).to.have.lengthOf(1)
211 215
212 expect(videos[0].name).to.equal('6666 7777 8888') 216 expect(videos[0].name).to.equal('6666 7777 8888')
@@ -218,11 +222,16 @@ describe('Test videos search', function () {
218 categoryOneOf: [ 1 ], 222 categoryOneOf: [ 1 ],
219 tagsOneOf: [ 'aAaa', 'ffff' ] 223 tagsOneOf: [ 'aAaa', 'ffff' ]
220 } 224 }
221 const res1 = await advancedVideosSearch(server.url, query)
222 expect(res1.body.total).to.equal(2)
223 225
224 const res2 = await advancedVideosSearch(server.url, immutableAssign(query, { tagsOneOf: [ 'blabla' ] })) 226 {
225 expect(res2.body.total).to.equal(0) 227 const body = await command.advancedVideoSearch({ search: query })
228 expect(body.total).to.equal(2)
229 }
230
231 {
232 const body = await command.advancedVideoSearch({ search: { ...query, tagsOneOf: [ 'blabla' ] } })
233 expect(body.total).to.equal(0)
234 }
226 }) 235 })
227 236
228 it('Should search by tags (all of)', async function () { 237 it('Should search by tags (all of)', async function () {
@@ -231,14 +240,21 @@ describe('Test videos search', function () {
231 categoryOneOf: [ 1 ], 240 categoryOneOf: [ 1 ],
232 tagsAllOf: [ 'CCcc' ] 241 tagsAllOf: [ 'CCcc' ]
233 } 242 }
234 const res1 = await advancedVideosSearch(server.url, query)
235 expect(res1.body.total).to.equal(2)
236 243
237 const res2 = await advancedVideosSearch(server.url, immutableAssign(query, { tagsAllOf: [ 'blAbla' ] })) 244 {
238 expect(res2.body.total).to.equal(0) 245 const body = await command.advancedVideoSearch({ search: query })
246 expect(body.total).to.equal(2)
247 }
248
249 {
250 const body = await command.advancedVideoSearch({ search: { ...query, tagsAllOf: [ 'blAbla' ] } })
251 expect(body.total).to.equal(0)
252 }
239 253
240 const res3 = await advancedVideosSearch(server.url, immutableAssign(query, { tagsAllOf: [ 'bbbb', 'CCCC' ] })) 254 {
241 expect(res3.body.total).to.equal(1) 255 const body = await command.advancedVideoSearch({ search: { ...query, tagsAllOf: [ 'bbbb', 'CCCC' ] } })
256 expect(body.total).to.equal(1)
257 }
242 }) 258 })
243 259
244 it('Should search by category', async function () { 260 it('Should search by category', async function () {
@@ -246,12 +262,17 @@ describe('Test videos search', function () {
246 search: '6666', 262 search: '6666',
247 categoryOneOf: [ 3 ] 263 categoryOneOf: [ 3 ]
248 } 264 }
249 const res1 = await advancedVideosSearch(server.url, query)
250 expect(res1.body.total).to.equal(1)
251 expect(res1.body.data[0].name).to.equal('6666 7777 8888')
252 265
253 const res2 = await advancedVideosSearch(server.url, immutableAssign(query, { categoryOneOf: [ 2 ] })) 266 {
254 expect(res2.body.total).to.equal(0) 267 const body = await command.advancedVideoSearch({ search: query })
268 expect(body.total).to.equal(1)
269 expect(body.data[0].name).to.equal('6666 7777 8888')
270 }
271
272 {
273 const body = await command.advancedVideoSearch({ search: { ...query, categoryOneOf: [ 2 ] } })
274 expect(body.total).to.equal(0)
275 }
255 }) 276 })
256 277
257 it('Should search by licence', async function () { 278 it('Should search by licence', async function () {
@@ -259,13 +280,18 @@ describe('Test videos search', function () {
259 search: '4444 5555', 280 search: '4444 5555',
260 licenceOneOf: [ 2 ] 281 licenceOneOf: [ 2 ]
261 } 282 }
262 const res1 = await advancedVideosSearch(server.url, query)
263 expect(res1.body.total).to.equal(2)
264 expect(res1.body.data[0].name).to.equal('3333 4444 5555')
265 expect(res1.body.data[1].name).to.equal('3333 4444 5555 duplicate')
266 283
267 const res2 = await advancedVideosSearch(server.url, immutableAssign(query, { licenceOneOf: [ 3 ] })) 284 {
268 expect(res2.body.total).to.equal(0) 285 const body = await command.advancedVideoSearch({ search: query })
286 expect(body.total).to.equal(2)
287 expect(body.data[0].name).to.equal('3333 4444 5555')
288 expect(body.data[1].name).to.equal('3333 4444 5555 duplicate')
289 }
290
291 {
292 const body = await command.advancedVideoSearch({ search: { ...query, licenceOneOf: [ 3 ] } })
293 expect(body.total).to.equal(0)
294 }
269 }) 295 })
270 296
271 it('Should search by languages', async function () { 297 it('Should search by languages', async function () {
@@ -275,23 +301,23 @@ describe('Test videos search', function () {
275 } 301 }
276 302
277 { 303 {
278 const res = await advancedVideosSearch(server.url, query) 304 const body = await command.advancedVideoSearch({ search: query })
279 expect(res.body.total).to.equal(2) 305 expect(body.total).to.equal(2)
280 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 3') 306 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
281 expect(res.body.data[1].name).to.equal('1111 2222 3333 - 4') 307 expect(body.data[1].name).to.equal('1111 2222 3333 - 4')
282 } 308 }
283 309
284 { 310 {
285 const res = await advancedVideosSearch(server.url, immutableAssign(query, { languageOneOf: [ 'pl', 'en', '_unknown' ] })) 311 const body = await command.advancedVideoSearch({ search: { ...query, languageOneOf: [ 'pl', 'en', '_unknown' ] } })
286 expect(res.body.total).to.equal(3) 312 expect(body.total).to.equal(3)
287 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 3') 313 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
288 expect(res.body.data[1].name).to.equal('1111 2222 3333 - 4') 314 expect(body.data[1].name).to.equal('1111 2222 3333 - 4')
289 expect(res.body.data[2].name).to.equal('1111 2222 3333 - 5') 315 expect(body.data[2].name).to.equal('1111 2222 3333 - 5')
290 } 316 }
291 317
292 { 318 {
293 const res = await advancedVideosSearch(server.url, immutableAssign(query, { languageOneOf: [ 'eo' ] })) 319 const body = await command.advancedVideoSearch({ search: { ...query, languageOneOf: [ 'eo' ] } })
294 expect(res.body.total).to.equal(0) 320 expect(body.total).to.equal(0)
295 } 321 }
296 }) 322 })
297 323
@@ -301,10 +327,10 @@ describe('Test videos search', function () {
301 startDate 327 startDate
302 } 328 }
303 329
304 const res = await advancedVideosSearch(server.url, query) 330 const body = await command.advancedVideoSearch({ search: query })
305 expect(res.body.total).to.equal(4) 331 expect(body.total).to.equal(4)
306 332
307 const videos = res.body.data 333 const videos = body.data
308 expect(videos[0].name).to.equal('1111 2222 3333 - 5') 334 expect(videos[0].name).to.equal('1111 2222 3333 - 5')
309 expect(videos[1].name).to.equal('1111 2222 3333 - 6') 335 expect(videos[1].name).to.equal('1111 2222 3333 - 6')
310 expect(videos[2].name).to.equal('1111 2222 3333 - 7') 336 expect(videos[2].name).to.equal('1111 2222 3333 - 7')
@@ -320,10 +346,10 @@ describe('Test videos search', function () {
320 licenceOneOf: [ 1, 4 ] 346 licenceOneOf: [ 1, 4 ]
321 } 347 }
322 348
323 const res = await advancedVideosSearch(server.url, query) 349 const body = await command.advancedVideoSearch({ search: query })
324 expect(res.body.total).to.equal(4) 350 expect(body.total).to.equal(4)
325 351
326 const videos = res.body.data 352 const videos = body.data
327 expect(videos[0].name).to.equal('1111 2222 3333') 353 expect(videos[0].name).to.equal('1111 2222 3333')
328 expect(videos[1].name).to.equal('1111 2222 3333 - 6') 354 expect(videos[1].name).to.equal('1111 2222 3333 - 6')
329 expect(videos[2].name).to.equal('1111 2222 3333 - 7') 355 expect(videos[2].name).to.equal('1111 2222 3333 - 7')
@@ -340,10 +366,10 @@ describe('Test videos search', function () {
340 sort: '-name' 366 sort: '-name'
341 } 367 }
342 368
343 const res = await advancedVideosSearch(server.url, query) 369 const body = await command.advancedVideoSearch({ search: query })
344 expect(res.body.total).to.equal(4) 370 expect(body.total).to.equal(4)
345 371
346 const videos = res.body.data 372 const videos = body.data
347 expect(videos[0].name).to.equal('1111 2222 3333 - 8') 373 expect(videos[0].name).to.equal('1111 2222 3333 - 8')
348 expect(videos[1].name).to.equal('1111 2222 3333 - 7') 374 expect(videos[1].name).to.equal('1111 2222 3333 - 7')
349 expect(videos[2].name).to.equal('1111 2222 3333 - 6') 375 expect(videos[2].name).to.equal('1111 2222 3333 - 6')
@@ -362,10 +388,10 @@ describe('Test videos search', function () {
362 count: 1 388 count: 1
363 } 389 }
364 390
365 const res = await advancedVideosSearch(server.url, query) 391 const body = await command.advancedVideoSearch({ search: query })
366 expect(res.body.total).to.equal(4) 392 expect(body.total).to.equal(4)
367 393
368 const videos = res.body.data 394 const videos = body.data
369 expect(videos[0].name).to.equal('1111 2222 3333 - 8') 395 expect(videos[0].name).to.equal('1111 2222 3333 - 8')
370 }) 396 })
371 397
@@ -381,10 +407,10 @@ describe('Test videos search', function () {
381 count: 1 407 count: 1
382 } 408 }
383 409
384 const res = await advancedVideosSearch(server.url, query) 410 const body = await command.advancedVideoSearch({ search: query })
385 expect(res.body.total).to.equal(4) 411 expect(body.total).to.equal(4)
386 412
387 const videos = res.body.data 413 const videos = body.data
388 expect(videos[0].name).to.equal('1111 2222 3333') 414 expect(videos[0].name).to.equal('1111 2222 3333')
389 }) 415 })
390 416
@@ -398,99 +424,140 @@ describe('Test videos search', function () {
398 } 424 }
399 425
400 { 426 {
401 const query = immutableAssign(baseQuery, { originallyPublishedStartDate: '2019-02-11T09:58:08.286Z' }) 427 const query = { ...baseQuery, originallyPublishedStartDate: '2019-02-11T09:58:08.286Z' }
402 const res = await advancedVideosSearch(server.url, query) 428 const body = await command.advancedVideoSearch({ search: query })
403 429
404 expect(res.body.total).to.equal(1) 430 expect(body.total).to.equal(1)
405 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 7') 431 expect(body.data[0].name).to.equal('1111 2222 3333 - 7')
406 } 432 }
407 433
408 { 434 {
409 const query = immutableAssign(baseQuery, { originallyPublishedEndDate: '2019-03-11T09:58:08.286Z' }) 435 const query = { ...baseQuery, originallyPublishedEndDate: '2019-03-11T09:58:08.286Z' }
410 const res = await advancedVideosSearch(server.url, query) 436 const body = await command.advancedVideoSearch({ search: query })
411 437
412 expect(res.body.total).to.equal(1) 438 expect(body.total).to.equal(1)
413 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 7') 439 expect(body.data[0].name).to.equal('1111 2222 3333 - 7')
414 } 440 }
415 441
416 { 442 {
417 const query = immutableAssign(baseQuery, { originallyPublishedEndDate: '2019-01-11T09:58:08.286Z' }) 443 const query = { ...baseQuery, originallyPublishedEndDate: '2019-01-11T09:58:08.286Z' }
418 const res = await advancedVideosSearch(server.url, query) 444 const body = await command.advancedVideoSearch({ search: query })
419 445
420 expect(res.body.total).to.equal(0) 446 expect(body.total).to.equal(0)
421 } 447 }
422 448
423 { 449 {
424 const query = immutableAssign(baseQuery, { originallyPublishedStartDate: '2019-03-11T09:58:08.286Z' }) 450 const query = { ...baseQuery, originallyPublishedStartDate: '2019-03-11T09:58:08.286Z' }
425 const res = await advancedVideosSearch(server.url, query) 451 const body = await command.advancedVideoSearch({ search: query })
426 452
427 expect(res.body.total).to.equal(0) 453 expect(body.total).to.equal(0)
428 } 454 }
429 455
430 { 456 {
431 const query = immutableAssign(baseQuery, { 457 const query = {
458 ...baseQuery,
432 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z', 459 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z',
433 originallyPublishedEndDate: '2019-01-10T09:58:08.286Z' 460 originallyPublishedEndDate: '2019-01-10T09:58:08.286Z'
434 }) 461 }
435 const res = await advancedVideosSearch(server.url, query) 462 const body = await command.advancedVideoSearch({ search: query })
436 463
437 expect(res.body.total).to.equal(0) 464 expect(body.total).to.equal(0)
438 } 465 }
439 466
440 { 467 {
441 const query = immutableAssign(baseQuery, { 468 const query = {
469 ...baseQuery,
442 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z', 470 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z',
443 originallyPublishedEndDate: '2019-04-11T09:58:08.286Z' 471 originallyPublishedEndDate: '2019-04-11T09:58:08.286Z'
444 }) 472 }
445 const res = await advancedVideosSearch(server.url, query) 473 const body = await command.advancedVideoSearch({ search: query })
446 474
447 expect(res.body.total).to.equal(1) 475 expect(body.total).to.equal(1)
448 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 7') 476 expect(body.data[0].name).to.equal('1111 2222 3333 - 7')
449 } 477 }
450 }) 478 })
451 479
452 it('Should search by UUID', async function () { 480 it('Should search by UUID', async function () {
453 const search = videoUUID 481 const search = videoUUID
454 const res = await advancedVideosSearch(server.url, { search }) 482 const body = await command.advancedVideoSearch({ search: { search } })
483
484 expect(body.total).to.equal(1)
485 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
486 })
487
488 it('Should filter by UUIDs', async function () {
489 for (const uuid of [ videoUUID, videoShortUUID ]) {
490 const body = await command.advancedVideoSearch({ search: { uuids: [ uuid ] } })
491
492 expect(body.total).to.equal(1)
493 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
494 }
495
496 {
497 const body = await command.advancedVideoSearch({ search: { uuids: [ 'dfd70b83-639f-4980-94af-304a56ab4b35' ] } })
498
499 expect(body.total).to.equal(0)
500 expect(body.data).to.have.lengthOf(0)
501 }
502 })
503
504 it('Should search by host', async function () {
505 {
506 const body = await command.advancedVideoSearch({ search: { search: '6666 7777 8888', host: server.host } })
507 expect(body.total).to.equal(1)
508 expect(body.data[0].name).to.equal('6666 7777 8888')
509 }
455 510
456 expect(res.body.total).to.equal(1) 511 {
457 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 3') 512 const body = await command.advancedVideoSearch({ search: { search: '1111', host: 'example.com' } })
513 expect(body.total).to.equal(0)
514 expect(body.data).to.have.lengthOf(0)
515 }
516
517 {
518 const body = await command.advancedVideoSearch({ search: { search: 'remote', host: remoteServer.host } })
519 expect(body.total).to.equal(2)
520 expect(body.data).to.have.lengthOf(2)
521 expect(body.data[0].name).to.equal('remote video 1')
522 expect(body.data[1].name).to.equal('remote video 2')
523 }
458 }) 524 })
459 525
460 it('Should search by live', async function () { 526 it('Should search by live', async function () {
461 this.timeout(30000) 527 this.timeout(60000)
462 528
463 { 529 {
464 const options = { 530 const newConfig = {
465 search: { 531 search: {
466 searchIndex: { enabled: false } 532 searchIndex: { enabled: false }
467 }, 533 },
468 live: { enabled: true } 534 live: { enabled: true }
469 } 535 }
470 await updateCustomSubConfig(server.url, server.accessToken, options) 536 await server.config.updateCustomSubConfig({ newConfig })
471 } 537 }
472 538
473 { 539 {
474 const res = await advancedVideosSearch(server.url, { isLive: true }) 540 const body = await command.advancedVideoSearch({ search: { isLive: true } })
475 541
476 expect(res.body.total).to.equal(0) 542 expect(body.total).to.equal(0)
477 expect(res.body.data).to.have.lengthOf(0) 543 expect(body.data).to.have.lengthOf(0)
478 } 544 }
479 545
480 { 546 {
481 const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: server.videoChannel.id } 547 const liveCommand = server.live
482 const resLive = await createLive(server.url, server.accessToken, liveOptions) 548
483 const liveVideoId = resLive.body.video.uuid 549 const liveAttributes = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: server.store.channel.id }
550 const live = await liveCommand.create({ fields: liveAttributes })
484 551
485 const command = await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId) 552 const ffmpegCommand = await liveCommand.sendRTMPStreamInVideo({ videoId: live.id })
486 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) 553 await liveCommand.waitUntilPublished({ videoId: live.id })
487 554
488 const res = await advancedVideosSearch(server.url, { isLive: true }) 555 const body = await command.advancedVideoSearch({ search: { isLive: true } })
489 556
490 expect(res.body.total).to.equal(1) 557 expect(body.total).to.equal(1)
491 expect(res.body.data[0].name).to.equal('live') 558 expect(body.data[0].name).to.equal('live')
492 559
493 await stopFfmpeg(command) 560 await stopFfmpeg(ffmpegCommand)
494 } 561 }
495 }) 562 })
496 563
diff --git a/server/tests/api/server/auto-follows.ts b/server/tests/api/server/auto-follows.ts
index 1519b263f..ce7b51925 100644
--- a/server/tests/api/server/auto-follows.ts
+++ b/server/tests/api/server/auto-follows.ts
@@ -3,64 +3,45 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 acceptFollower,
7 cleanupTests, 6 cleanupTests,
8 flushAndRunMultipleServers, 7 createMultipleServers,
9 MockInstancesIndex, 8 MockInstancesIndex,
10 ServerInfo, 9 PeerTubeServer,
11 setAccessTokensToServers, 10 setAccessTokensToServers,
12 unfollow, 11 wait,
13 updateCustomSubConfig, 12 waitJobs
14 wait 13} from '@shared/extra-utils'
15} from '../../../../shared/extra-utils/index'
16import { follow, getFollowersListPaginationAndSort, getFollowingListPaginationAndSort } from '../../../../shared/extra-utils/server/follows'
17import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
18import { ActorFollow } from '../../../../shared/models/actors'
19 14
20const expect = chai.expect 15const expect = chai.expect
21 16
22async function checkFollow (follower: ServerInfo, following: ServerInfo, exists: boolean) { 17async function checkFollow (follower: PeerTubeServer, following: PeerTubeServer, exists: boolean) {
23 { 18 {
24 const res = await getFollowersListPaginationAndSort({ url: following.url, start: 0, count: 5, sort: '-createdAt' }) 19 const body = await following.follows.getFollowers({ start: 0, count: 5, sort: '-createdAt' })
25 const follows = res.body.data as ActorFollow[] 20 const follow = body.data.find(f => f.follower.host === follower.host && f.state === 'accepted')
26 21
27 const follow = follows.find(f => { 22 if (exists === true) expect(follow).to.exist
28 return f.follower.host === follower.host && f.state === 'accepted' 23 else expect(follow).to.be.undefined
29 })
30
31 if (exists === true) {
32 expect(follow).to.exist
33 } else {
34 expect(follow).to.be.undefined
35 }
36 } 24 }
37 25
38 { 26 {
39 const res = await getFollowingListPaginationAndSort({ url: follower.url, start: 0, count: 5, sort: '-createdAt' }) 27 const body = await follower.follows.getFollowings({ start: 0, count: 5, sort: '-createdAt' })
40 const follows = res.body.data as ActorFollow[] 28 const follow = body.data.find(f => f.following.host === following.host && f.state === 'accepted')
41
42 const follow = follows.find(f => {
43 return f.following.host === following.host && f.state === 'accepted'
44 })
45 29
46 if (exists === true) { 30 if (exists === true) expect(follow).to.exist
47 expect(follow).to.exist 31 else expect(follow).to.be.undefined
48 } else {
49 expect(follow).to.be.undefined
50 }
51 } 32 }
52} 33}
53 34
54async function server1Follows2 (servers: ServerInfo[]) { 35async function server1Follows2 (servers: PeerTubeServer[]) {
55 await follow(servers[0].url, [ servers[1].host ], servers[0].accessToken) 36 await servers[0].follows.follow({ hosts: [ servers[1].host ] })
56 37
57 await waitJobs(servers) 38 await waitJobs(servers)
58} 39}
59 40
60async function resetFollows (servers: ServerInfo[]) { 41async function resetFollows (servers: PeerTubeServer[]) {
61 try { 42 try {
62 await unfollow(servers[0].url, servers[0].accessToken, servers[1]) 43 await servers[0].follows.unfollow({ target: servers[1] })
63 await unfollow(servers[1].url, servers[1].accessToken, servers[0]) 44 await servers[1].follows.unfollow({ target: servers[0] })
64 } catch { /* empty */ 45 } catch { /* empty */
65 } 46 }
66 47
@@ -71,12 +52,12 @@ async function resetFollows (servers: ServerInfo[]) {
71} 52}
72 53
73describe('Test auto follows', function () { 54describe('Test auto follows', function () {
74 let servers: ServerInfo[] = [] 55 let servers: PeerTubeServer[] = []
75 56
76 before(async function () { 57 before(async function () {
77 this.timeout(30000) 58 this.timeout(30000)
78 59
79 servers = await flushAndRunMultipleServers(3) 60 servers = await createMultipleServers(3)
80 61
81 // Get the access tokens 62 // Get the access tokens
82 await setAccessTokensToServers(servers) 63 await setAccessTokensToServers(servers)
@@ -105,7 +86,7 @@ describe('Test auto follows', function () {
105 } 86 }
106 } 87 }
107 } 88 }
108 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, config) 89 await servers[1].config.updateCustomSubConfig({ newConfig: config })
109 90
110 await server1Follows2(servers) 91 await server1Follows2(servers)
111 92
@@ -130,14 +111,14 @@ describe('Test auto follows', function () {
130 } 111 }
131 } 112 }
132 } 113 }
133 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, config) 114 await servers[1].config.updateCustomSubConfig({ newConfig: config })
134 115
135 await server1Follows2(servers) 116 await server1Follows2(servers)
136 117
137 await checkFollow(servers[0], servers[1], false) 118 await checkFollow(servers[0], servers[1], false)
138 await checkFollow(servers[1], servers[0], false) 119 await checkFollow(servers[1], servers[0], false)
139 120
140 await acceptFollower(servers[1].url, servers[1].accessToken, 'peertube@' + servers[0].host) 121 await servers[1].follows.acceptFollower({ follower: 'peertube@' + servers[0].host })
141 await waitJobs(servers) 122 await waitJobs(servers)
142 123
143 await checkFollow(servers[0], servers[1], true) 124 await checkFollow(servers[0], servers[1], true)
@@ -147,7 +128,7 @@ describe('Test auto follows', function () {
147 128
148 config.followings.instance.autoFollowBack.enabled = false 129 config.followings.instance.autoFollowBack.enabled = false
149 config.followers.instance.manualApproval = false 130 config.followers.instance.manualApproval = false
150 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, config) 131 await servers[1].config.updateCustomSubConfig({ newConfig: config })
151 }) 132 })
152 }) 133 })
153 134
@@ -184,7 +165,7 @@ describe('Test auto follows', function () {
184 } 165 }
185 } 166 }
186 } 167 }
187 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 168 await servers[0].config.updateCustomSubConfig({ newConfig: config })
188 169
189 await wait(5000) 170 await wait(5000)
190 await waitJobs(servers) 171 await waitJobs(servers)
diff --git a/server/tests/api/server/bulk.ts b/server/tests/api/server/bulk.ts
index 80fa7fce6..16cbcd5c3 100644
--- a/server/tests/api/server/bulk.ts
+++ b/server/tests/api/server/bulk.ts
@@ -2,91 +2,83 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { Video, VideoComment } from '@shared/models'
6import { 5import {
7 addVideoCommentReply, 6 BulkCommand,
8 addVideoCommentThread,
9 bulkRemoveCommentsOf,
10 cleanupTests, 7 cleanupTests,
11 createUser, 8 createMultipleServers,
12 doubleFollow, 9 doubleFollow,
13 flushAndRunMultipleServers, 10 PeerTubeServer,
14 getVideoCommentThreads,
15 getVideosList,
16 ServerInfo,
17 setAccessTokensToServers, 11 setAccessTokensToServers,
18 uploadVideo,
19 userLogin,
20 waitJobs 12 waitJobs
21} from '../../../../shared/extra-utils/index' 13} from '@shared/extra-utils'
22 14
23const expect = chai.expect 15const expect = chai.expect
24 16
25describe('Test bulk actions', function () { 17describe('Test bulk actions', function () {
26 const commentsUser3: { videoId: number, commentId: number }[] = [] 18 const commentsUser3: { videoId: number, commentId: number }[] = []
27 19
28 let servers: ServerInfo[] = [] 20 let servers: PeerTubeServer[] = []
29 let user1AccessToken: string 21 let user1Token: string
30 let user2AccessToken: string 22 let user2Token: string
31 let user3AccessToken: string 23 let user3Token: string
24
25 let bulkCommand: BulkCommand
32 26
33 before(async function () { 27 before(async function () {
34 this.timeout(30000) 28 this.timeout(30000)
35 29
36 servers = await flushAndRunMultipleServers(2) 30 servers = await createMultipleServers(2)
37 31
38 // Get the access tokens 32 // Get the access tokens
39 await setAccessTokensToServers(servers) 33 await setAccessTokensToServers(servers)
40 34
41 { 35 {
42 const user = { username: 'user1', password: 'password' } 36 const user = { username: 'user1', password: 'password' }
43 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 37 await servers[0].users.create({ username: user.username, password: user.password })
44 38
45 user1AccessToken = await userLogin(servers[0], user) 39 user1Token = await servers[0].login.getAccessToken(user)
46 } 40 }
47 41
48 { 42 {
49 const user = { username: 'user2', password: 'password' } 43 const user = { username: 'user2', password: 'password' }
50 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 44 await servers[0].users.create({ username: user.username, password: user.password })
51 45
52 user2AccessToken = await userLogin(servers[0], user) 46 user2Token = await servers[0].login.getAccessToken(user)
53 } 47 }
54 48
55 { 49 {
56 const user = { username: 'user3', password: 'password' } 50 const user = { username: 'user3', password: 'password' }
57 await createUser({ url: servers[1].url, accessToken: servers[1].accessToken, username: user.username, password: user.password }) 51 await servers[1].users.create({ username: user.username, password: user.password })
58 52
59 user3AccessToken = await userLogin(servers[1], user) 53 user3Token = await servers[1].login.getAccessToken(user)
60 } 54 }
61 55
62 await doubleFollow(servers[0], servers[1]) 56 await doubleFollow(servers[0], servers[1])
57
58 bulkCommand = new BulkCommand(servers[0])
63 }) 59 })
64 60
65 describe('Bulk remove comments', function () { 61 describe('Bulk remove comments', function () {
66 async function checkInstanceCommentsRemoved () { 62 async function checkInstanceCommentsRemoved () {
67 { 63 {
68 const res = await getVideosList(servers[0].url) 64 const { data } = await servers[0].videos.list()
69 const videos = res.body.data as Video[]
70 65
71 // Server 1 should not have these comments anymore 66 // Server 1 should not have these comments anymore
72 for (const video of videos) { 67 for (const video of data) {
73 const resThreads = await getVideoCommentThreads(servers[0].url, video.id, 0, 10) 68 const { data } = await servers[0].comments.listThreads({ videoId: video.id })
74 const comments = resThreads.body.data as VideoComment[] 69 const comment = data.find(c => c.text === 'comment by user 3')
75 const comment = comments.find(c => c.text === 'comment by user 3')
76 70
77 expect(comment).to.not.exist 71 expect(comment).to.not.exist
78 } 72 }
79 } 73 }
80 74
81 { 75 {
82 const res = await getVideosList(servers[1].url) 76 const { data } = await servers[1].videos.list()
83 const videos = res.body.data as Video[]
84 77
85 // Server 1 should not have these comments on videos of server 1 78 // Server 1 should not have these comments on videos of server 1
86 for (const video of videos) { 79 for (const video of data) {
87 const resThreads = await getVideoCommentThreads(servers[1].url, video.id, 0, 10) 80 const { data } = await servers[1].comments.listThreads({ videoId: video.id })
88 const comments = resThreads.body.data as VideoComment[] 81 const comment = data.find(c => c.text === 'comment by user 3')
89 const comment = comments.find(c => c.text === 'comment by user 3')
90 82
91 if (video.account.host === 'localhost:' + servers[0].port) { 83 if (video.account.host === 'localhost:' + servers[0].port) {
92 expect(comment).to.not.exist 84 expect(comment).to.not.exist
@@ -100,30 +92,31 @@ describe('Test bulk actions', function () {
100 before(async function () { 92 before(async function () {
101 this.timeout(120000) 93 this.timeout(120000)
102 94
103 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video 1 server 1' }) 95 await servers[0].videos.upload({ attributes: { name: 'video 1 server 1' } })
104 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video 2 server 1' }) 96 await servers[0].videos.upload({ attributes: { name: 'video 2 server 1' } })
105 await uploadVideo(servers[0].url, user1AccessToken, { name: 'video 3 server 1' }) 97 await servers[0].videos.upload({ token: user1Token, attributes: { name: 'video 3 server 1' } })
106 98
107 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 1 server 2' }) 99 await servers[1].videos.upload({ attributes: { name: 'video 1 server 2' } })
108 100
109 await waitJobs(servers) 101 await waitJobs(servers)
110 102
111 { 103 {
112 const res = await getVideosList(servers[0].url) 104 const { data } = await servers[0].videos.list()
113 for (const video of res.body.data) { 105 for (const video of data) {
114 await addVideoCommentThread(servers[0].url, servers[0].accessToken, video.id, 'comment by root server 1') 106 await servers[0].comments.createThread({ videoId: video.id, text: 'comment by root server 1' })
115 await addVideoCommentThread(servers[0].url, user1AccessToken, video.id, 'comment by user 1') 107 await servers[0].comments.createThread({ token: user1Token, videoId: video.id, text: 'comment by user 1' })
116 await addVideoCommentThread(servers[0].url, user2AccessToken, video.id, 'comment by user 2') 108 await servers[0].comments.createThread({ token: user2Token, videoId: video.id, text: 'comment by user 2' })
117 } 109 }
118 } 110 }
119 111
120 { 112 {
121 const res = await getVideosList(servers[1].url) 113 const { data } = await servers[1].videos.list()
122 for (const video of res.body.data) {
123 await addVideoCommentThread(servers[1].url, servers[1].accessToken, video.id, 'comment by root server 2')
124 114
125 const res = await addVideoCommentThread(servers[1].url, user3AccessToken, video.id, 'comment by user 3') 115 for (const video of data) {
126 commentsUser3.push({ videoId: video.id, commentId: res.body.comment.id }) 116 await servers[1].comments.createThread({ videoId: video.id, text: 'comment by root server 2' })
117
118 const comment = await servers[1].comments.createThread({ token: user3Token, videoId: video.id, text: 'comment by user 3' })
119 commentsUser3.push({ videoId: video.id, commentId: comment.id })
127 } 120 }
128 } 121 }
129 122
@@ -133,9 +126,8 @@ describe('Test bulk actions', function () {
133 it('Should delete comments of an account on my videos', async function () { 126 it('Should delete comments of an account on my videos', async function () {
134 this.timeout(60000) 127 this.timeout(60000)
135 128
136 await bulkRemoveCommentsOf({ 129 await bulkCommand.removeCommentsOf({
137 url: servers[0].url, 130 token: user1Token,
138 token: user1AccessToken,
139 attributes: { 131 attributes: {
140 accountName: 'user2', 132 accountName: 'user2',
141 scope: 'my-videos' 133 scope: 'my-videos'
@@ -145,18 +137,14 @@ describe('Test bulk actions', function () {
145 await waitJobs(servers) 137 await waitJobs(servers)
146 138
147 for (const server of servers) { 139 for (const server of servers) {
148 const res = await getVideosList(server.url) 140 const { data } = await server.videos.list()
149 141
150 for (const video of res.body.data) { 142 for (const video of data) {
151 const resThreads = await getVideoCommentThreads(server.url, video.id, 0, 10) 143 const { data } = await server.comments.listThreads({ videoId: video.id })
152 const comments = resThreads.body.data as VideoComment[] 144 const comment = data.find(c => c.text === 'comment by user 2')
153 const comment = comments.find(c => c.text === 'comment by user 2')
154 145
155 if (video.name === 'video 3 server 1') { 146 if (video.name === 'video 3 server 1') expect(comment).to.not.exist
156 expect(comment).to.not.exist 147 else expect(comment).to.exist
157 } else {
158 expect(comment).to.exist
159 }
160 } 148 }
161 } 149 }
162 }) 150 })
@@ -164,9 +152,7 @@ describe('Test bulk actions', function () {
164 it('Should delete comments of an account on the instance', async function () { 152 it('Should delete comments of an account on the instance', async function () {
165 this.timeout(60000) 153 this.timeout(60000)
166 154
167 await bulkRemoveCommentsOf({ 155 await bulkCommand.removeCommentsOf({
168 url: servers[0].url,
169 token: servers[0].accessToken,
170 attributes: { 156 attributes: {
171 accountName: 'user3@localhost:' + servers[1].port, 157 accountName: 'user3@localhost:' + servers[1].port,
172 scope: 'instance' 158 scope: 'instance'
@@ -182,7 +168,12 @@ describe('Test bulk actions', function () {
182 this.timeout(60000) 168 this.timeout(60000)
183 169
184 for (const obj of commentsUser3) { 170 for (const obj of commentsUser3) {
185 await addVideoCommentReply(servers[1].url, user3AccessToken, obj.videoId, obj.commentId, 'comment by user 3 bis') 171 await servers[1].comments.addReply({
172 token: user3Token,
173 videoId: obj.videoId,
174 toCommentId: obj.commentId,
175 text: 'comment by user 3 bis'
176 })
186 } 177 }
187 178
188 await waitJobs(servers) 179 await waitJobs(servers)
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts
index 19bf9582c..fd61e95df 100644
--- a/server/tests/api/server/config.ts
+++ b/server/tests/api/server/config.ts
@@ -2,31 +2,20 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { About } from '../../../../shared/models/server/about.model'
6import { CustomConfig } from '../../../../shared/models/server/custom-config.model'
7import { 5import {
8 cleanupTests, 6 cleanupTests,
9 deleteCustomConfig, 7 createSingleServer,
10 flushAndRunServer,
11 getAbout,
12 getConfig,
13 getCustomConfig,
14 killallServers, 8 killallServers,
15 makeGetRequest, 9 makeGetRequest,
16 parallelTests, 10 parallelTests,
17 registerUser, 11 PeerTubeServer,
18 reRunServer, 12 setAccessTokensToServers
19 ServerInfo, 13} from '@shared/extra-utils'
20 setAccessTokensToServers, 14import { CustomConfig, HttpStatusCode } from '@shared/models'
21 updateCustomConfig,
22 uploadVideo
23} from '../../../../shared/extra-utils'
24import { ServerConfig } from '../../../../shared/models'
25import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
26 15
27const expect = chai.expect 16const expect = chai.expect
28 17
29function checkInitialConfig (server: ServerInfo, data: CustomConfig) { 18function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) {
30 expect(data.instance.name).to.equal('PeerTube') 19 expect(data.instance.name).to.equal('PeerTube')
31 expect(data.instance.shortDescription).to.equal( 20 expect(data.instance.shortDescription).to.equal(
32 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.' 21 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
@@ -213,18 +202,17 @@ function checkUpdatedConfig (data: CustomConfig) {
213} 202}
214 203
215describe('Test config', function () { 204describe('Test config', function () {
216 let server = null 205 let server: PeerTubeServer = null
217 206
218 before(async function () { 207 before(async function () {
219 this.timeout(30000) 208 this.timeout(30000)
220 209
221 server = await flushAndRunServer(1) 210 server = await createSingleServer(1)
222 await setAccessTokensToServers([ server ]) 211 await setAccessTokensToServers([ server ])
223 }) 212 })
224 213
225 it('Should have a correct config on a server with registration enabled', async function () { 214 it('Should have a correct config on a server with registration enabled', async function () {
226 const res = await getConfig(server.url) 215 const data = await server.config.getConfig()
227 const data: ServerConfig = res.body
228 216
229 expect(data.signup.allowed).to.be.true 217 expect(data.signup.allowed).to.be.true
230 }) 218 })
@@ -233,35 +221,32 @@ describe('Test config', function () {
233 this.timeout(5000) 221 this.timeout(5000)
234 222
235 await Promise.all([ 223 await Promise.all([
236 registerUser(server.url, 'user1', 'super password'), 224 server.users.register({ username: 'user1' }),
237 registerUser(server.url, 'user2', 'super password'), 225 server.users.register({ username: 'user2' }),
238 registerUser(server.url, 'user3', 'super password') 226 server.users.register({ username: 'user3' })
239 ]) 227 ])
240 228
241 const res = await getConfig(server.url) 229 const data = await server.config.getConfig()
242 const data: ServerConfig = res.body
243 230
244 expect(data.signup.allowed).to.be.false 231 expect(data.signup.allowed).to.be.false
245 }) 232 })
246 233
247 it('Should have the correct video allowed extensions', async function () { 234 it('Should have the correct video allowed extensions', async function () {
248 const res = await getConfig(server.url) 235 const data = await server.config.getConfig()
249 const data: ServerConfig = res.body
250 236
251 expect(data.video.file.extensions).to.have.lengthOf(3) 237 expect(data.video.file.extensions).to.have.lengthOf(3)
252 expect(data.video.file.extensions).to.contain('.mp4') 238 expect(data.video.file.extensions).to.contain('.mp4')
253 expect(data.video.file.extensions).to.contain('.webm') 239 expect(data.video.file.extensions).to.contain('.webm')
254 expect(data.video.file.extensions).to.contain('.ogv') 240 expect(data.video.file.extensions).to.contain('.ogv')
255 241
256 await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.mkv' }, HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415) 242 await server.videos.upload({ attributes: { fixture: 'video_short.mkv' }, expectedStatus: HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415 })
257 await uploadVideo(server.url, server.accessToken, { fixture: 'sample.ogg' }, HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415) 243 await server.videos.upload({ attributes: { fixture: 'sample.ogg' }, expectedStatus: HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415 })
258 244
259 expect(data.contactForm.enabled).to.be.true 245 expect(data.contactForm.enabled).to.be.true
260 }) 246 })
261 247
262 it('Should get the customized configuration', async function () { 248 it('Should get the customized configuration', async function () {
263 const res = await getCustomConfig(server.url, server.accessToken) 249 const data = await server.config.getCustomConfig()
264 const data = res.body as CustomConfig
265 250
266 checkInitialConfig(server, data) 251 checkInitialConfig(server, data)
267 }) 252 })
@@ -438,19 +423,16 @@ describe('Test config', function () {
438 } 423 }
439 } 424 }
440 } 425 }
441 await updateCustomConfig(server.url, server.accessToken, newCustomConfig) 426 await server.config.updateCustomConfig({ newCustomConfig })
442
443 const res = await getCustomConfig(server.url, server.accessToken)
444 const data = res.body
445 427
428 const data = await server.config.getCustomConfig()
446 checkUpdatedConfig(data) 429 checkUpdatedConfig(data)
447 }) 430 })
448 431
449 it('Should have the correct updated video allowed extensions', async function () { 432 it('Should have the correct updated video allowed extensions', async function () {
450 this.timeout(10000) 433 this.timeout(10000)
451 434
452 const res = await getConfig(server.url) 435 const data = await server.config.getConfig()
453 const data: ServerConfig = res.body
454 436
455 expect(data.video.file.extensions).to.have.length.above(4) 437 expect(data.video.file.extensions).to.have.length.above(4)
456 expect(data.video.file.extensions).to.contain('.mp4') 438 expect(data.video.file.extensions).to.contain('.mp4')
@@ -463,26 +445,24 @@ describe('Test config', function () {
463 expect(data.video.file.extensions).to.contain('.ogg') 445 expect(data.video.file.extensions).to.contain('.ogg')
464 expect(data.video.file.extensions).to.contain('.flac') 446 expect(data.video.file.extensions).to.contain('.flac')
465 447
466 await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.mkv' }, HttpStatusCode.OK_200) 448 await server.videos.upload({ attributes: { fixture: 'video_short.mkv' }, expectedStatus: HttpStatusCode.OK_200 })
467 await uploadVideo(server.url, server.accessToken, { fixture: 'sample.ogg' }, HttpStatusCode.OK_200) 449 await server.videos.upload({ attributes: { fixture: 'sample.ogg' }, expectedStatus: HttpStatusCode.OK_200 })
468 }) 450 })
469 451
470 it('Should have the configuration updated after a restart', async function () { 452 it('Should have the configuration updated after a restart', async function () {
471 this.timeout(10000) 453 this.timeout(10000)
472 454
473 killallServers([ server ]) 455 await killallServers([ server ])
474 456
475 await reRunServer(server) 457 await server.run()
476 458
477 const res = await getCustomConfig(server.url, server.accessToken) 459 const data = await server.config.getCustomConfig()
478 const data = res.body
479 460
480 checkUpdatedConfig(data) 461 checkUpdatedConfig(data)
481 }) 462 })
482 463
483 it('Should fetch the about information', async function () { 464 it('Should fetch the about information', async function () {
484 const res = await getAbout(server.url) 465 const data = await server.config.getAbout()
485 const data: About = res.body
486 466
487 expect(data.instance.name).to.equal('PeerTube updated') 467 expect(data.instance.name).to.equal('PeerTube updated')
488 expect(data.instance.shortDescription).to.equal('my short description') 468 expect(data.instance.shortDescription).to.equal('my short description')
@@ -504,11 +484,9 @@ describe('Test config', function () {
504 it('Should remove the custom configuration', async function () { 484 it('Should remove the custom configuration', async function () {
505 this.timeout(10000) 485 this.timeout(10000)
506 486
507 await deleteCustomConfig(server.url, server.accessToken) 487 await server.config.deleteCustomConfig()
508
509 const res = await getCustomConfig(server.url, server.accessToken)
510 const data = res.body
511 488
489 const data = await server.config.getCustomConfig()
512 checkInitialConfig(server, data) 490 checkInitialConfig(server, data)
513 }) 491 })
514 492
@@ -519,26 +497,26 @@ describe('Test config', function () {
519 const res = await makeGetRequest({ 497 const res = await makeGetRequest({
520 url: server.url, 498 url: server.url,
521 path: '/api/v1/config', 499 path: '/api/v1/config',
522 statusCodeExpected: 200 500 expectedStatus: 200
523 }) 501 })
524 502
525 expect(res.headers['x-frame-options']).to.exist 503 expect(res.headers['x-frame-options']).to.exist
526 } 504 }
527 505
528 killallServers([ server ]) 506 await killallServers([ server ])
529 507
530 const config = { 508 const config = {
531 security: { 509 security: {
532 frameguard: { enabled: false } 510 frameguard: { enabled: false }
533 } 511 }
534 } 512 }
535 server = await reRunServer(server, config) 513 await server.run(config)
536 514
537 { 515 {
538 const res = await makeGetRequest({ 516 const res = await makeGetRequest({
539 url: server.url, 517 url: server.url,
540 path: '/api/v1/config', 518 path: '/api/v1/config',
541 statusCodeExpected: 200 519 expectedStatus: 200
542 }) 520 })
543 521
544 expect(res.headers['x-frame-options']).to.not.exist 522 expect(res.headers['x-frame-options']).to.not.exist
diff --git a/server/tests/api/server/contact-form.ts b/server/tests/api/server/contact-form.ts
index 8851ad55e..c555661ad 100644
--- a/server/tests/api/server/contact-form.ts
+++ b/server/tests/api/server/contact-form.ts
@@ -1,18 +1,25 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { cleanupTests, flushAndRunServer, ServerInfo, setAccessTokensToServers, wait } from '../../../../shared/extra-utils' 4import * as chai from 'chai'
6import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email' 5import {
7import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 6 cleanupTests,
8import { sendContactForm } from '../../../../shared/extra-utils/server/contact-form' 7 ContactFormCommand,
9import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 8 createSingleServer,
9 MockSmtpServer,
10 PeerTubeServer,
11 setAccessTokensToServers,
12 wait,
13 waitJobs
14} from '@shared/extra-utils'
15import { HttpStatusCode } from '@shared/models'
10 16
11const expect = chai.expect 17const expect = chai.expect
12 18
13describe('Test contact form', function () { 19describe('Test contact form', function () {
14 let server: ServerInfo 20 let server: PeerTubeServer
15 const emails: object[] = [] 21 const emails: object[] = []
22 let command: ContactFormCommand
16 23
17 before(async function () { 24 before(async function () {
18 this.timeout(30000) 25 this.timeout(30000)
@@ -25,15 +32,16 @@ describe('Test contact form', function () {
25 port 32 port
26 } 33 }
27 } 34 }
28 server = await flushAndRunServer(1, overrideConfig) 35 server = await createSingleServer(1, overrideConfig)
29 await setAccessTokensToServers([ server ]) 36 await setAccessTokensToServers([ server ])
37
38 command = server.contactForm
30 }) 39 })
31 40
32 it('Should send a contact form', async function () { 41 it('Should send a contact form', async function () {
33 this.timeout(10000) 42 this.timeout(10000)
34 43
35 await sendContactForm({ 44 await command.send({
36 url: server.url,
37 fromEmail: 'toto@example.com', 45 fromEmail: 'toto@example.com',
38 body: 'my super message', 46 body: 'my super message',
39 subject: 'my subject', 47 subject: 'my subject',
@@ -58,16 +66,14 @@ describe('Test contact form', function () {
58 66
59 await wait(1000) 67 await wait(1000)
60 68
61 await sendContactForm({ 69 await command.send({
62 url: server.url,
63 fromEmail: 'toto@example.com', 70 fromEmail: 'toto@example.com',
64 body: 'my super message', 71 body: 'my super message',
65 subject: 'my subject', 72 subject: 'my subject',
66 fromName: 'Super toto' 73 fromName: 'Super toto'
67 }) 74 })
68 75
69 await sendContactForm({ 76 await command.send({
70 url: server.url,
71 fromEmail: 'toto@example.com', 77 fromEmail: 'toto@example.com',
72 body: 'my super message', 78 body: 'my super message',
73 fromName: 'Super toto', 79 fromName: 'Super toto',
@@ -79,8 +85,7 @@ describe('Test contact form', function () {
79 it('Should be able to send another contact form after a while', async function () { 85 it('Should be able to send another contact form after a while', async function () {
80 await wait(1000) 86 await wait(1000)
81 87
82 await sendContactForm({ 88 await command.send({
83 url: server.url,
84 fromEmail: 'toto@example.com', 89 fromEmail: 'toto@example.com',
85 fromName: 'Super toto', 90 fromName: 'Super toto',
86 subject: 'my subject', 91 subject: 'my subject',
diff --git a/server/tests/api/server/email.ts b/server/tests/api/server/email.ts
index 92768d9df..5f97edbc2 100644
--- a/server/tests/api/server/email.ts
+++ b/server/tests/api/server/email.ts
@@ -2,37 +2,18 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import { cleanupTests, createSingleServer, MockSmtpServer, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
6 addVideoToBlacklist, 6import { HttpStatusCode } from '@shared/models'
7 askResetPassword,
8 askSendVerifyEmail,
9 blockUser,
10 cleanupTests,
11 createUser,
12 flushAndRunServer,
13 removeVideoFromBlacklist,
14 reportAbuse,
15 resetPassword,
16 ServerInfo,
17 setAccessTokensToServers,
18 unblockUser,
19 uploadVideo,
20 userLogin,
21 verifyEmail
22} from '../../../../shared/extra-utils'
23import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
24import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
25import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
26 7
27const expect = chai.expect 8const expect = chai.expect
28 9
29describe('Test emails', function () { 10describe('Test emails', function () {
30 let server: ServerInfo 11 let server: PeerTubeServer
31 let userId: number 12 let userId: number
32 let userId2: number 13 let userId2: number
33 let userAccessToken: string 14 let userAccessToken: string
34 15
35 let videoUUID: string 16 let videoShortUUID: string
36 let videoId: number 17 let videoId: number
37 18
38 let videoUserUUID: string 19 let videoUserUUID: string
@@ -58,31 +39,29 @@ describe('Test emails', function () {
58 port: emailPort 39 port: emailPort
59 } 40 }
60 } 41 }
61 server = await flushAndRunServer(1, overrideConfig) 42 server = await createSingleServer(1, overrideConfig)
62 await setAccessTokensToServers([ server ]) 43 await setAccessTokensToServers([ server ])
63 44
64 { 45 {
65 const res = await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 46 const created = await server.users.create({ username: user.username, password: user.password })
66 userId = res.body.user.id 47 userId = created.id
67 48
68 userAccessToken = await userLogin(server, user) 49 userAccessToken = await server.login.getAccessToken(user)
69 } 50 }
70 51
71 { 52 {
72 const attributes = { 53 const attributes = { name: 'my super user video' }
73 name: 'my super user video' 54 const { uuid } = await server.videos.upload({ token: userAccessToken, attributes })
74 } 55 videoUserUUID = uuid
75 const res = await uploadVideo(server.url, userAccessToken, attributes)
76 videoUserUUID = res.body.video.uuid
77 } 56 }
78 57
79 { 58 {
80 const attributes = { 59 const attributes = {
81 name: 'my super name' 60 name: 'my super name'
82 } 61 }
83 const res = await uploadVideo(server.url, server.accessToken, attributes) 62 const { shortUUID, id } = await server.videos.upload({ attributes })
84 videoUUID = res.body.video.uuid 63 videoShortUUID = shortUUID
85 videoId = res.body.video.id 64 videoId = id
86 } 65 }
87 }) 66 })
88 67
@@ -91,7 +70,7 @@ describe('Test emails', function () {
91 it('Should ask to reset the password', async function () { 70 it('Should ask to reset the password', async function () {
92 this.timeout(10000) 71 this.timeout(10000)
93 72
94 await askResetPassword(server.url, 'user_1@example.com') 73 await server.users.askResetPassword({ email: 'user_1@example.com' })
95 74
96 await waitJobs(server) 75 await waitJobs(server)
97 expect(emails).to.have.lengthOf(1) 76 expect(emails).to.have.lengthOf(1)
@@ -117,34 +96,40 @@ describe('Test emails', function () {
117 }) 96 })
118 97
119 it('Should not reset the password with an invalid verification string', async function () { 98 it('Should not reset the password with an invalid verification string', async function () {
120 await resetPassword(server.url, userId, verificationString + 'b', 'super_password2', HttpStatusCode.FORBIDDEN_403) 99 await server.users.resetPassword({
100 userId,
101 verificationString: verificationString + 'b',
102 password: 'super_password2',
103 expectedStatus: HttpStatusCode.FORBIDDEN_403
104 })
121 }) 105 })
122 106
123 it('Should reset the password', async function () { 107 it('Should reset the password', async function () {
124 await resetPassword(server.url, userId, verificationString, 'super_password2') 108 await server.users.resetPassword({ userId, verificationString, password: 'super_password2' })
125 }) 109 })
126 110
127 it('Should not reset the password with the same verification string', async function () { 111 it('Should not reset the password with the same verification string', async function () {
128 await resetPassword(server.url, userId, verificationString, 'super_password3', HttpStatusCode.FORBIDDEN_403) 112 await server.users.resetPassword({
113 userId,
114 verificationString,
115 password: 'super_password3',
116 expectedStatus: HttpStatusCode.FORBIDDEN_403
117 })
129 }) 118 })
130 119
131 it('Should login with this new password', async function () { 120 it('Should login with this new password', async function () {
132 user.password = 'super_password2' 121 user.password = 'super_password2'
133 122
134 await userLogin(server, user) 123 await server.login.getAccessToken(user)
135 }) 124 })
136 }) 125 })
137 126
138 describe('When creating a user without password', function () { 127 describe('When creating a user without password', function () {
128
139 it('Should send a create password email', async function () { 129 it('Should send a create password email', async function () {
140 this.timeout(10000) 130 this.timeout(10000)
141 131
142 await createUser({ 132 await server.users.create({ username: 'create_password', password: '' })
143 url: server.url,
144 accessToken: server.accessToken,
145 username: 'create_password',
146 password: ''
147 })
148 133
149 await waitJobs(server) 134 await waitJobs(server)
150 expect(emails).to.have.lengthOf(2) 135 expect(emails).to.have.lengthOf(2)
@@ -170,15 +155,24 @@ describe('Test emails', function () {
170 }) 155 })
171 156
172 it('Should not reset the password with an invalid verification string', async function () { 157 it('Should not reset the password with an invalid verification string', async function () {
173 await resetPassword(server.url, userId2, verificationString2 + 'c', 'newly_created_password', HttpStatusCode.FORBIDDEN_403) 158 await server.users.resetPassword({
159 userId: userId2,
160 verificationString: verificationString2 + 'c',
161 password: 'newly_created_password',
162 expectedStatus: HttpStatusCode.FORBIDDEN_403
163 })
174 }) 164 })
175 165
176 it('Should reset the password', async function () { 166 it('Should reset the password', async function () {
177 await resetPassword(server.url, userId2, verificationString2, 'newly_created_password') 167 await server.users.resetPassword({
168 userId: userId2,
169 verificationString: verificationString2,
170 password: 'newly_created_password'
171 })
178 }) 172 })
179 173
180 it('Should login with this new password', async function () { 174 it('Should login with this new password', async function () {
181 await userLogin(server, { 175 await server.login.getAccessToken({
182 username: 'create_password', 176 username: 'create_password',
183 password: 'newly_created_password' 177 password: 'newly_created_password'
184 }) 178 })
@@ -186,11 +180,12 @@ describe('Test emails', function () {
186 }) 180 })
187 181
188 describe('When creating an abuse', function () { 182 describe('When creating an abuse', function () {
183
189 it('Should send the notification email', async function () { 184 it('Should send the notification email', async function () {
190 this.timeout(10000) 185 this.timeout(10000)
191 186
192 const reason = 'my super bad reason' 187 const reason = 'my super bad reason'
193 await reportAbuse({ url: server.url, token: server.accessToken, videoId, reason }) 188 await server.abuses.report({ videoId, reason })
194 189
195 await waitJobs(server) 190 await waitJobs(server)
196 expect(emails).to.have.lengthOf(3) 191 expect(emails).to.have.lengthOf(3)
@@ -201,7 +196,7 @@ describe('Test emails', function () {
201 expect(email['from'][0]['address']).equal('test-admin@localhost') 196 expect(email['from'][0]['address']).equal('test-admin@localhost')
202 expect(email['to'][0]['address']).equal('admin' + server.internalServerNumber + '@example.com') 197 expect(email['to'][0]['address']).equal('admin' + server.internalServerNumber + '@example.com')
203 expect(email['subject']).contains('abuse') 198 expect(email['subject']).contains('abuse')
204 expect(email['text']).contains(videoUUID) 199 expect(email['text']).contains(videoShortUUID)
205 }) 200 })
206 }) 201 })
207 202
@@ -211,7 +206,7 @@ describe('Test emails', function () {
211 this.timeout(10000) 206 this.timeout(10000)
212 207
213 const reason = 'my super bad reason' 208 const reason = 'my super bad reason'
214 await blockUser(server.url, userId, server.accessToken, HttpStatusCode.NO_CONTENT_204, reason) 209 await server.users.banUser({ userId, reason })
215 210
216 await waitJobs(server) 211 await waitJobs(server)
217 expect(emails).to.have.lengthOf(4) 212 expect(emails).to.have.lengthOf(4)
@@ -229,7 +224,7 @@ describe('Test emails', function () {
229 it('Should send the notification email when unblocking a user', async function () { 224 it('Should send the notification email when unblocking a user', async function () {
230 this.timeout(10000) 225 this.timeout(10000)
231 226
232 await unblockUser(server.url, userId, server.accessToken, HttpStatusCode.NO_CONTENT_204) 227 await server.users.unbanUser({ userId })
233 228
234 await waitJobs(server) 229 await waitJobs(server)
235 expect(emails).to.have.lengthOf(5) 230 expect(emails).to.have.lengthOf(5)
@@ -249,7 +244,7 @@ describe('Test emails', function () {
249 this.timeout(10000) 244 this.timeout(10000)
250 245
251 const reason = 'my super reason' 246 const reason = 'my super reason'
252 await addVideoToBlacklist(server.url, server.accessToken, videoUserUUID, reason) 247 await server.blacklist.add({ videoId: videoUserUUID, reason })
253 248
254 await waitJobs(server) 249 await waitJobs(server)
255 expect(emails).to.have.lengthOf(6) 250 expect(emails).to.have.lengthOf(6)
@@ -267,7 +262,7 @@ describe('Test emails', function () {
267 it('Should send the notification email', async function () { 262 it('Should send the notification email', async function () {
268 this.timeout(10000) 263 this.timeout(10000)
269 264
270 await removeVideoFromBlacklist(server.url, server.accessToken, videoUserUUID) 265 await server.blacklist.remove({ videoId: videoUserUUID })
271 266
272 await waitJobs(server) 267 await waitJobs(server)
273 expect(emails).to.have.lengthOf(7) 268 expect(emails).to.have.lengthOf(7)
@@ -292,7 +287,7 @@ describe('Test emails', function () {
292 it('Should ask to send the verification email', async function () { 287 it('Should ask to send the verification email', async function () {
293 this.timeout(10000) 288 this.timeout(10000)
294 289
295 await askSendVerifyEmail(server.url, 'user_1@example.com') 290 await server.users.askSendVerifyEmail({ email: 'user_1@example.com' })
296 291
297 await waitJobs(server) 292 await waitJobs(server)
298 expect(emails).to.have.lengthOf(8) 293 expect(emails).to.have.lengthOf(8)
@@ -318,11 +313,16 @@ describe('Test emails', function () {
318 }) 313 })
319 314
320 it('Should not verify the email with an invalid verification string', async function () { 315 it('Should not verify the email with an invalid verification string', async function () {
321 await verifyEmail(server.url, userId, verificationString + 'b', false, HttpStatusCode.FORBIDDEN_403) 316 await server.users.verifyEmail({
317 userId,
318 verificationString: verificationString + 'b',
319 isPendingEmail: false,
320 expectedStatus: HttpStatusCode.FORBIDDEN_403
321 })
322 }) 322 })
323 323
324 it('Should verify the email', async function () { 324 it('Should verify the email', async function () {
325 await verifyEmail(server.url, userId, verificationString) 325 await server.users.verifyEmail({ userId, verificationString })
326 }) 326 })
327 }) 327 })
328 328
diff --git a/server/tests/api/server/follow-constraints.ts b/server/tests/api/server/follow-constraints.ts
index 3f2f71f46..471f5d8d0 100644
--- a/server/tests/api/server/follow-constraints.ts
+++ b/server/tests/api/server/follow-constraints.ts
@@ -1,56 +1,41 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { 4import * as chai from 'chai'
6 cleanupTests, 5import { cleanupTests, createMultipleServers, doubleFollow, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
7 doubleFollow, 6import { HttpStatusCode, PeerTubeProblemDocument, ServerErrorCode } from '@shared/models'
8 flushAndRunMultipleServers,
9 getAccountVideos,
10 getVideo,
11 getVideoChannelVideos,
12 getVideoWithToken,
13 ServerInfo,
14 setAccessTokensToServers,
15 uploadVideo
16} from '../../../../shared/extra-utils'
17import { unfollow } from '../../../../shared/extra-utils/server/follows'
18import { userLogin } from '../../../../shared/extra-utils/users/login'
19import { createUser } from '../../../../shared/extra-utils/users/users'
20import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
21import { PeerTubeProblemDocument, ServerErrorCode } from '@shared/models'
22 7
23const expect = chai.expect 8const expect = chai.expect
24 9
25describe('Test follow constraints', function () { 10describe('Test follow constraints', function () {
26 let servers: ServerInfo[] = [] 11 let servers: PeerTubeServer[] = []
27 let video1UUID: string 12 let video1UUID: string
28 let video2UUID: string 13 let video2UUID: string
29 let userAccessToken: string 14 let userToken: string
30 15
31 before(async function () { 16 before(async function () {
32 this.timeout(90000) 17 this.timeout(90000)
33 18
34 servers = await flushAndRunMultipleServers(2) 19 servers = await createMultipleServers(2)
35 20
36 // Get the access tokens 21 // Get the access tokens
37 await setAccessTokensToServers(servers) 22 await setAccessTokensToServers(servers)
38 23
39 { 24 {
40 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video server 1' }) 25 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video server 1' } })
41 video1UUID = res.body.video.uuid 26 video1UUID = uuid
42 } 27 }
43 { 28 {
44 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video server 2' }) 29 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video server 2' } })
45 video2UUID = res.body.video.uuid 30 video2UUID = uuid
46 } 31 }
47 32
48 const user = { 33 const user = {
49 username: 'user1', 34 username: 'user1',
50 password: 'super_password' 35 password: 'super_password'
51 } 36 }
52 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 37 await servers[0].users.create({ username: user.username, password: user.password })
53 userAccessToken = await userLogin(servers[0], user) 38 userToken = await servers[0].login.getAccessToken(user)
54 39
55 await doubleFollow(servers[0], servers[1]) 40 await doubleFollow(servers[0], servers[1])
56 }) 41 })
@@ -60,81 +45,81 @@ describe('Test follow constraints', function () {
60 describe('With an unlogged user', function () { 45 describe('With an unlogged user', function () {
61 46
62 it('Should get the local video', async function () { 47 it('Should get the local video', async function () {
63 await getVideo(servers[0].url, video1UUID, HttpStatusCode.OK_200) 48 await servers[0].videos.get({ id: video1UUID })
64 }) 49 })
65 50
66 it('Should get the remote video', async function () { 51 it('Should get the remote video', async function () {
67 await getVideo(servers[0].url, video2UUID, HttpStatusCode.OK_200) 52 await servers[0].videos.get({ id: video2UUID })
68 }) 53 })
69 54
70 it('Should list local account videos', async function () { 55 it('Should list local account videos', async function () {
71 const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[0].port, 0, 5) 56 const { total, data } = await servers[0].videos.listByAccount({ handle: 'root@localhost:' + servers[0].port })
72 57
73 expect(res.body.total).to.equal(1) 58 expect(total).to.equal(1)
74 expect(res.body.data).to.have.lengthOf(1) 59 expect(data).to.have.lengthOf(1)
75 }) 60 })
76 61
77 it('Should list remote account videos', async function () { 62 it('Should list remote account videos', async function () {
78 const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[1].port, 0, 5) 63 const { total, data } = await servers[0].videos.listByAccount({ handle: 'root@localhost:' + servers[1].port })
79 64
80 expect(res.body.total).to.equal(1) 65 expect(total).to.equal(1)
81 expect(res.body.data).to.have.lengthOf(1) 66 expect(data).to.have.lengthOf(1)
82 }) 67 })
83 68
84 it('Should list local channel videos', async function () { 69 it('Should list local channel videos', async function () {
85 const videoChannelName = 'root_channel@localhost:' + servers[0].port 70 const handle = 'root_channel@localhost:' + servers[0].port
86 const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) 71 const { total, data } = await servers[0].videos.listByChannel({ handle })
87 72
88 expect(res.body.total).to.equal(1) 73 expect(total).to.equal(1)
89 expect(res.body.data).to.have.lengthOf(1) 74 expect(data).to.have.lengthOf(1)
90 }) 75 })
91 76
92 it('Should list remote channel videos', async function () { 77 it('Should list remote channel videos', async function () {
93 const videoChannelName = 'root_channel@localhost:' + servers[1].port 78 const handle = 'root_channel@localhost:' + servers[1].port
94 const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) 79 const { total, data } = await servers[0].videos.listByChannel({ handle })
95 80
96 expect(res.body.total).to.equal(1) 81 expect(total).to.equal(1)
97 expect(res.body.data).to.have.lengthOf(1) 82 expect(data).to.have.lengthOf(1)
98 }) 83 })
99 }) 84 })
100 85
101 describe('With a logged user', function () { 86 describe('With a logged user', function () {
102 it('Should get the local video', async function () { 87 it('Should get the local video', async function () {
103 await getVideoWithToken(servers[0].url, userAccessToken, video1UUID, HttpStatusCode.OK_200) 88 await servers[0].videos.getWithToken({ token: userToken, id: video1UUID })
104 }) 89 })
105 90
106 it('Should get the remote video', async function () { 91 it('Should get the remote video', async function () {
107 await getVideoWithToken(servers[0].url, userAccessToken, video2UUID, HttpStatusCode.OK_200) 92 await servers[0].videos.getWithToken({ token: userToken, id: video2UUID })
108 }) 93 })
109 94
110 it('Should list local account videos', async function () { 95 it('Should list local account videos', async function () {
111 const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[0].port, 0, 5) 96 const { total, data } = await servers[0].videos.listByAccount({ token: userToken, handle: 'root@localhost:' + servers[0].port })
112 97
113 expect(res.body.total).to.equal(1) 98 expect(total).to.equal(1)
114 expect(res.body.data).to.have.lengthOf(1) 99 expect(data).to.have.lengthOf(1)
115 }) 100 })
116 101
117 it('Should list remote account videos', async function () { 102 it('Should list remote account videos', async function () {
118 const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[1].port, 0, 5) 103 const { total, data } = await servers[0].videos.listByAccount({ token: userToken, handle: 'root@localhost:' + servers[1].port })
119 104
120 expect(res.body.total).to.equal(1) 105 expect(total).to.equal(1)
121 expect(res.body.data).to.have.lengthOf(1) 106 expect(data).to.have.lengthOf(1)
122 }) 107 })
123 108
124 it('Should list local channel videos', async function () { 109 it('Should list local channel videos', async function () {
125 const videoChannelName = 'root_channel@localhost:' + servers[0].port 110 const handle = 'root_channel@localhost:' + servers[0].port
126 const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) 111 const { total, data } = await servers[0].videos.listByChannel({ token: userToken, handle })
127 112
128 expect(res.body.total).to.equal(1) 113 expect(total).to.equal(1)
129 expect(res.body.data).to.have.lengthOf(1) 114 expect(data).to.have.lengthOf(1)
130 }) 115 })
131 116
132 it('Should list remote channel videos', async function () { 117 it('Should list remote channel videos', async function () {
133 const videoChannelName = 'root_channel@localhost:' + servers[1].port 118 const handle = 'root_channel@localhost:' + servers[1].port
134 const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) 119 const { total, data } = await servers[0].videos.listByChannel({ token: userToken, handle })
135 120
136 expect(res.body.total).to.equal(1) 121 expect(total).to.equal(1)
137 expect(res.body.data).to.have.lengthOf(1) 122 expect(data).to.have.lengthOf(1)
138 }) 123 })
139 }) 124 })
140 }) 125 })
@@ -144,19 +129,18 @@ describe('Test follow constraints', function () {
144 before(async function () { 129 before(async function () {
145 this.timeout(30000) 130 this.timeout(30000)
146 131
147 await unfollow(servers[0].url, servers[0].accessToken, servers[1]) 132 await servers[0].follows.unfollow({ target: servers[1] })
148 }) 133 })
149 134
150 describe('With an unlogged user', function () { 135 describe('With an unlogged user', function () {
151 136
152 it('Should get the local video', async function () { 137 it('Should get the local video', async function () {
153 await getVideo(servers[0].url, video1UUID, HttpStatusCode.OK_200) 138 await servers[0].videos.get({ id: video1UUID })
154 }) 139 })
155 140
156 it('Should not get the remote video', async function () { 141 it('Should not get the remote video', async function () {
157 const res = await getVideo(servers[0].url, video2UUID, HttpStatusCode.FORBIDDEN_403) 142 const body = await servers[0].videos.get({ id: video2UUID, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
158 143 const error = body as unknown as PeerTubeProblemDocument
159 const error = res.body as PeerTubeProblemDocument
160 144
161 const doc = 'https://docs.joinpeertube.org/api-rest-reference.html#section/Errors/does_not_respect_follow_constraints' 145 const doc = 'https://docs.joinpeertube.org/api-rest-reference.html#section/Errors/does_not_respect_follow_constraints'
162 expect(error.type).to.equal(doc) 146 expect(error.type).to.equal(doc)
@@ -171,73 +155,79 @@ describe('Test follow constraints', function () {
171 }) 155 })
172 156
173 it('Should list local account videos', async function () { 157 it('Should list local account videos', async function () {
174 const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[0].port, 0, 5) 158 const { total, data } = await servers[0].videos.listByAccount({
159 token: null,
160 handle: 'root@localhost:' + servers[0].port
161 })
175 162
176 expect(res.body.total).to.equal(1) 163 expect(total).to.equal(1)
177 expect(res.body.data).to.have.lengthOf(1) 164 expect(data).to.have.lengthOf(1)
178 }) 165 })
179 166
180 it('Should not list remote account videos', async function () { 167 it('Should not list remote account videos', async function () {
181 const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[1].port, 0, 5) 168 const { total, data } = await servers[0].videos.listByAccount({
169 token: null,
170 handle: 'root@localhost:' + servers[1].port
171 })
182 172
183 expect(res.body.total).to.equal(0) 173 expect(total).to.equal(0)
184 expect(res.body.data).to.have.lengthOf(0) 174 expect(data).to.have.lengthOf(0)
185 }) 175 })
186 176
187 it('Should list local channel videos', async function () { 177 it('Should list local channel videos', async function () {
188 const videoChannelName = 'root_channel@localhost:' + servers[0].port 178 const handle = 'root_channel@localhost:' + servers[0].port
189 const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) 179 const { total, data } = await servers[0].videos.listByChannel({ token: null, handle })
190 180
191 expect(res.body.total).to.equal(1) 181 expect(total).to.equal(1)
192 expect(res.body.data).to.have.lengthOf(1) 182 expect(data).to.have.lengthOf(1)
193 }) 183 })
194 184
195 it('Should not list remote channel videos', async function () { 185 it('Should not list remote channel videos', async function () {
196 const videoChannelName = 'root_channel@localhost:' + servers[1].port 186 const handle = 'root_channel@localhost:' + servers[1].port
197 const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) 187 const { total, data } = await servers[0].videos.listByChannel({ token: null, handle })
198 188
199 expect(res.body.total).to.equal(0) 189 expect(total).to.equal(0)
200 expect(res.body.data).to.have.lengthOf(0) 190 expect(data).to.have.lengthOf(0)
201 }) 191 })
202 }) 192 })
203 193
204 describe('With a logged user', function () { 194 describe('With a logged user', function () {
205 it('Should get the local video', async function () { 195 it('Should get the local video', async function () {
206 await getVideoWithToken(servers[0].url, userAccessToken, video1UUID, HttpStatusCode.OK_200) 196 await servers[0].videos.getWithToken({ token: userToken, id: video1UUID })
207 }) 197 })
208 198
209 it('Should get the remote video', async function () { 199 it('Should get the remote video', async function () {
210 await getVideoWithToken(servers[0].url, userAccessToken, video2UUID, HttpStatusCode.OK_200) 200 await servers[0].videos.getWithToken({ token: userToken, id: video2UUID })
211 }) 201 })
212 202
213 it('Should list local account videos', async function () { 203 it('Should list local account videos', async function () {
214 const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[0].port, 0, 5) 204 const { total, data } = await servers[0].videos.listByAccount({ token: userToken, handle: 'root@localhost:' + servers[0].port })
215 205
216 expect(res.body.total).to.equal(1) 206 expect(total).to.equal(1)
217 expect(res.body.data).to.have.lengthOf(1) 207 expect(data).to.have.lengthOf(1)
218 }) 208 })
219 209
220 it('Should list remote account videos', async function () { 210 it('Should list remote account videos', async function () {
221 const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[1].port, 0, 5) 211 const { total, data } = await servers[0].videos.listByAccount({ token: userToken, handle: 'root@localhost:' + servers[1].port })
222 212
223 expect(res.body.total).to.equal(1) 213 expect(total).to.equal(1)
224 expect(res.body.data).to.have.lengthOf(1) 214 expect(data).to.have.lengthOf(1)
225 }) 215 })
226 216
227 it('Should list local channel videos', async function () { 217 it('Should list local channel videos', async function () {
228 const videoChannelName = 'root_channel@localhost:' + servers[0].port 218 const handle = 'root_channel@localhost:' + servers[0].port
229 const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) 219 const { total, data } = await servers[0].videos.listByChannel({ token: userToken, handle })
230 220
231 expect(res.body.total).to.equal(1) 221 expect(total).to.equal(1)
232 expect(res.body.data).to.have.lengthOf(1) 222 expect(data).to.have.lengthOf(1)
233 }) 223 })
234 224
235 it('Should list remote channel videos', async function () { 225 it('Should list remote channel videos', async function () {
236 const videoChannelName = 'root_channel@localhost:' + servers[1].port 226 const handle = 'root_channel@localhost:' + servers[1].port
237 const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) 227 const { total, data } = await servers[0].videos.listByChannel({ token: userToken, handle })
238 228
239 expect(res.body.total).to.equal(1) 229 expect(total).to.equal(1)
240 expect(res.body.data).to.have.lengthOf(1) 230 expect(data).to.have.lengthOf(1)
241 }) 231 })
242 }) 232 })
243 }) 233 })
diff --git a/server/tests/api/server/follows-moderation.ts b/server/tests/api/server/follows-moderation.ts
index 73c212a32..921f51043 100644
--- a/server/tests/api/server/follows-moderation.ts
+++ b/server/tests/api/server/follows-moderation.ts
@@ -1,77 +1,66 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 acceptFollower,
7 cleanupTests, 6 cleanupTests,
8 flushAndRunMultipleServers, 7 createMultipleServers,
9 ServerInfo, 8 FollowsCommand,
9 PeerTubeServer,
10 setAccessTokensToServers, 10 setAccessTokensToServers,
11 updateCustomSubConfig 11 waitJobs
12} from '../../../../shared/extra-utils/index' 12} from '@shared/extra-utils'
13import {
14 follow,
15 getFollowersListPaginationAndSort,
16 getFollowingListPaginationAndSort,
17 rejectFollower,
18 removeFollower
19} from '../../../../shared/extra-utils/server/follows'
20import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
21import { ActorFollow } from '../../../../shared/models/actors'
22 13
23const expect = chai.expect 14const expect = chai.expect
24 15
25async function checkServer1And2HasFollowers (servers: ServerInfo[], state = 'accepted') { 16async function checkServer1And2HasFollowers (servers: PeerTubeServer[], state = 'accepted') {
26 { 17 const fns = [
27 const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: 'createdAt' }) 18 servers[0].follows.getFollowings.bind(servers[0].follows),
28 expect(res.body.total).to.equal(1) 19 servers[1].follows.getFollowers.bind(servers[1].follows)
29 20 ]
30 const follow = res.body.data[0] as ActorFollow
31 expect(follow.state).to.equal(state)
32 expect(follow.follower.url).to.equal('http://localhost:' + servers[0].port + '/accounts/peertube')
33 expect(follow.following.url).to.equal('http://localhost:' + servers[1].port + '/accounts/peertube')
34 }
35 21
36 { 22 for (const fn of fns) {
37 const res = await getFollowersListPaginationAndSort({ url: servers[1].url, start: 0, count: 5, sort: 'createdAt' }) 23 const body = await fn({ start: 0, count: 5, sort: 'createdAt' })
38 expect(res.body.total).to.equal(1) 24 expect(body.total).to.equal(1)
39 25
40 const follow = res.body.data[0] as ActorFollow 26 const follow = body.data[0]
41 expect(follow.state).to.equal(state) 27 expect(follow.state).to.equal(state)
42 expect(follow.follower.url).to.equal('http://localhost:' + servers[0].port + '/accounts/peertube') 28 expect(follow.follower.url).to.equal('http://localhost:' + servers[0].port + '/accounts/peertube')
43 expect(follow.following.url).to.equal('http://localhost:' + servers[1].port + '/accounts/peertube') 29 expect(follow.following.url).to.equal('http://localhost:' + servers[1].port + '/accounts/peertube')
44 } 30 }
45} 31}
46 32
47async function checkNoFollowers (servers: ServerInfo[]) { 33async function checkNoFollowers (servers: PeerTubeServer[]) {
48 { 34 const fns = [
49 const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: 'createdAt' }) 35 servers[0].follows.getFollowings.bind(servers[0].follows),
50 expect(res.body.total).to.equal(0) 36 servers[1].follows.getFollowers.bind(servers[1].follows)
51 } 37 ]
52 38
53 { 39 for (const fn of fns) {
54 const res = await getFollowersListPaginationAndSort({ url: servers[1].url, start: 0, count: 5, sort: 'createdAt' }) 40 const body = await fn({ start: 0, count: 5, sort: 'createdAt' })
55 expect(res.body.total).to.equal(0) 41 expect(body.total).to.equal(0)
56 } 42 }
57} 43}
58 44
59describe('Test follows moderation', function () { 45describe('Test follows moderation', function () {
60 let servers: ServerInfo[] = [] 46 let servers: PeerTubeServer[] = []
47 let commands: FollowsCommand[]
61 48
62 before(async function () { 49 before(async function () {
63 this.timeout(30000) 50 this.timeout(30000)
64 51
65 servers = await flushAndRunMultipleServers(3) 52 servers = await createMultipleServers(3)
66 53
67 // Get the access tokens 54 // Get the access tokens
68 await setAccessTokensToServers(servers) 55 await setAccessTokensToServers(servers)
56
57 commands = servers.map(s => s.follows)
69 }) 58 })
70 59
71 it('Should have server 1 following server 2', async function () { 60 it('Should have server 1 following server 2', async function () {
72 this.timeout(30000) 61 this.timeout(30000)
73 62
74 await follow(servers[0].url, [ servers[1].url ], servers[0].accessToken) 63 await commands[0].follow({ hosts: [ servers[1].url ] })
75 64
76 await waitJobs(servers) 65 await waitJobs(servers)
77 }) 66 })
@@ -83,7 +72,7 @@ describe('Test follows moderation', function () {
83 it('Should remove follower on server 2', async function () { 72 it('Should remove follower on server 2', async function () {
84 this.timeout(10000) 73 this.timeout(10000)
85 74
86 await removeFollower(servers[1].url, servers[1].accessToken, servers[0]) 75 await commands[1].removeFollower({ follower: servers[0] })
87 76
88 await waitJobs(servers) 77 await waitJobs(servers)
89 }) 78 })
@@ -104,9 +93,9 @@ describe('Test follows moderation', function () {
104 } 93 }
105 } 94 }
106 95
107 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, subConfig) 96 await servers[1].config.updateCustomSubConfig({ newConfig: subConfig })
108 97
109 await follow(servers[0].url, [ servers[1].url ], servers[0].accessToken) 98 await commands[0].follow({ hosts: [ servers[1].url ] })
110 await waitJobs(servers) 99 await waitJobs(servers)
111 100
112 await checkNoFollowers(servers) 101 await checkNoFollowers(servers)
@@ -124,9 +113,9 @@ describe('Test follows moderation', function () {
124 } 113 }
125 } 114 }
126 115
127 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, subConfig) 116 await servers[1].config.updateCustomSubConfig({ newConfig: subConfig })
128 117
129 await follow(servers[0].url, [ servers[1].url ], servers[0].accessToken) 118 await commands[0].follow({ hosts: [ servers[1].url ] })
130 await waitJobs(servers) 119 await waitJobs(servers)
131 120
132 await checkServer1And2HasFollowers(servers) 121 await checkServer1And2HasFollowers(servers)
@@ -135,7 +124,7 @@ describe('Test follows moderation', function () {
135 it('Should manually approve followers', async function () { 124 it('Should manually approve followers', async function () {
136 this.timeout(20000) 125 this.timeout(20000)
137 126
138 await removeFollower(servers[1].url, servers[1].accessToken, servers[0]) 127 await commands[1].removeFollower({ follower: servers[0] })
139 await waitJobs(servers) 128 await waitJobs(servers)
140 129
141 const subConfig = { 130 const subConfig = {
@@ -147,10 +136,10 @@ describe('Test follows moderation', function () {
147 } 136 }
148 } 137 }
149 138
150 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, subConfig) 139 await servers[1].config.updateCustomSubConfig({ newConfig: subConfig })
151 await updateCustomSubConfig(servers[2].url, servers[2].accessToken, subConfig) 140 await servers[2].config.updateCustomSubConfig({ newConfig: subConfig })
152 141
153 await follow(servers[0].url, [ servers[1].url ], servers[0].accessToken) 142 await commands[0].follow({ hosts: [ servers[1].url ] })
154 await waitJobs(servers) 143 await waitJobs(servers)
155 144
156 await checkServer1And2HasFollowers(servers, 'pending') 145 await checkServer1And2HasFollowers(servers, 'pending')
@@ -159,7 +148,7 @@ describe('Test follows moderation', function () {
159 it('Should accept a follower', async function () { 148 it('Should accept a follower', async function () {
160 this.timeout(10000) 149 this.timeout(10000)
161 150
162 await acceptFollower(servers[1].url, servers[1].accessToken, 'peertube@localhost:' + servers[0].port) 151 await commands[1].acceptFollower({ follower: 'peertube@localhost:' + servers[0].port })
163 await waitJobs(servers) 152 await waitJobs(servers)
164 153
165 await checkServer1And2HasFollowers(servers) 154 await checkServer1And2HasFollowers(servers)
@@ -168,32 +157,32 @@ describe('Test follows moderation', function () {
168 it('Should reject another follower', async function () { 157 it('Should reject another follower', async function () {
169 this.timeout(20000) 158 this.timeout(20000)
170 159
171 await follow(servers[0].url, [ servers[2].url ], servers[0].accessToken) 160 await commands[0].follow({ hosts: [ servers[2].url ] })
172 await waitJobs(servers) 161 await waitJobs(servers)
173 162
174 { 163 {
175 const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: 'createdAt' }) 164 const body = await commands[0].getFollowings({ start: 0, count: 5, sort: 'createdAt' })
176 expect(res.body.total).to.equal(2) 165 expect(body.total).to.equal(2)
177 } 166 }
178 167
179 { 168 {
180 const res = await getFollowersListPaginationAndSort({ url: servers[1].url, start: 0, count: 5, sort: 'createdAt' }) 169 const body = await commands[1].getFollowers({ start: 0, count: 5, sort: 'createdAt' })
181 expect(res.body.total).to.equal(1) 170 expect(body.total).to.equal(1)
182 } 171 }
183 172
184 { 173 {
185 const res = await getFollowersListPaginationAndSort({ url: servers[2].url, start: 0, count: 5, sort: 'createdAt' }) 174 const body = await commands[2].getFollowers({ start: 0, count: 5, sort: 'createdAt' })
186 expect(res.body.total).to.equal(1) 175 expect(body.total).to.equal(1)
187 } 176 }
188 177
189 await rejectFollower(servers[2].url, servers[2].accessToken, 'peertube@localhost:' + servers[0].port) 178 await commands[2].rejectFollower({ follower: 'peertube@localhost:' + servers[0].port })
190 await waitJobs(servers) 179 await waitJobs(servers)
191 180
192 await checkServer1And2HasFollowers(servers) 181 await checkServer1And2HasFollowers(servers)
193 182
194 { 183 {
195 const res = await getFollowersListPaginationAndSort({ url: servers[2].url, start: 0, count: 5, sort: 'createdAt' }) 184 const body = await commands[2].getFollowers({ start: 0, count: 5, sort: 'createdAt' })
196 expect(res.body.total).to.equal(0) 185 expect(body.total).to.equal(0)
197 } 186 }
198 }) 187 })
199 188
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts
index 9e5aa00c7..832ba561a 100644
--- a/server/tests/api/server/follows.ts
+++ b/server/tests/api/server/follows.ts
@@ -3,325 +3,374 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 addVideoCommentReply,
7 addVideoCommentThread,
8 cleanupTests, 6 cleanupTests,
9 completeVideoCheck, 7 completeVideoCheck,
10 createUser, 8 createMultipleServers,
11 createVideoCaption,
12 dateIsValid, 9 dateIsValid,
13 deleteVideoComment,
14 expectAccountFollows, 10 expectAccountFollows,
15 flushAndRunMultipleServers, 11 expectChannelsFollows,
16 follow, 12 PeerTubeServer,
17 getFollowersListPaginationAndSort,
18 getFollowingListPaginationAndSort,
19 getVideoCommentThreads,
20 getVideosList,
21 getVideoThreadComments,
22 listVideoCaptions,
23 rateVideo,
24 ServerInfo,
25 setAccessTokensToServers, 13 setAccessTokensToServers,
26 testCaptionFile, 14 testCaptionFile,
27 unfollow,
28 uploadVideo,
29 userLogin,
30 waitJobs 15 waitJobs
31} from '@shared/extra-utils' 16} from '@shared/extra-utils'
32import { Video, VideoCaption, VideoComment, VideoCommentThreadTree, VideoPrivacy } from '@shared/models' 17import { VideoCreateResult, VideoPrivacy } from '@shared/models'
33 18
34const expect = chai.expect 19const expect = chai.expect
35 20
36describe('Test follows', function () { 21describe('Test follows', function () {
37 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
38 23
39 before(async function () { 24 before(async function () {
40 this.timeout(30000) 25 this.timeout(30000)
41 26
42 servers = await flushAndRunMultipleServers(3) 27 servers = await createMultipleServers(3)
43 28
44 // Get the access tokens 29 // Get the access tokens
45 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
46 }) 31 })
47 32
48 it('Should not have followers', async function () { 33 describe('Data propagation after follow', function () {
49 for (const server of servers) {
50 const res = await getFollowersListPaginationAndSort({ url: server.url, start: 0, count: 5, sort: 'createdAt' })
51 const follows = res.body.data
52 34
53 expect(res.body.total).to.equal(0) 35 it('Should not have followers/followings', async function () {
54 expect(follows).to.be.an('array') 36 for (const server of servers) {
55 expect(follows.length).to.equal(0) 37 const bodies = await Promise.all([
56 } 38 server.follows.getFollowers({ start: 0, count: 5, sort: 'createdAt' }),
57 }) 39 server.follows.getFollowings({ start: 0, count: 5, sort: 'createdAt' })
58 40 ])
59 it('Should not have following', async function () {
60 for (const server of servers) {
61 const res = await getFollowingListPaginationAndSort({ url: server.url, start: 0, count: 5, sort: 'createdAt' })
62 const follows = res.body.data
63 41
64 expect(res.body.total).to.equal(0) 42 for (const body of bodies) {
65 expect(follows).to.be.an('array') 43 expect(body.total).to.equal(0)
66 expect(follows.length).to.equal(0)
67 }
68 })
69 44
70 it('Should have server 1 following server 2 and 3', async function () { 45 const follows = body.data
71 this.timeout(30000) 46 expect(follows).to.be.an('array')
47 expect(follows).to.have.lengthOf(0)
48 }
49 }
50 })
72 51
73 await follow(servers[0].url, [ servers[1].url, servers[2].url ], servers[0].accessToken) 52 it('Should have server 1 following root account of server 2 and server 3', async function () {
53 this.timeout(30000)
74 54
75 await waitJobs(servers) 55 await servers[0].follows.follow({
76 }) 56 hosts: [ servers[2].url ],
57 handles: [ 'root@' + servers[1].host ]
58 })
77 59
78 it('Should have 2 followings on server 1', async function () { 60 await waitJobs(servers)
79 let res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 1, sort: 'createdAt' }) 61 })
80 let follows = res.body.data
81 62
82 expect(res.body.total).to.equal(2) 63 it('Should have 2 followings on server 1', async function () {
83 expect(follows).to.be.an('array') 64 const body = await servers[0].follows.getFollowings({ start: 0, count: 1, sort: 'createdAt' })
84 expect(follows.length).to.equal(1) 65 expect(body.total).to.equal(2)
85 66
86 res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 1, count: 1, sort: 'createdAt' }) 67 let follows = body.data
87 follows = follows.concat(res.body.data) 68 expect(follows).to.be.an('array')
69 expect(follows).to.have.lengthOf(1)
88 70
89 const server2Follow = follows.find(f => f.following.host === 'localhost:' + servers[1].port) 71 const body2 = await servers[0].follows.getFollowings({ start: 1, count: 1, sort: 'createdAt' })
90 const server3Follow = follows.find(f => f.following.host === 'localhost:' + servers[2].port) 72 follows = follows.concat(body2.data)
91 73
92 expect(server2Follow).to.not.be.undefined 74 const server2Follow = follows.find(f => f.following.host === servers[1].host)
93 expect(server3Follow).to.not.be.undefined 75 const server3Follow = follows.find(f => f.following.host === servers[2].host)
94 expect(server2Follow.state).to.equal('accepted')
95 expect(server3Follow.state).to.equal('accepted')
96 })
97 76
98 it('Should search/filter followings on server 1', async function () { 77 expect(server2Follow).to.not.be.undefined
99 const sort = 'createdAt' 78 expect(server2Follow.following.name).to.equal('root')
100 const start = 0 79 expect(server2Follow.state).to.equal('accepted')
101 const count = 1
102 const url = servers[0].url
103 80
104 { 81 expect(server3Follow).to.not.be.undefined
105 const search = ':' + servers[1].port 82 expect(server3Follow.following.name).to.equal('peertube')
83 expect(server3Follow.state).to.equal('accepted')
84 })
106 85
107 { 86 it('Should have 0 followings on server 2 and 3', async function () {
108 const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search }) 87 for (const server of [ servers[1], servers[2] ]) {
109 const follows = res.body.data 88 const body = await server.follows.getFollowings({ start: 0, count: 5, sort: 'createdAt' })
89 expect(body.total).to.equal(0)
110 90
111 expect(res.body.total).to.equal(1) 91 const follows = body.data
112 expect(follows.length).to.equal(1) 92 expect(follows).to.be.an('array')
113 expect(follows[0].following.host).to.equal('localhost:' + servers[1].port) 93 expect(follows).to.have.lengthOf(0)
114 } 94 }
95 })
115 96
116 { 97 it('Should have 1 followers on server 3', async function () {
117 const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search, state: 'accepted' }) 98 const body = await servers[2].follows.getFollowers({ start: 0, count: 1, sort: 'createdAt' })
118 expect(res.body.total).to.equal(1) 99 expect(body.total).to.equal(1)
119 expect(res.body.data).to.have.lengthOf(1) 100
101 const follows = body.data
102 expect(follows).to.be.an('array')
103 expect(follows).to.have.lengthOf(1)
104 expect(follows[0].follower.host).to.equal('localhost:' + servers[0].port)
105 })
106
107 it('Should have 0 followers on server 1 and 2', async function () {
108 for (const server of [ servers[0], servers[1] ]) {
109 const body = await server.follows.getFollowers({ start: 0, count: 5, sort: 'createdAt' })
110 expect(body.total).to.equal(0)
111
112 const follows = body.data
113 expect(follows).to.be.an('array')
114 expect(follows).to.have.lengthOf(0)
120 } 115 }
116 })
117
118 it('Should search/filter followings on server 1', async function () {
119 const sort = 'createdAt'
120 const start = 0
121 const count = 1
121 122
122 { 123 {
123 const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search, state: 'accepted', actorType: 'Person' }) 124 const search = ':' + servers[1].port
124 expect(res.body.total).to.equal(0) 125
125 expect(res.body.data).to.have.lengthOf(0) 126 {
127 const body = await servers[0].follows.getFollowings({ start, count, sort, search })
128 expect(body.total).to.equal(1)
129
130 const follows = body.data
131 expect(follows).to.have.lengthOf(1)
132 expect(follows[0].following.host).to.equal(servers[1].host)
133 }
134
135 {
136 const body = await servers[0].follows.getFollowings({ start, count, sort, search, state: 'accepted' })
137 expect(body.total).to.equal(1)
138 expect(body.data).to.have.lengthOf(1)
139 }
140
141 {
142 const body = await servers[0].follows.getFollowings({ start, count, sort, search, state: 'accepted', actorType: 'Person' })
143 expect(body.total).to.equal(1)
144 expect(body.data).to.have.lengthOf(1)
145 }
146
147 {
148 const body = await servers[0].follows.getFollowings({
149 start,
150 count,
151 sort,
152 search,
153 state: 'accepted',
154 actorType: 'Application'
155 })
156 expect(body.total).to.equal(0)
157 expect(body.data).to.have.lengthOf(0)
158 }
159
160 {
161 const body = await servers[0].follows.getFollowings({ start, count, sort, search, state: 'pending' })
162 expect(body.total).to.equal(0)
163 expect(body.data).to.have.lengthOf(0)
164 }
126 } 165 }
127 166
128 { 167 {
129 const res = await getFollowingListPaginationAndSort({ 168 const body = await servers[0].follows.getFollowings({ start, count, sort, search: 'root' })
130 url, 169 expect(body.total).to.equal(1)
131 start, 170 expect(body.data).to.have.lengthOf(1)
132 count,
133 sort,
134 search,
135 state: 'accepted',
136 actorType: 'Application'
137 })
138 expect(res.body.total).to.equal(1)
139 expect(res.body.data).to.have.lengthOf(1)
140 } 171 }
141 172
142 { 173 {
143 const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search, state: 'pending' }) 174 const body = await servers[0].follows.getFollowings({ start, count, sort, search: 'bla' })
144 expect(res.body.total).to.equal(0) 175 expect(body.total).to.equal(0)
145 expect(res.body.data).to.have.lengthOf(0) 176
177 expect(body.data).to.have.lengthOf(0)
146 } 178 }
147 } 179 })
148 180
149 { 181 it('Should search/filter followers on server 2', async function () {
150 const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search: 'bla' }) 182 const start = 0
151 const follows = res.body.data 183 const count = 5
184 const sort = 'createdAt'
152 185
153 expect(res.body.total).to.equal(0) 186 {
154 expect(follows.length).to.equal(0) 187 const search = servers[0].port + ''
155 }
156 })
157 188
158 it('Should have 0 followings on server 2 and 3', async function () { 189 {
159 for (const server of [ servers[1], servers[2] ]) { 190 const body = await servers[2].follows.getFollowers({ start, count, sort, search })
160 const res = await getFollowingListPaginationAndSort({ url: server.url, start: 0, count: 5, sort: 'createdAt' }) 191 expect(body.total).to.equal(1)
161 const follows = res.body.data
162 192
163 expect(res.body.total).to.equal(0) 193 const follows = body.data
164 expect(follows).to.be.an('array') 194 expect(follows).to.have.lengthOf(1)
165 expect(follows.length).to.equal(0) 195 expect(follows[0].following.host).to.equal(servers[2].host)
166 } 196 }
167 })
168 197
169 it('Should have 1 followers on server 2 and 3', async function () { 198 {
170 for (const server of [ servers[1], servers[2] ]) { 199 const body = await servers[2].follows.getFollowers({ start, count, sort, search, state: 'accepted' })
171 const res = await getFollowersListPaginationAndSort({ url: server.url, start: 0, count: 1, sort: 'createdAt' }) 200 expect(body.total).to.equal(1)
201 expect(body.data).to.have.lengthOf(1)
202 }
172 203
173 const follows = res.body.data 204 {
174 expect(res.body.total).to.equal(1) 205 const body = await servers[2].follows.getFollowers({ start, count, sort, search, state: 'accepted', actorType: 'Person' })
175 expect(follows).to.be.an('array') 206 expect(body.total).to.equal(0)
176 expect(follows.length).to.equal(1) 207 expect(body.data).to.have.lengthOf(0)
177 expect(follows[0].follower.host).to.equal('localhost:' + servers[0].port) 208 }
178 }
179 })
180 209
181 it('Should search/filter followers on server 2', async function () { 210 {
182 const url = servers[2].url 211 const body = await servers[2].follows.getFollowers({
183 const start = 0 212 start,
184 const count = 5 213 count,
185 const sort = 'createdAt' 214 sort,
215 search,
216 state: 'accepted',
217 actorType: 'Application'
218 })
219 expect(body.total).to.equal(1)
220 expect(body.data).to.have.lengthOf(1)
221 }
186 222
187 { 223 {
188 const search = servers[0].port + '' 224 const body = await servers[2].follows.getFollowers({ start, count, sort, search, state: 'pending' })
225 expect(body.total).to.equal(0)
226 expect(body.data).to.have.lengthOf(0)
227 }
228 }
189 229
190 { 230 {
191 const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search }) 231 const body = await servers[2].follows.getFollowers({ start, count, sort, search: 'bla' })
192 const follows = res.body.data 232 expect(body.total).to.equal(0)
193 233
194 expect(res.body.total).to.equal(1) 234 const follows = body.data
195 expect(follows.length).to.equal(1) 235 expect(follows).to.have.lengthOf(0)
196 expect(follows[0].following.host).to.equal('localhost:' + servers[2].port)
197 } 236 }
237 })
198 238
199 { 239 it('Should have the correct follows counts', async function () {
200 const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search, state: 'accepted' }) 240 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[0].host, followers: 0, following: 2 })
201 expect(res.body.total).to.equal(1) 241 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 1, following: 0 })
202 expect(res.body.data).to.have.lengthOf(1) 242 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 })
203 }
204 243
205 { 244 // Server 2 and 3 does not know server 1 follow another server (there was not a refresh)
206 const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search, state: 'accepted', actorType: 'Person' }) 245 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
207 expect(res.body.total).to.equal(0) 246 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 1, following: 0 })
208 expect(res.body.data).to.have.lengthOf(0) 247 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[1].host, followers: 0, following: 0 })
209 }
210 248
211 { 249 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
212 const res = await getFollowersListPaginationAndSort({ 250 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 })
213 url, 251 })
214 start,
215 count,
216 sort,
217 search,
218 state: 'accepted',
219 actorType: 'Application'
220 })
221 expect(res.body.total).to.equal(1)
222 expect(res.body.data).to.have.lengthOf(1)
223 }
224 252
225 { 253 it('Should unfollow server 3 on server 1', async function () {
226 const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search, state: 'pending' }) 254 this.timeout(15000)
227 expect(res.body.total).to.equal(0)
228 expect(res.body.data).to.have.lengthOf(0)
229 }
230 }
231 255
232 { 256 await servers[0].follows.unfollow({ target: servers[2] })
233 const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search: 'bla' })
234 const follows = res.body.data
235 257
236 expect(res.body.total).to.equal(0) 258 await waitJobs(servers)
237 expect(follows.length).to.equal(0) 259 })
238 }
239 })
240 260
241 it('Should have 0 followers on server 1', async function () { 261 it('Should not follow server 3 on server 1 anymore', async function () {
242 const res = await getFollowersListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: 'createdAt' }) 262 const body = await servers[0].follows.getFollowings({ start: 0, count: 2, sort: 'createdAt' })
243 const follows = res.body.data 263 expect(body.total).to.equal(1)
244 264
245 expect(res.body.total).to.equal(0) 265 const follows = body.data
246 expect(follows).to.be.an('array') 266 expect(follows).to.be.an('array')
247 expect(follows.length).to.equal(0) 267 expect(follows).to.have.lengthOf(1)
248 })
249 268
250 it('Should have the correct follows counts', async function () { 269 expect(follows[0].following.host).to.equal(servers[1].host)
251 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 2) 270 })
252 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0)
253 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[2].port, 1, 0)
254 271
255 // Server 2 and 3 does not know server 1 follow another server (there was not a refresh) 272 it('Should not have server 1 as follower on server 3 anymore', async function () {
256 await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1) 273 const body = await servers[2].follows.getFollowers({ start: 0, count: 1, sort: 'createdAt' })
257 await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0) 274 expect(body.total).to.equal(0)
258 275
259 await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 1) 276 const follows = body.data
260 await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 1, 0) 277 expect(follows).to.be.an('array')
261 }) 278 expect(follows).to.have.lengthOf(0)
279 })
262 280
263 it('Should unfollow server 3 on server 1', async function () { 281 it('Should have the correct follows counts after the unfollow', async function () {
264 this.timeout(5000) 282 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
283 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 1, following: 0 })
284 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[2].host, followers: 0, following: 0 })
265 285
266 await unfollow(servers[0].url, servers[0].accessToken, servers[2]) 286 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
287 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 1, following: 0 })
288 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[1].host, followers: 0, following: 0 })
267 289
268 await waitJobs(servers) 290 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[0].host, followers: 0, following: 0 })
269 }) 291 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[2].host, followers: 0, following: 0 })
292 })
270 293
271 it('Should not follow server 3 on server 1 anymore', async function () { 294 it('Should upload a video on server 2 and 3 and propagate only the video of server 2', async function () {
272 const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 2, sort: 'createdAt' }) 295 this.timeout(60000)
273 const follows = res.body.data
274 296
275 expect(res.body.total).to.equal(1) 297 await servers[1].videos.upload({ attributes: { name: 'server2' } })
276 expect(follows).to.be.an('array') 298 await servers[2].videos.upload({ attributes: { name: 'server3' } })
277 expect(follows.length).to.equal(1)
278 299
279 expect(follows[0].following.host).to.equal('localhost:' + servers[1].port) 300 await waitJobs(servers)
280 })
281 301
282 it('Should not have server 1 as follower on server 3 anymore', async function () { 302 {
283 const res = await getFollowersListPaginationAndSort({ url: servers[2].url, start: 0, count: 1, sort: 'createdAt' }) 303 const { total, data } = await servers[0].videos.list()
304 expect(total).to.equal(1)
305 expect(data[0].name).to.equal('server2')
306 }
284 307
285 const follows = res.body.data 308 {
286 expect(res.body.total).to.equal(0) 309 const { total, data } = await servers[1].videos.list()
287 expect(follows).to.be.an('array') 310 expect(total).to.equal(1)
288 expect(follows.length).to.equal(0) 311 expect(data[0].name).to.equal('server2')
289 }) 312 }
313
314 {
315 const { total, data } = await servers[2].videos.list()
316 expect(total).to.equal(1)
317 expect(data[0].name).to.equal('server3')
318 }
319 })
290 320
291 it('Should have the correct follows counts 2', async function () { 321 it('Should remove account follow', async function () {
292 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 1) 322 this.timeout(15000)
293 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0)
294 323
295 await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1) 324 await servers[0].follows.unfollow({ target: 'root@' + servers[1].host })
296 await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0)
297 325
298 await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 0) 326 await waitJobs(servers)
299 await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 0, 0) 327 })
300 })
301 328
302 it('Should upload a video on server 2 and 3 and propagate only the video of server 2', async function () { 329 it('Should have removed the account follow', async function () {
303 this.timeout(60000) 330 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
331 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
304 332
305 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'server2' }) 333 {
306 await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3' }) 334 const { total, data } = await servers[0].follows.getFollowings()
335 expect(total).to.equal(0)
336 expect(data).to.have.lengthOf(0)
337 }
307 338
308 await waitJobs(servers) 339 {
340 const { total, data } = await servers[0].videos.list()
341 expect(total).to.equal(0)
342 expect(data).to.have.lengthOf(0)
343 }
344 })
309 345
310 let res = await getVideosList(servers[0].url) 346 it('Should follow a channel', async function () {
311 expect(res.body.total).to.equal(1) 347 this.timeout(15000)
312 expect(res.body.data[0].name).to.equal('server2')
313 348
314 res = await getVideosList(servers[1].url) 349 await servers[0].follows.follow({
315 expect(res.body.total).to.equal(1) 350 handles: [ 'root_channel@' + servers[1].host ]
316 expect(res.body.data[0].name).to.equal('server2') 351 })
317 352
318 res = await getVideosList(servers[2].url) 353 await waitJobs(servers)
319 expect(res.body.total).to.equal(1) 354
320 expect(res.body.data[0].name).to.equal('server3') 355 await expectChannelsFollows({ server: servers[0], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 })
356 await expectChannelsFollows({ server: servers[1], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 })
357
358 {
359 const { total, data } = await servers[0].follows.getFollowings()
360 expect(total).to.equal(1)
361 expect(data).to.have.lengthOf(1)
362 }
363
364 {
365 const { total, data } = await servers[0].videos.list()
366 expect(total).to.equal(1)
367 expect(data).to.have.lengthOf(1)
368 }
369 })
321 }) 370 })
322 371
323 describe('Should propagate data on a new following', function () { 372 describe('Should propagate data on a new server follow', function () {
324 let video4: Video 373 let video4: VideoCreateResult
325 374
326 before(async function () { 375 before(async function () {
327 this.timeout(50000) 376 this.timeout(50000)
@@ -334,100 +383,75 @@ describe('Test follows', function () {
334 tags: [ 'tag1', 'tag2', 'tag3' ] 383 tags: [ 'tag1', 'tag2', 'tag3' ]
335 } 384 }
336 385
337 await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-2' }) 386 await servers[2].videos.upload({ attributes: { name: 'server3-2' } })
338 await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-3' }) 387 await servers[2].videos.upload({ attributes: { name: 'server3-3' } })
339 await uploadVideo(servers[2].url, servers[2].accessToken, video4Attributes) 388 video4 = await servers[2].videos.upload({ attributes: video4Attributes })
340 await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-5' }) 389 await servers[2].videos.upload({ attributes: { name: 'server3-5' } })
341 await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-6' }) 390 await servers[2].videos.upload({ attributes: { name: 'server3-6' } })
342 391
343 { 392 {
344 const user = { username: 'captain', password: 'password' } 393 const userAccessToken = await servers[2].users.generateUserAndToken('captain')
345 await createUser({ url: servers[2].url, accessToken: servers[2].accessToken, username: user.username, password: user.password })
346 const userAccessToken = await userLogin(servers[2], user)
347
348 const resVideos = await getVideosList(servers[2].url)
349 video4 = resVideos.body.data.find(v => v.name === 'server3-4')
350 394
351 { 395 await servers[2].videos.rate({ id: video4.id, rating: 'like' })
352 await rateVideo(servers[2].url, servers[2].accessToken, video4.id, 'like') 396 await servers[2].videos.rate({ token: userAccessToken, id: video4.id, rating: 'dislike' })
353 await rateVideo(servers[2].url, userAccessToken, video4.id, 'dislike') 397 }
354 }
355
356 {
357 {
358 const text = 'my super first comment'
359 const res = await addVideoCommentThread(servers[2].url, servers[2].accessToken, video4.id, text)
360 const threadId = res.body.comment.id
361
362 const text1 = 'my super answer to thread 1'
363 const childCommentRes = await addVideoCommentReply(servers[2].url, servers[2].accessToken, video4.id, threadId, text1)
364 const childCommentId = childCommentRes.body.comment.id
365
366 const text2 = 'my super answer to answer of thread 1'
367 await addVideoCommentReply(servers[2].url, servers[2].accessToken, video4.id, childCommentId, text2)
368
369 const text3 = 'my second answer to thread 1'
370 await addVideoCommentReply(servers[2].url, servers[2].accessToken, video4.id, threadId, text3)
371 }
372 398
373 { 399 {
374 const text = 'will be deleted' 400 await servers[2].comments.createThread({ videoId: video4.id, text: 'my super first comment' })
375 const res = await addVideoCommentThread(servers[2].url, servers[2].accessToken, video4.id, text)
376 const threadId = res.body.comment.id
377 401
378 const text1 = 'answer to deleted' 402 await servers[2].comments.addReplyToLastThread({ text: 'my super answer to thread 1' })
379 await addVideoCommentReply(servers[2].url, servers[2].accessToken, video4.id, threadId, text1) 403 await servers[2].comments.addReplyToLastReply({ text: 'my super answer to answer of thread 1' })
404 await servers[2].comments.addReplyToLastThread({ text: 'my second answer to thread 1' })
405 }
380 406
381 const text2 = 'will also be deleted' 407 {
382 const childCommentRes = await addVideoCommentReply(servers[2].url, servers[2].accessToken, video4.id, threadId, text2) 408 const { id: threadId } = await servers[2].comments.createThread({ videoId: video4.id, text: 'will be deleted' })
383 const childCommentId = childCommentRes.body.comment.id 409 await servers[2].comments.addReplyToLastThread({ text: 'answer to deleted' })
384 410
385 const text3 = 'my second answer to deleted' 411 const { id: replyId } = await servers[2].comments.addReplyToLastThread({ text: 'will also be deleted' })
386 await addVideoCommentReply(servers[2].url, servers[2].accessToken, video4.id, childCommentId, text3)
387 412
388 await deleteVideoComment(servers[2].url, servers[2].accessToken, video4.id, threadId) 413 await servers[2].comments.addReplyToLastReply({ text: 'my second answer to deleted' })
389 await deleteVideoComment(servers[2].url, servers[2].accessToken, video4.id, childCommentId)
390 }
391 }
392 414
393 { 415 await servers[2].comments.delete({ videoId: video4.id, commentId: threadId })
394 await createVideoCaption({ 416 await servers[2].comments.delete({ videoId: video4.id, commentId: replyId })
395 url: servers[2].url,
396 accessToken: servers[2].accessToken,
397 language: 'ar',
398 videoId: video4.id,
399 fixture: 'subtitle-good2.vtt'
400 })
401 }
402 } 417 }
403 418
419 await servers[2].captions.add({
420 language: 'ar',
421 videoId: video4.id,
422 fixture: 'subtitle-good2.vtt'
423 })
424
404 await waitJobs(servers) 425 await waitJobs(servers)
405 426
406 // Server 1 follows server 3 427 // Server 1 follows server 3
407 await follow(servers[0].url, [ servers[2].url ], servers[0].accessToken) 428 await servers[0].follows.follow({ hosts: [ servers[2].url ] })
408 429
409 await waitJobs(servers) 430 await waitJobs(servers)
410 }) 431 })
411 432
412 it('Should have the correct follows counts 3', async function () { 433 it('Should have the correct follows counts', async function () {
413 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 2) 434 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[0].host, followers: 0, following: 2 })
414 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0) 435 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
415 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[2].port, 1, 0) 436 await expectChannelsFollows({ server: servers[0], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 })
437 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 })
416 438
417 await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1) 439 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
418 await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0) 440 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[1].host, followers: 0, following: 0 })
441 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
442 await expectChannelsFollows({ server: servers[1], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 })
419 443
420 await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 1) 444 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
421 await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 1, 0) 445 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 })
422 }) 446 })
423 447
424 it('Should have propagated videos', async function () { 448 it('Should have propagated videos', async function () {
425 const res = await getVideosList(servers[0].url) 449 const { total, data } = await servers[0].videos.list()
426 expect(res.body.total).to.equal(7) 450 expect(total).to.equal(7)
427 451
428 const video2 = res.body.data.find(v => v.name === 'server3-2') 452 const video2 = data.find(v => v.name === 'server3-2')
429 video4 = res.body.data.find(v => v.name === 'server3-4') 453 video4 = data.find(v => v.name === 'server3-4')
430 const video6 = res.body.data.find(v => v.name === 'server3-6') 454 const video6 = data.find(v => v.name === 'server3-6')
431 455
432 expect(video2).to.not.be.undefined 456 expect(video2).to.not.be.undefined
433 expect(video4).to.not.be.undefined 457 expect(video4).to.not.be.undefined
@@ -444,7 +468,7 @@ describe('Test follows', function () {
444 support: 'my super support text', 468 support: 'my super support text',
445 account: { 469 account: {
446 name: 'root', 470 name: 'root',
447 host: 'localhost:' + servers[2].port 471 host: servers[2].host
448 }, 472 },
449 isLocal, 473 isLocal,
450 commentsEnabled: true, 474 commentsEnabled: true,
@@ -468,33 +492,31 @@ describe('Test follows', function () {
468 } 492 }
469 ] 493 ]
470 } 494 }
471 await completeVideoCheck(servers[0].url, video4, checkAttributes) 495 await completeVideoCheck(servers[0], video4, checkAttributes)
472 }) 496 })
473 497
474 it('Should have propagated comments', async function () { 498 it('Should have propagated comments', async function () {
475 const res1 = await getVideoCommentThreads(servers[0].url, video4.id, 0, 5, 'createdAt') 499 const { total, data } = await servers[0].comments.listThreads({ videoId: video4.id, sort: 'createdAt' })
476 500
477 expect(res1.body.total).to.equal(2) 501 expect(total).to.equal(2)
478 expect(res1.body.data).to.be.an('array') 502 expect(data).to.be.an('array')
479 expect(res1.body.data).to.have.lengthOf(2) 503 expect(data).to.have.lengthOf(2)
480 504
481 { 505 {
482 const comment: VideoComment = res1.body.data[0] 506 const comment = data[0]
483 expect(comment.inReplyToCommentId).to.be.null 507 expect(comment.inReplyToCommentId).to.be.null
484 expect(comment.text).equal('my super first comment') 508 expect(comment.text).equal('my super first comment')
485 expect(comment.videoId).to.equal(video4.id) 509 expect(comment.videoId).to.equal(video4.id)
486 expect(comment.id).to.equal(comment.threadId) 510 expect(comment.id).to.equal(comment.threadId)
487 expect(comment.account.name).to.equal('root') 511 expect(comment.account.name).to.equal('root')
488 expect(comment.account.host).to.equal('localhost:' + servers[2].port) 512 expect(comment.account.host).to.equal(servers[2].host)
489 expect(comment.totalReplies).to.equal(3) 513 expect(comment.totalReplies).to.equal(3)
490 expect(dateIsValid(comment.createdAt as string)).to.be.true 514 expect(dateIsValid(comment.createdAt as string)).to.be.true
491 expect(dateIsValid(comment.updatedAt as string)).to.be.true 515 expect(dateIsValid(comment.updatedAt as string)).to.be.true
492 516
493 const threadId = comment.threadId 517 const threadId = comment.threadId
494 518
495 const res2 = await getVideoThreadComments(servers[0].url, video4.id, threadId) 519 const tree = await servers[0].comments.getThread({ videoId: video4.id, threadId })
496
497 const tree: VideoCommentThreadTree = res2.body
498 expect(tree.comment.text).equal('my super first comment') 520 expect(tree.comment.text).equal('my super first comment')
499 expect(tree.children).to.have.lengthOf(2) 521 expect(tree.children).to.have.lengthOf(2)
500 522
@@ -512,7 +534,7 @@ describe('Test follows', function () {
512 } 534 }
513 535
514 { 536 {
515 const deletedComment: VideoComment = res1.body.data[1] 537 const deletedComment = data[1]
516 expect(deletedComment).to.not.be.undefined 538 expect(deletedComment).to.not.be.undefined
517 expect(deletedComment.isDeleted).to.be.true 539 expect(deletedComment.isDeleted).to.be.true
518 expect(deletedComment.deletedAt).to.not.be.null 540 expect(deletedComment.deletedAt).to.not.be.null
@@ -522,9 +544,7 @@ describe('Test follows', function () {
522 expect(deletedComment.totalReplies).to.equal(2) 544 expect(deletedComment.totalReplies).to.equal(2)
523 expect(dateIsValid(deletedComment.deletedAt as string)).to.be.true 545 expect(dateIsValid(deletedComment.deletedAt as string)).to.be.true
524 546
525 const res2 = await getVideoThreadComments(servers[0].url, video4.id, deletedComment.threadId) 547 const tree = await servers[0].comments.getThread({ videoId: video4.id, threadId: deletedComment.threadId })
526
527 const tree: VideoCommentThreadTree = res2.body
528 const [ commentRoot, deletedChildRoot ] = tree.children 548 const [ commentRoot, deletedChildRoot ] = tree.children
529 549
530 expect(deletedChildRoot).to.not.be.undefined 550 expect(deletedChildRoot).to.not.be.undefined
@@ -549,11 +569,11 @@ describe('Test follows', function () {
549 }) 569 })
550 570
551 it('Should have propagated captions', async function () { 571 it('Should have propagated captions', async function () {
552 const res = await listVideoCaptions(servers[0].url, video4.id) 572 const body = await servers[0].captions.list({ videoId: video4.id })
553 expect(res.body.total).to.equal(1) 573 expect(body.total).to.equal(1)
554 expect(res.body.data).to.have.lengthOf(1) 574 expect(body.data).to.have.lengthOf(1)
555 575
556 const caption1: VideoCaption = res.body.data[0] 576 const caption1 = body.data[0]
557 expect(caption1.language.id).to.equal('ar') 577 expect(caption1.language.id).to.equal('ar')
558 expect(caption1.language.label).to.equal('Arabic') 578 expect(caption1.language.label).to.equal('Arabic')
559 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/.+-ar.vtt$')) 579 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/.+-ar.vtt$'))
@@ -563,14 +583,39 @@ describe('Test follows', function () {
563 it('Should unfollow server 3 on server 1 and does not list server 3 videos', async function () { 583 it('Should unfollow server 3 on server 1 and does not list server 3 videos', async function () {
564 this.timeout(5000) 584 this.timeout(5000)
565 585
566 await unfollow(servers[0].url, servers[0].accessToken, servers[2]) 586 await servers[0].follows.unfollow({ target: servers[2] })
587
588 await waitJobs(servers)
589
590 const { total } = await servers[0].videos.list()
591 expect(total).to.equal(1)
592 })
593 })
594
595 describe('Should propagate data on a new channel follow', function () {
596
597 before(async function () {
598 this.timeout(60000)
599
600 await servers[2].videos.upload({ attributes: { name: 'server3-7' } })
567 601
568 await waitJobs(servers) 602 await waitJobs(servers)
569 603
570 const res = await getVideosList(servers[0].url) 604 const video = await servers[0].videos.find({ name: 'server3-7' })
571 expect(res.body.total).to.equal(1) 605 expect(video).to.not.exist
572 }) 606 })
573 607
608 it('Should have propagated channel video', async function () {
609 this.timeout(60000)
610
611 await servers[0].follows.follow({ handles: [ 'root_channel@' + servers[2].host ] })
612
613 await waitJobs(servers)
614
615 const video = await servers[0].videos.find({ name: 'server3-7' })
616
617 expect(video).to.exist
618 })
574 }) 619 })
575 620
576 after(async function () { 621 after(async function () {
diff --git a/server/tests/api/server/handle-down.ts b/server/tests/api/server/handle-down.ts
index d57d72f5e..2f3950354 100644
--- a/server/tests/api/server/handle-down.ts
+++ b/server/tests/api/server/handle-down.ts
@@ -1,51 +1,31 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { JobState, Video } from '../../../../shared/models' 4import * as chai from 'chai'
6import { VideoPrivacy } from '../../../../shared/models/videos'
7import { VideoCommentThreadTree } from '../../../../shared/models/videos/comment/video-comment.model'
8
9import { 5import {
10 cleanupTests, 6 cleanupTests,
11 closeAllSequelize, 7 CommentsCommand,
12 completeVideoCheck, 8 completeVideoCheck,
13 flushAndRunMultipleServers, 9 createMultipleServers,
14 getVideo,
15 getVideosList,
16 immutableAssign,
17 killallServers, 10 killallServers,
18 reRunServer, 11 PeerTubeServer,
19 ServerInfo,
20 setAccessTokensToServers, 12 setAccessTokensToServers,
21 setActorFollowScores, 13 wait,
22 unfollow, 14 waitJobs
23 updateVideo, 15} from '@shared/extra-utils'
24 uploadVideo, 16import { HttpStatusCode, JobState, VideoCreateResult, VideoPrivacy } from '@shared/models'
25 uploadVideoAndGetId,
26 wait
27} from '../../../../shared/extra-utils'
28import { follow, getFollowersListPaginationAndSort } from '../../../../shared/extra-utils/server/follows'
29import { getJobsListPaginationAndSort, waitJobs } from '../../../../shared/extra-utils/server/jobs'
30import {
31 addVideoCommentReply,
32 addVideoCommentThread,
33 getVideoCommentThreads,
34 getVideoThreadComments
35} from '../../../../shared/extra-utils/videos/video-comments'
36import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
37 17
38const expect = chai.expect 18const expect = chai.expect
39 19
40describe('Test handle downs', function () { 20describe('Test handle downs', function () {
41 let servers: ServerInfo[] = [] 21 let servers: PeerTubeServer[] = []
42 let threadIdServer1: number 22 let threadIdServer1: number
43 let threadIdServer2: number 23 let threadIdServer2: number
44 let commentIdServer1: number 24 let commentIdServer1: number
45 let commentIdServer2: number 25 let commentIdServer2: number
46 let missedVideo1: Video 26 let missedVideo1: VideoCreateResult
47 let missedVideo2: Video 27 let missedVideo2: VideoCreateResult
48 let unlistedVideo: Video 28 let unlistedVideo: VideoCreateResult
49 29
50 const videoIdsServer1: string[] = [] 30 const videoIdsServer1: string[] = []
51 31
@@ -62,17 +42,18 @@ describe('Test handle downs', function () {
62 fixture: 'video_short1.webm' 42 fixture: 'video_short1.webm'
63 } 43 }
64 44
65 const unlistedVideoAttributes = immutableAssign(videoAttributes, { 45 const unlistedVideoAttributes = { ...videoAttributes, privacy: VideoPrivacy.UNLISTED }
66 privacy: VideoPrivacy.UNLISTED
67 })
68 46
69 let checkAttributes: any 47 let checkAttributes: any
70 let unlistedCheckAttributes: any 48 let unlistedCheckAttributes: any
71 49
50 let commentCommands: CommentsCommand[]
51
72 before(async function () { 52 before(async function () {
73 this.timeout(30000) 53 this.timeout(30000)
74 54
75 servers = await flushAndRunMultipleServers(3) 55 servers = await createMultipleServers(3)
56 commentCommands = servers.map(s => s.comments)
76 57
77 checkAttributes = { 58 checkAttributes = {
78 name: 'my super name for server 1', 59 name: 'my super name for server 1',
@@ -106,9 +87,7 @@ describe('Test handle downs', function () {
106 } 87 }
107 ] 88 ]
108 } 89 }
109 unlistedCheckAttributes = immutableAssign(checkAttributes, { 90 unlistedCheckAttributes = { ...checkAttributes, privacy: VideoPrivacy.UNLISTED }
110 privacy: VideoPrivacy.UNLISTED
111 })
112 91
113 // Get the access tokens 92 // Get the access tokens
114 await setAccessTokensToServers(servers) 93 await setAccessTokensToServers(servers)
@@ -118,58 +97,53 @@ describe('Test handle downs', function () {
118 this.timeout(240000) 97 this.timeout(240000)
119 98
120 // Server 2 and 3 follow server 1 99 // Server 2 and 3 follow server 1
121 await follow(servers[1].url, [ servers[0].url ], servers[1].accessToken) 100 await servers[1].follows.follow({ hosts: [ servers[0].url ] })
122 await follow(servers[2].url, [ servers[0].url ], servers[2].accessToken) 101 await servers[2].follows.follow({ hosts: [ servers[0].url ] })
123 102
124 await waitJobs(servers) 103 await waitJobs(servers)
125 104
126 // Upload a video to server 1 105 // Upload a video to server 1
127 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 106 await servers[0].videos.upload({ attributes: videoAttributes })
128 107
129 await waitJobs(servers) 108 await waitJobs(servers)
130 109
131 // And check all servers have this video 110 // And check all servers have this video
132 for (const server of servers) { 111 for (const server of servers) {
133 const res = await getVideosList(server.url) 112 const { data } = await server.videos.list()
134 expect(res.body.data).to.be.an('array') 113 expect(data).to.be.an('array')
135 expect(res.body.data).to.have.lengthOf(1) 114 expect(data).to.have.lengthOf(1)
136 } 115 }
137 116
138 // Kill server 2 117 // Kill server 2
139 killallServers([ servers[1] ]) 118 await killallServers([ servers[1] ])
140 119
141 // Remove server 2 follower 120 // Remove server 2 follower
142 for (let i = 0; i < 10; i++) { 121 for (let i = 0; i < 10; i++) {
143 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 122 await servers[0].videos.upload({ attributes: videoAttributes })
144 } 123 }
145 124
146 await waitJobs([ servers[0], servers[2] ]) 125 await waitJobs([ servers[0], servers[2] ])
147 126
148 // Kill server 3 127 // Kill server 3
149 killallServers([ servers[2] ]) 128 await killallServers([ servers[2] ])
150 129
151 const resLastVideo1 = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 130 missedVideo1 = await servers[0].videos.upload({ attributes: videoAttributes })
152 missedVideo1 = resLastVideo1.body.video
153 131
154 const resLastVideo2 = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 132 missedVideo2 = await servers[0].videos.upload({ attributes: videoAttributes })
155 missedVideo2 = resLastVideo2.body.video
156 133
157 // Unlisted video 134 // Unlisted video
158 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, unlistedVideoAttributes) 135 unlistedVideo = await servers[0].videos.upload({ attributes: unlistedVideoAttributes })
159 unlistedVideo = resVideo.body.video
160 136
161 // Add comments to video 2 137 // Add comments to video 2
162 { 138 {
163 const text = 'thread 1' 139 const text = 'thread 1'
164 let resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, missedVideo2.uuid, text) 140 let comment = await commentCommands[0].createThread({ videoId: missedVideo2.uuid, text })
165 let comment = resComment.body.comment
166 threadIdServer1 = comment.id 141 threadIdServer1 = comment.id
167 142
168 resComment = await addVideoCommentReply(servers[0].url, servers[0].accessToken, missedVideo2.uuid, comment.id, 'comment 1-1') 143 comment = await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: comment.id, text: 'comment 1-1' })
169 comment = resComment.body.comment
170 144
171 resComment = await addVideoCommentReply(servers[0].url, servers[0].accessToken, missedVideo2.uuid, comment.id, 'comment 1-2') 145 const created = await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: comment.id, text: 'comment 1-2' })
172 commentIdServer1 = resComment.body.comment.id 146 commentIdServer1 = created.id
173 } 147 }
174 148
175 await waitJobs(servers[0]) 149 await waitJobs(servers[0])
@@ -177,90 +151,87 @@ describe('Test handle downs', function () {
177 await wait(11000) 151 await wait(11000)
178 152
179 // Only server 3 is still a follower of server 1 153 // Only server 3 is still a follower of server 1
180 const res = await getFollowersListPaginationAndSort({ url: servers[0].url, start: 0, count: 2, sort: 'createdAt' }) 154 const body = await servers[0].follows.getFollowers({ start: 0, count: 2, sort: 'createdAt' })
181 expect(res.body.data).to.be.an('array') 155 expect(body.data).to.be.an('array')
182 expect(res.body.data).to.have.lengthOf(1) 156 expect(body.data).to.have.lengthOf(1)
183 expect(res.body.data[0].follower.host).to.equal('localhost:' + servers[2].port) 157 expect(body.data[0].follower.host).to.equal('localhost:' + servers[2].port)
184 }) 158 })
185 159
186 it('Should not have pending/processing jobs anymore', async function () { 160 it('Should not have pending/processing jobs anymore', async function () {
187 const states: JobState[] = [ 'waiting', 'active' ] 161 const states: JobState[] = [ 'waiting', 'active' ]
188 162
189 for (const state of states) { 163 for (const state of states) {
190 const res = await getJobsListPaginationAndSort({ 164 const body = await servers[0].jobs.getJobsList({
191 url: servers[0].url,
192 accessToken: servers[0].accessToken,
193 state: state, 165 state: state,
194 start: 0, 166 start: 0,
195 count: 50, 167 count: 50,
196 sort: '-createdAt' 168 sort: '-createdAt'
197 }) 169 })
198 expect(res.body.data).to.have.length(0) 170 expect(body.data).to.have.length(0)
199 } 171 }
200 }) 172 })
201 173
202 it('Should re-follow server 1', async function () { 174 it('Should re-follow server 1', async function () {
203 this.timeout(35000) 175 this.timeout(35000)
204 176
205 await reRunServer(servers[1]) 177 await servers[1].run()
206 await reRunServer(servers[2]) 178 await servers[2].run()
207 179
208 await unfollow(servers[1].url, servers[1].accessToken, servers[0]) 180 await servers[1].follows.unfollow({ target: servers[0] })
209 await waitJobs(servers) 181 await waitJobs(servers)
210 182
211 await follow(servers[1].url, [ servers[0].url ], servers[1].accessToken) 183 await servers[1].follows.follow({ hosts: [ servers[0].url ] })
212 184
213 await waitJobs(servers) 185 await waitJobs(servers)
214 186
215 const res = await getFollowersListPaginationAndSort({ url: servers[0].url, start: 0, count: 2, sort: 'createdAt' }) 187 const body = await servers[0].follows.getFollowers({ start: 0, count: 2, sort: 'createdAt' })
216 expect(res.body.data).to.be.an('array') 188 expect(body.data).to.be.an('array')
217 expect(res.body.data).to.have.lengthOf(2) 189 expect(body.data).to.have.lengthOf(2)
218 }) 190 })
219 191
220 it('Should send an update to server 3, and automatically fetch the video', async function () { 192 it('Should send an update to server 3, and automatically fetch the video', async function () {
221 this.timeout(15000) 193 this.timeout(15000)
222 194
223 const res1 = await getVideosList(servers[2].url) 195 {
224 expect(res1.body.data).to.be.an('array') 196 const { data } = await servers[2].videos.list()
225 expect(res1.body.data).to.have.lengthOf(11) 197 expect(data).to.be.an('array')
198 expect(data).to.have.lengthOf(11)
199 }
226 200
227 await updateVideo(servers[0].url, servers[0].accessToken, missedVideo1.uuid, {}) 201 await servers[0].videos.update({ id: missedVideo1.uuid })
228 await updateVideo(servers[0].url, servers[0].accessToken, unlistedVideo.uuid, {}) 202 await servers[0].videos.update({ id: unlistedVideo.uuid })
229 203
230 await waitJobs(servers) 204 await waitJobs(servers)
231 205
232 const res = await getVideosList(servers[2].url) 206 {
233 expect(res.body.data).to.be.an('array') 207 const { data } = await servers[2].videos.list()
234 // 1 video is unlisted 208 expect(data).to.be.an('array')
235 expect(res.body.data).to.have.lengthOf(12) 209 // 1 video is unlisted
210 expect(data).to.have.lengthOf(12)
211 }
236 212
237 // Check unlisted video 213 // Check unlisted video
238 const resVideo = await getVideo(servers[2].url, unlistedVideo.uuid) 214 const video = await servers[2].videos.get({ id: unlistedVideo.uuid })
239 expect(resVideo.body).not.to.be.undefined 215 await completeVideoCheck(servers[2], video, unlistedCheckAttributes)
240
241 await completeVideoCheck(servers[2].url, resVideo.body, unlistedCheckAttributes)
242 }) 216 })
243 217
244 it('Should send comments on a video to server 3, and automatically fetch the video', async function () { 218 it('Should send comments on a video to server 3, and automatically fetch the video', async function () {
245 this.timeout(25000) 219 this.timeout(25000)
246 220
247 await addVideoCommentReply(servers[0].url, servers[0].accessToken, missedVideo2.uuid, commentIdServer1, 'comment 1-3') 221 await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: commentIdServer1, text: 'comment 1-3' })
248 222
249 await waitJobs(servers) 223 await waitJobs(servers)
250 224
251 const resVideo = await getVideo(servers[2].url, missedVideo2.uuid) 225 await servers[2].videos.get({ id: missedVideo2.uuid })
252 expect(resVideo.body).not.to.be.undefined
253 226
254 { 227 {
255 let resComment = await getVideoCommentThreads(servers[2].url, missedVideo2.uuid, 0, 5) 228 const { data } = await servers[2].comments.listThreads({ videoId: missedVideo2.uuid })
256 expect(resComment.body.data).to.be.an('array') 229 expect(data).to.be.an('array')
257 expect(resComment.body.data).to.have.lengthOf(1) 230 expect(data).to.have.lengthOf(1)
258
259 threadIdServer2 = resComment.body.data[0].id
260 231
261 resComment = await getVideoThreadComments(servers[2].url, missedVideo2.uuid, threadIdServer2) 232 threadIdServer2 = data[0].id
262 233
263 const tree: VideoCommentThreadTree = resComment.body 234 const tree = await servers[2].comments.getThread({ videoId: missedVideo2.uuid, threadId: threadIdServer2 })
264 expect(tree.comment.text).equal('thread 1') 235 expect(tree.comment.text).equal('thread 1')
265 expect(tree.children).to.have.lengthOf(1) 236 expect(tree.children).to.have.lengthOf(1)
266 237
@@ -283,57 +254,54 @@ describe('Test handle downs', function () {
283 it('Should correctly reply to the comment', async function () { 254 it('Should correctly reply to the comment', async function () {
284 this.timeout(15000) 255 this.timeout(15000)
285 256
286 await addVideoCommentReply(servers[2].url, servers[2].accessToken, missedVideo2.uuid, commentIdServer2, 'comment 1-4') 257 await servers[2].comments.addReply({ videoId: missedVideo2.uuid, toCommentId: commentIdServer2, text: 'comment 1-4' })
287 258
288 await waitJobs(servers) 259 await waitJobs(servers)
289 260
290 { 261 const tree = await commentCommands[0].getThread({ videoId: missedVideo2.uuid, threadId: threadIdServer1 })
291 const resComment = await getVideoThreadComments(servers[0].url, missedVideo2.uuid, threadIdServer1)
292 262
293 const tree: VideoCommentThreadTree = resComment.body 263 expect(tree.comment.text).equal('thread 1')
294 expect(tree.comment.text).equal('thread 1') 264 expect(tree.children).to.have.lengthOf(1)
295 expect(tree.children).to.have.lengthOf(1)
296 265
297 const firstChild = tree.children[0] 266 const firstChild = tree.children[0]
298 expect(firstChild.comment.text).to.equal('comment 1-1') 267 expect(firstChild.comment.text).to.equal('comment 1-1')
299 expect(firstChild.children).to.have.lengthOf(1) 268 expect(firstChild.children).to.have.lengthOf(1)
300 269
301 const childOfFirstChild = firstChild.children[0] 270 const childOfFirstChild = firstChild.children[0]
302 expect(childOfFirstChild.comment.text).to.equal('comment 1-2') 271 expect(childOfFirstChild.comment.text).to.equal('comment 1-2')
303 expect(childOfFirstChild.children).to.have.lengthOf(1) 272 expect(childOfFirstChild.children).to.have.lengthOf(1)
304 273
305 const childOfChildFirstChild = childOfFirstChild.children[0] 274 const childOfChildFirstChild = childOfFirstChild.children[0]
306 expect(childOfChildFirstChild.comment.text).to.equal('comment 1-3') 275 expect(childOfChildFirstChild.comment.text).to.equal('comment 1-3')
307 expect(childOfChildFirstChild.children).to.have.lengthOf(1) 276 expect(childOfChildFirstChild.children).to.have.lengthOf(1)
308 277
309 const childOfChildOfChildOfFirstChild = childOfChildFirstChild.children[0] 278 const childOfChildOfChildOfFirstChild = childOfChildFirstChild.children[0]
310 expect(childOfChildOfChildOfFirstChild.comment.text).to.equal('comment 1-4') 279 expect(childOfChildOfChildOfFirstChild.comment.text).to.equal('comment 1-4')
311 expect(childOfChildOfChildOfFirstChild.children).to.have.lengthOf(0) 280 expect(childOfChildOfChildOfFirstChild.children).to.have.lengthOf(0)
312 }
313 }) 281 })
314 282
315 it('Should upload many videos on server 1', async function () { 283 it('Should upload many videos on server 1', async function () {
316 this.timeout(120000) 284 this.timeout(120000)
317 285
318 for (let i = 0; i < 10; i++) { 286 for (let i = 0; i < 10; i++) {
319 const uuid = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video ' + i })).uuid 287 const uuid = (await servers[0].videos.quickUpload({ name: 'video ' + i })).uuid
320 videoIdsServer1.push(uuid) 288 videoIdsServer1.push(uuid)
321 } 289 }
322 290
323 await waitJobs(servers) 291 await waitJobs(servers)
324 292
325 for (const id of videoIdsServer1) { 293 for (const id of videoIdsServer1) {
326 await getVideo(servers[1].url, id) 294 await servers[1].videos.get({ id })
327 } 295 }
328 296
329 await waitJobs(servers) 297 await waitJobs(servers)
330 await setActorFollowScores(servers[1].internalServerNumber, 20) 298 await servers[1].sql.setActorFollowScores(20)
331 299
332 // Wait video expiration 300 // Wait video expiration
333 await wait(11000) 301 await wait(11000)
334 302
335 // Refresh video -> score + 10 = 30 303 // Refresh video -> score + 10 = 30
336 await getVideo(servers[1].url, videoIdsServer1[0]) 304 await servers[1].videos.get({ id: videoIdsServer1[0] })
337 305
338 await waitJobs(servers) 306 await waitJobs(servers)
339 }) 307 })
@@ -341,27 +309,25 @@ describe('Test handle downs', function () {
341 it('Should remove followings that are down', async function () { 309 it('Should remove followings that are down', async function () {
342 this.timeout(120000) 310 this.timeout(120000)
343 311
344 killallServers([ servers[0] ]) 312 await killallServers([ servers[0] ])
345 313
346 // Wait video expiration 314 // Wait video expiration
347 await wait(11000) 315 await wait(11000)
348 316
349 for (let i = 0; i < 5; i++) { 317 for (let i = 0; i < 5; i++) {
350 try { 318 try {
351 await getVideo(servers[1].url, videoIdsServer1[i]) 319 await servers[1].videos.get({ id: videoIdsServer1[i] })
352 await waitJobs([ servers[1] ]) 320 await waitJobs([ servers[1] ])
353 await wait(1500) 321 await wait(1500)
354 } catch {} 322 } catch {}
355 } 323 }
356 324
357 for (const id of videoIdsServer1) { 325 for (const id of videoIdsServer1) {
358 await getVideo(servers[1].url, id, HttpStatusCode.FORBIDDEN_403) 326 await servers[1].videos.get({ id, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
359 } 327 }
360 }) 328 })
361 329
362 after(async function () { 330 after(async function () {
363 await closeAllSequelize([ servers[1] ])
364
365 await cleanupTests(servers) 331 await cleanupTests(servers)
366 }) 332 })
367}) 333})
diff --git a/server/tests/api/server/homepage.ts b/server/tests/api/server/homepage.ts
index e8ba89ca6..cb3ba5677 100644
--- a/server/tests/api/server/homepage.ts
+++ b/server/tests/api/server/homepage.ts
@@ -2,51 +2,48 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { HttpStatusCode } from '@shared/core-utils' 5import { HttpStatusCode } from '@shared/models'
6import { CustomPage, ServerConfig } from '@shared/models'
7import { 6import {
8 cleanupTests, 7 cleanupTests,
9 flushAndRunServer, 8 createSingleServer,
10 getConfig, 9 CustomPagesCommand,
11 getInstanceHomepage,
12 killallServers, 10 killallServers,
13 reRunServer, 11 PeerTubeServer,
14 ServerInfo, 12 setAccessTokensToServers
15 setAccessTokensToServers,
16 updateInstanceHomepage
17} from '../../../../shared/extra-utils/index' 13} from '../../../../shared/extra-utils/index'
18 14
19const expect = chai.expect 15const expect = chai.expect
20 16
21async function getHomepageState (server: ServerInfo) { 17async function getHomepageState (server: PeerTubeServer) {
22 const res = await getConfig(server.url) 18 const config = await server.config.getConfig()
23 19
24 const config = res.body as ServerConfig
25 return config.homepage.enabled 20 return config.homepage.enabled
26} 21}
27 22
28describe('Test instance homepage actions', function () { 23describe('Test instance homepage actions', function () {
29 let server: ServerInfo 24 let server: PeerTubeServer
25 let command: CustomPagesCommand
30 26
31 before(async function () { 27 before(async function () {
32 this.timeout(30000) 28 this.timeout(30000)
33 29
34 server = await flushAndRunServer(1) 30 server = await createSingleServer(1)
35 await setAccessTokensToServers([ server ]) 31 await setAccessTokensToServers([ server ])
32
33 command = server.customPage
36 }) 34 })
37 35
38 it('Should not have a homepage', async function () { 36 it('Should not have a homepage', async function () {
39 const state = await getHomepageState(server) 37 const state = await getHomepageState(server)
40 expect(state).to.be.false 38 expect(state).to.be.false
41 39
42 await getInstanceHomepage(server.url, HttpStatusCode.NOT_FOUND_404) 40 await command.getInstanceHomepage({ expectedStatus: HttpStatusCode.NOT_FOUND_404 })
43 }) 41 })
44 42
45 it('Should set a homepage', async function () { 43 it('Should set a homepage', async function () {
46 await updateInstanceHomepage(server.url, server.accessToken, '<picsou-magazine></picsou-magazine>') 44 await command.updateInstanceHomepage({ content: '<picsou-magazine></picsou-magazine>' })
47 45
48 const res = await getInstanceHomepage(server.url) 46 const page = await command.getInstanceHomepage()
49 const page: CustomPage = res.body
50 expect(page.content).to.equal('<picsou-magazine></picsou-magazine>') 47 expect(page.content).to.equal('<picsou-magazine></picsou-magazine>')
51 48
52 const state = await getHomepageState(server) 49 const state = await getHomepageState(server)
@@ -56,12 +53,11 @@ describe('Test instance homepage actions', function () {
56 it('Should have the same homepage after a restart', async function () { 53 it('Should have the same homepage after a restart', async function () {
57 this.timeout(30000) 54 this.timeout(30000)
58 55
59 killallServers([ server ]) 56 await killallServers([ server ])
60 57
61 await reRunServer(server) 58 await server.run()
62 59
63 const res = await getInstanceHomepage(server.url) 60 const page = await command.getInstanceHomepage()
64 const page: CustomPage = res.body
65 expect(page.content).to.equal('<picsou-magazine></picsou-magazine>') 61 expect(page.content).to.equal('<picsou-magazine></picsou-magazine>')
66 62
67 const state = await getHomepageState(server) 63 const state = await getHomepageState(server)
@@ -69,10 +65,9 @@ describe('Test instance homepage actions', function () {
69 }) 65 })
70 66
71 it('Should empty the homepage', async function () { 67 it('Should empty the homepage', async function () {
72 await updateInstanceHomepage(server.url, server.accessToken, '') 68 await command.updateInstanceHomepage({ content: '' })
73 69
74 const res = await getInstanceHomepage(server.url) 70 const page = await command.getInstanceHomepage()
75 const page: CustomPage = res.body
76 expect(page.content).to.be.empty 71 expect(page.content).to.be.empty
77 72
78 const state = await getHomepageState(server) 73 const state = await getHomepageState(server)
diff --git a/server/tests/api/server/jobs.ts b/server/tests/api/server/jobs.ts
index d0e222997..c10c154c2 100644
--- a/server/tests/api/server/jobs.ts
+++ b/server/tests/api/server/jobs.ts
@@ -1,24 +1,26 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { cleanupTests, ServerInfo, setAccessTokensToServers } from '../../../../shared/extra-utils/index' 4import * as chai from 'chai'
6import { doubleFollow } from '../../../../shared/extra-utils/server/follows' 5import {
7import { getJobsList, getJobsListPaginationAndSort, waitJobs } from '../../../../shared/extra-utils/server/jobs' 6 cleanupTests,
8import { flushAndRunMultipleServers } from '../../../../shared/extra-utils/server/servers' 7 createMultipleServers,
9import { uploadVideo } from '../../../../shared/extra-utils/videos/videos' 8 dateIsValid,
10import { dateIsValid } from '../../../../shared/extra-utils/miscs/miscs' 9 doubleFollow,
11import { Job } from '../../../../shared/models/server' 10 PeerTubeServer,
11 setAccessTokensToServers,
12 waitJobs
13} from '@shared/extra-utils'
12 14
13const expect = chai.expect 15const expect = chai.expect
14 16
15describe('Test jobs', function () { 17describe('Test jobs', function () {
16 let servers: ServerInfo[] 18 let servers: PeerTubeServer[]
17 19
18 before(async function () { 20 before(async function () {
19 this.timeout(30000) 21 this.timeout(30000)
20 22
21 servers = await flushAndRunMultipleServers(2) 23 servers = await createMultipleServers(2)
22 24
23 await setAccessTokensToServers(servers) 25 await setAccessTokensToServers(servers)
24 26
@@ -27,36 +29,34 @@ describe('Test jobs', function () {
27 }) 29 })
28 30
29 it('Should create some jobs', async function () { 31 it('Should create some jobs', async function () {
30 this.timeout(60000) 32 this.timeout(120000)
31 33
32 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video1' }) 34 await servers[1].videos.upload({ attributes: { name: 'video1' } })
33 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video2' }) 35 await servers[1].videos.upload({ attributes: { name: 'video2' } })
34 36
35 await waitJobs(servers) 37 await waitJobs(servers)
36 }) 38 })
37 39
38 it('Should list jobs', async function () { 40 it('Should list jobs', async function () {
39 const res = await getJobsList(servers[1].url, servers[1].accessToken, 'completed') 41 const body = await servers[1].jobs.getJobsList({ state: 'completed' })
40 expect(res.body.total).to.be.above(2) 42 expect(body.total).to.be.above(2)
41 expect(res.body.data).to.have.length.above(2) 43 expect(body.data).to.have.length.above(2)
42 }) 44 })
43 45
44 it('Should list jobs with sort, pagination and job type', async function () { 46 it('Should list jobs with sort, pagination and job type', async function () {
45 { 47 {
46 const res = await getJobsListPaginationAndSort({ 48 const body = await servers[1].jobs.getJobsList({
47 url: servers[1].url,
48 accessToken: servers[1].accessToken,
49 state: 'completed', 49 state: 'completed',
50 start: 1, 50 start: 1,
51 count: 2, 51 count: 2,
52 sort: 'createdAt' 52 sort: 'createdAt'
53 }) 53 })
54 expect(res.body.total).to.be.above(2) 54 expect(body.total).to.be.above(2)
55 expect(res.body.data).to.have.lengthOf(2) 55 expect(body.data).to.have.lengthOf(2)
56 56
57 let job: Job = res.body.data[0] 57 let job = body.data[0]
58 // Skip repeat jobs 58 // Skip repeat jobs
59 if (job.type === 'videos-views') job = res.body.data[1] 59 if (job.type === 'videos-views') job = body.data[1]
60 60
61 expect(job.state).to.equal('completed') 61 expect(job.state).to.equal('completed')
62 expect(job.type.startsWith('activitypub-')).to.be.true 62 expect(job.type.startsWith('activitypub-')).to.be.true
@@ -66,29 +66,26 @@ describe('Test jobs', function () {
66 } 66 }
67 67
68 { 68 {
69 const res = await getJobsListPaginationAndSort({ 69 const body = await servers[1].jobs.getJobsList({
70 url: servers[1].url,
71 accessToken: servers[1].accessToken,
72 state: 'completed', 70 state: 'completed',
73 start: 0, 71 start: 0,
74 count: 100, 72 count: 100,
75 sort: 'createdAt', 73 sort: 'createdAt',
76 jobType: 'activitypub-http-broadcast' 74 jobType: 'activitypub-http-broadcast'
77 }) 75 })
78 expect(res.body.total).to.be.above(2) 76 expect(body.total).to.be.above(2)
79 77
80 for (const j of res.body.data as Job[]) { 78 for (const j of body.data) {
81 expect(j.type).to.equal('activitypub-http-broadcast') 79 expect(j.type).to.equal('activitypub-http-broadcast')
82 } 80 }
83 } 81 }
84 }) 82 })
85 83
86 it('Should list all jobs', async function () { 84 it('Should list all jobs', async function () {
87 const res = await getJobsList(servers[1].url, servers[1].accessToken) 85 const body = await servers[1].jobs.getJobsList()
88 86 expect(body.total).to.be.above(2)
89 const jobs = res.body.data as Job[]
90 87
91 expect(res.body.total).to.be.above(2) 88 const jobs = body.data
92 expect(jobs).to.have.length.above(2) 89 expect(jobs).to.have.length.above(2)
93 90
94 // We know there are a least 1 delayed job (video views) and 1 completed job (broadcast) 91 // We know there are a least 1 delayed job (video views) and 1 completed job (broadcast)
diff --git a/server/tests/api/server/logs.ts b/server/tests/api/server/logs.ts
index bc398ea73..bcd94dda3 100644
--- a/server/tests/api/server/logs.ts
+++ b/server/tests/api/server/logs.ts
@@ -4,27 +4,27 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 flushAndRunServer, 7 createSingleServer,
8 killallServers, 8 killallServers,
9 makePingRequest, 9 LogsCommand,
10 reRunServer, 10 PeerTubeServer,
11 ServerInfo, 11 setAccessTokensToServers,
12 setAccessTokensToServers 12 waitJobs
13} from '../../../../shared/extra-utils/index' 13} from '@shared/extra-utils'
14import { getAuditLogs, getLogs } from '../../../../shared/extra-utils/logs/logs'
15import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
16import { uploadVideo } from '../../../../shared/extra-utils/videos/videos'
17 14
18const expect = chai.expect 15const expect = chai.expect
19 16
20describe('Test logs', function () { 17describe('Test logs', function () {
21 let server: ServerInfo 18 let server: PeerTubeServer
19 let logsCommand: LogsCommand
22 20
23 before(async function () { 21 before(async function () {
24 this.timeout(30000) 22 this.timeout(30000)
25 23
26 server = await flushAndRunServer(1) 24 server = await createSingleServer(1)
27 await setAccessTokensToServers([ server ]) 25 await setAccessTokensToServers([ server ])
26
27 logsCommand = server.logs
28 }) 28 })
29 29
30 describe('With the standard log file', function () { 30 describe('With the standard log file', function () {
@@ -32,16 +32,16 @@ describe('Test logs', function () {
32 it('Should get logs with a start date', async function () { 32 it('Should get logs with a start date', async function () {
33 this.timeout(20000) 33 this.timeout(20000)
34 34
35 await uploadVideo(server.url, server.accessToken, { name: 'video 1' }) 35 await server.videos.upload({ attributes: { name: 'video 1' } })
36 await waitJobs([ server ]) 36 await waitJobs([ server ])
37 37
38 const now = new Date() 38 const now = new Date()
39 39
40 await uploadVideo(server.url, server.accessToken, { name: 'video 2' }) 40 await server.videos.upload({ attributes: { name: 'video 2' } })
41 await waitJobs([ server ]) 41 await waitJobs([ server ])
42 42
43 const res = await getLogs(server.url, server.accessToken, now) 43 const body = await logsCommand.getLogs({ startDate: now })
44 const logsString = JSON.stringify(res.body) 44 const logsString = JSON.stringify(body)
45 45
46 expect(logsString.includes('video 1')).to.be.false 46 expect(logsString.includes('video 1')).to.be.false
47 expect(logsString.includes('video 2')).to.be.true 47 expect(logsString.includes('video 2')).to.be.true
@@ -50,21 +50,21 @@ describe('Test logs', function () {
50 it('Should get logs with an end date', async function () { 50 it('Should get logs with an end date', async function () {
51 this.timeout(30000) 51 this.timeout(30000)
52 52
53 await uploadVideo(server.url, server.accessToken, { name: 'video 3' }) 53 await server.videos.upload({ attributes: { name: 'video 3' } })
54 await waitJobs([ server ]) 54 await waitJobs([ server ])
55 55
56 const now1 = new Date() 56 const now1 = new Date()
57 57
58 await uploadVideo(server.url, server.accessToken, { name: 'video 4' }) 58 await server.videos.upload({ attributes: { name: 'video 4' } })
59 await waitJobs([ server ]) 59 await waitJobs([ server ])
60 60
61 const now2 = new Date() 61 const now2 = new Date()
62 62
63 await uploadVideo(server.url, server.accessToken, { name: 'video 5' }) 63 await server.videos.upload({ attributes: { name: 'video 5' } })
64 await waitJobs([ server ]) 64 await waitJobs([ server ])
65 65
66 const res = await getLogs(server.url, server.accessToken, now1, now2) 66 const body = await logsCommand.getLogs({ startDate: now1, endDate: now2 })
67 const logsString = JSON.stringify(res.body) 67 const logsString = JSON.stringify(body)
68 68
69 expect(logsString.includes('video 3')).to.be.false 69 expect(logsString.includes('video 3')).to.be.false
70 expect(logsString.includes('video 4')).to.be.true 70 expect(logsString.includes('video 4')).to.be.true
@@ -76,19 +76,19 @@ describe('Test logs', function () {
76 76
77 const now = new Date() 77 const now = new Date()
78 78
79 await uploadVideo(server.url, server.accessToken, { name: 'video 6' }) 79 await server.videos.upload({ attributes: { name: 'video 6' } })
80 await waitJobs([ server ]) 80 await waitJobs([ server ])
81 81
82 { 82 {
83 const res = await getLogs(server.url, server.accessToken, now, undefined, 'info') 83 const body = await logsCommand.getLogs({ startDate: now, level: 'info' })
84 const logsString = JSON.stringify(res.body) 84 const logsString = JSON.stringify(body)
85 85
86 expect(logsString.includes('video 6')).to.be.true 86 expect(logsString.includes('video 6')).to.be.true
87 } 87 }
88 88
89 { 89 {
90 const res = await getLogs(server.url, server.accessToken, now, undefined, 'warn') 90 const body = await logsCommand.getLogs({ startDate: now, level: 'warn' })
91 const logsString = JSON.stringify(res.body) 91 const logsString = JSON.stringify(body)
92 92
93 expect(logsString.includes('video 6')).to.be.false 93 expect(logsString.includes('video 6')).to.be.false
94 } 94 }
@@ -99,10 +99,10 @@ describe('Test logs', function () {
99 99
100 const now = new Date() 100 const now = new Date()
101 101
102 await makePingRequest(server) 102 await server.servers.ping()
103 103
104 const res = await getLogs(server.url, server.accessToken, now, undefined, 'info') 104 const body = await logsCommand.getLogs({ startDate: now, level: 'info' })
105 const logsString = JSON.stringify(res.body) 105 const logsString = JSON.stringify(body)
106 106
107 expect(logsString.includes('/api/v1/ping')).to.be.true 107 expect(logsString.includes('/api/v1/ping')).to.be.true
108 }) 108 })
@@ -110,16 +110,16 @@ describe('Test logs', function () {
110 it('Should not log ping requests', async function () { 110 it('Should not log ping requests', async function () {
111 this.timeout(30000) 111 this.timeout(30000)
112 112
113 killallServers([ server ]) 113 await killallServers([ server ])
114 114
115 await reRunServer(server, { log: { log_ping_requests: false } }) 115 await server.run({ log: { log_ping_requests: false } })
116 116
117 const now = new Date() 117 const now = new Date()
118 118
119 await makePingRequest(server) 119 await server.servers.ping()
120 120
121 const res = await getLogs(server.url, server.accessToken, now, undefined, 'info') 121 const body = await logsCommand.getLogs({ startDate: now, level: 'info' })
122 const logsString = JSON.stringify(res.body) 122 const logsString = JSON.stringify(body)
123 123
124 expect(logsString.includes('/api/v1/ping')).to.be.false 124 expect(logsString.includes('/api/v1/ping')).to.be.false
125 }) 125 })
@@ -129,23 +129,23 @@ describe('Test logs', function () {
129 it('Should get logs with a start date', async function () { 129 it('Should get logs with a start date', async function () {
130 this.timeout(20000) 130 this.timeout(20000)
131 131
132 await uploadVideo(server.url, server.accessToken, { name: 'video 7' }) 132 await server.videos.upload({ attributes: { name: 'video 7' } })
133 await waitJobs([ server ]) 133 await waitJobs([ server ])
134 134
135 const now = new Date() 135 const now = new Date()
136 136
137 await uploadVideo(server.url, server.accessToken, { name: 'video 8' }) 137 await server.videos.upload({ attributes: { name: 'video 8' } })
138 await waitJobs([ server ]) 138 await waitJobs([ server ])
139 139
140 const res = await getAuditLogs(server.url, server.accessToken, now) 140 const body = await logsCommand.getAuditLogs({ startDate: now })
141 const logsString = JSON.stringify(res.body) 141 const logsString = JSON.stringify(body)
142 142
143 expect(logsString.includes('video 7')).to.be.false 143 expect(logsString.includes('video 7')).to.be.false
144 expect(logsString.includes('video 8')).to.be.true 144 expect(logsString.includes('video 8')).to.be.true
145 145
146 expect(res.body).to.have.lengthOf(1) 146 expect(body).to.have.lengthOf(1)
147 147
148 const item = res.body[0] 148 const item = body[0]
149 149
150 const message = JSON.parse(item.message) 150 const message = JSON.parse(item.message)
151 expect(message.domain).to.equal('videos') 151 expect(message.domain).to.equal('videos')
@@ -155,21 +155,21 @@ describe('Test logs', function () {
155 it('Should get logs with an end date', async function () { 155 it('Should get logs with an end date', async function () {
156 this.timeout(30000) 156 this.timeout(30000)
157 157
158 await uploadVideo(server.url, server.accessToken, { name: 'video 9' }) 158 await server.videos.upload({ attributes: { name: 'video 9' } })
159 await waitJobs([ server ]) 159 await waitJobs([ server ])
160 160
161 const now1 = new Date() 161 const now1 = new Date()
162 162
163 await uploadVideo(server.url, server.accessToken, { name: 'video 10' }) 163 await server.videos.upload({ attributes: { name: 'video 10' } })
164 await waitJobs([ server ]) 164 await waitJobs([ server ])
165 165
166 const now2 = new Date() 166 const now2 = new Date()
167 167
168 await uploadVideo(server.url, server.accessToken, { name: 'video 11' }) 168 await server.videos.upload({ attributes: { name: 'video 11' } })
169 await waitJobs([ server ]) 169 await waitJobs([ server ])
170 170
171 const res = await getAuditLogs(server.url, server.accessToken, now1, now2) 171 const body = await logsCommand.getAuditLogs({ startDate: now1, endDate: now2 })
172 const logsString = JSON.stringify(res.body) 172 const logsString = JSON.stringify(body)
173 173
174 expect(logsString.includes('video 9')).to.be.false 174 expect(logsString.includes('video 9')).to.be.false
175 expect(logsString.includes('video 10')).to.be.true 175 expect(logsString.includes('video 10')).to.be.true
diff --git a/server/tests/api/server/no-client.ts b/server/tests/api/server/no-client.ts
index d589f51f3..719813ae9 100644
--- a/server/tests/api/server/no-client.ts
+++ b/server/tests/api/server/no-client.ts
@@ -1,16 +1,15 @@
1import 'mocha' 1import 'mocha'
2import * as request from 'supertest' 2import * as request from 'supertest'
3import { ServerInfo } from '../../../../shared/extra-utils' 3import { cleanupTests, createSingleServer, PeerTubeServer } from '@shared/extra-utils'
4import { cleanupTests, flushAndRunServer } from '../../../../shared/extra-utils/server/servers' 4import { HttpStatusCode } from '@shared/models'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6 5
7describe('Start and stop server without web client routes', function () { 6describe('Start and stop server without web client routes', function () {
8 let server: ServerInfo 7 let server: PeerTubeServer
9 8
10 before(async function () { 9 before(async function () {
11 this.timeout(30000) 10 this.timeout(30000)
12 11
13 server = await flushAndRunServer(1, {}, [ '--no-client' ]) 12 server = await createSingleServer(1, {}, { peertubeArgs: [ '--no-client' ] })
14 }) 13 })
15 14
16 it('Should fail getting the client', function () { 15 it('Should fail getting the client', function () {
diff --git a/server/tests/api/server/plugins.ts b/server/tests/api/server/plugins.ts
index 6b61c7c33..5f9f4ffdd 100644
--- a/server/tests/api/server/plugins.ts
+++ b/server/tests/api/server/plugins.ts
@@ -2,41 +2,23 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { HttpStatusCode } from '@shared/core-utils'
6import { 5import {
7 cleanupTests, 6 cleanupTests,
8 closeAllSequelize, 7 createSingleServer,
9 flushAndRunServer,
10 getConfig,
11 getMyUserInformation,
12 getPlugin,
13 getPluginPackageJSON,
14 getPluginTestPath,
15 getPublicSettings,
16 installPlugin,
17 killallServers, 8 killallServers,
18 listAvailablePlugins, 9 PeerTubeServer,
19 listPlugins, 10 PluginsCommand,
20 reRunServer,
21 ServerInfo,
22 setAccessTokensToServers, 11 setAccessTokensToServers,
23 setPluginVersion,
24 testHelloWorldRegisteredSettings, 12 testHelloWorldRegisteredSettings,
25 uninstallPlugin, 13 wait
26 updateCustomSubConfig,
27 updateMyUser,
28 updatePlugin,
29 updatePluginPackageJSON,
30 updatePluginSettings,
31 wait,
32 waitUntilLog
33} from '@shared/extra-utils' 14} from '@shared/extra-utils'
34import { PeerTubePlugin, PeerTubePluginIndex, PluginPackageJson, PluginType, PublicServerSetting, ServerConfig, User } from '@shared/models' 15import { HttpStatusCode, PluginType } from '@shared/models'
35 16
36const expect = chai.expect 17const expect = chai.expect
37 18
38describe('Test plugins', function () { 19describe('Test plugins', function () {
39 let server: ServerInfo = null 20 let server: PeerTubeServer = null
21 let command: PluginsCommand
40 22
41 before(async function () { 23 before(async function () {
42 this.timeout(30000) 24 this.timeout(30000)
@@ -46,68 +28,61 @@ describe('Test plugins', function () {
46 index: { check_latest_versions_interval: '5 seconds' } 28 index: { check_latest_versions_interval: '5 seconds' }
47 } 29 }
48 } 30 }
49 server = await flushAndRunServer(1, configOverride) 31 server = await createSingleServer(1, configOverride)
50 await setAccessTokensToServers([ server ]) 32 await setAccessTokensToServers([ server ])
33
34 command = server.plugins
51 }) 35 })
52 36
53 it('Should list and search available plugins and themes', async function () { 37 it('Should list and search available plugins and themes', async function () {
54 this.timeout(30000) 38 this.timeout(30000)
55 39
56 { 40 {
57 const res = await listAvailablePlugins({ 41 const body = await command.listAvailable({
58 url: server.url,
59 accessToken: server.accessToken,
60 count: 1, 42 count: 1,
61 start: 0, 43 start: 0,
62 pluginType: PluginType.THEME, 44 pluginType: PluginType.THEME,
63 search: 'background-red' 45 search: 'background-red'
64 }) 46 })
65 47
66 expect(res.body.total).to.be.at.least(1) 48 expect(body.total).to.be.at.least(1)
67 expect(res.body.data).to.have.lengthOf(1) 49 expect(body.data).to.have.lengthOf(1)
68 } 50 }
69 51
70 { 52 {
71 const res1 = await listAvailablePlugins({ 53 const body1 = await command.listAvailable({
72 url: server.url,
73 accessToken: server.accessToken,
74 count: 2, 54 count: 2,
75 start: 0, 55 start: 0,
76 sort: 'npmName' 56 sort: 'npmName'
77 }) 57 })
78 const data1: PeerTubePluginIndex[] = res1.body.data 58 expect(body1.total).to.be.at.least(2)
79 59
80 expect(res1.body.total).to.be.at.least(2) 60 const data1 = body1.data
81 expect(data1).to.have.lengthOf(2) 61 expect(data1).to.have.lengthOf(2)
82 62
83 const res2 = await listAvailablePlugins({ 63 const body2 = await command.listAvailable({
84 url: server.url,
85 accessToken: server.accessToken,
86 count: 2, 64 count: 2,
87 start: 0, 65 start: 0,
88 sort: '-npmName' 66 sort: '-npmName'
89 }) 67 })
90 const data2: PeerTubePluginIndex[] = res2.body.data 68 expect(body2.total).to.be.at.least(2)
91 69
92 expect(res2.body.total).to.be.at.least(2) 70 const data2 = body2.data
93 expect(data2).to.have.lengthOf(2) 71 expect(data2).to.have.lengthOf(2)
94 72
95 expect(data1[0].npmName).to.not.equal(data2[0].npmName) 73 expect(data1[0].npmName).to.not.equal(data2[0].npmName)
96 } 74 }
97 75
98 { 76 {
99 const res = await listAvailablePlugins({ 77 const body = await command.listAvailable({
100 url: server.url,
101 accessToken: server.accessToken,
102 count: 10, 78 count: 10,
103 start: 0, 79 start: 0,
104 pluginType: PluginType.THEME, 80 pluginType: PluginType.THEME,
105 search: 'background-red', 81 search: 'background-red',
106 currentPeerTubeEngine: '1.0.0' 82 currentPeerTubeEngine: '1.0.0'
107 }) 83 })
108 const data: PeerTubePluginIndex[] = res.body.data
109 84
110 const p = data.find(p => p.npmName === 'peertube-theme-background-red') 85 const p = body.data.find(p => p.npmName === 'peertube-theme-background-red')
111 expect(p).to.be.undefined 86 expect(p).to.be.undefined
112 } 87 }
113 }) 88 })
@@ -115,22 +90,12 @@ describe('Test plugins', function () {
115 it('Should install a plugin and a theme', async function () { 90 it('Should install a plugin and a theme', async function () {
116 this.timeout(30000) 91 this.timeout(30000)
117 92
118 await installPlugin({ 93 await command.install({ npmName: 'peertube-plugin-hello-world' })
119 url: server.url, 94 await command.install({ npmName: 'peertube-theme-background-red' })
120 accessToken: server.accessToken,
121 npmName: 'peertube-plugin-hello-world'
122 })
123
124 await installPlugin({
125 url: server.url,
126 accessToken: server.accessToken,
127 npmName: 'peertube-theme-background-red'
128 })
129 }) 95 })
130 96
131 it('Should have the plugin loaded in the configuration', async function () { 97 it('Should have the plugin loaded in the configuration', async function () {
132 const res = await getConfig(server.url) 98 const config = await server.config.getConfig()
133 const config: ServerConfig = res.body
134 99
135 const theme = config.theme.registered.find(r => r.name === 'background-red') 100 const theme = config.theme.registered.find(r => r.name === 'background-red')
136 expect(theme).to.not.be.undefined 101 expect(theme).to.not.be.undefined
@@ -140,66 +105,56 @@ describe('Test plugins', function () {
140 }) 105 })
141 106
142 it('Should update the default theme in the configuration', async function () { 107 it('Should update the default theme in the configuration', async function () {
143 await updateCustomSubConfig(server.url, server.accessToken, { theme: { default: 'background-red' } }) 108 await server.config.updateCustomSubConfig({
144 109 newConfig: {
145 const res = await getConfig(server.url) 110 theme: { default: 'background-red' }
146 const config: ServerConfig = res.body 111 }
112 })
147 113
114 const config = await server.config.getConfig()
148 expect(config.theme.default).to.equal('background-red') 115 expect(config.theme.default).to.equal('background-red')
149 }) 116 })
150 117
151 it('Should update my default theme', async function () { 118 it('Should update my default theme', async function () {
152 await updateMyUser({ 119 await server.users.updateMe({ theme: 'background-red' })
153 url: server.url,
154 accessToken: server.accessToken,
155 theme: 'background-red'
156 })
157 120
158 const res = await getMyUserInformation(server.url, server.accessToken) 121 const user = await server.users.getMyInfo()
159 expect((res.body as User).theme).to.equal('background-red') 122 expect(user.theme).to.equal('background-red')
160 }) 123 })
161 124
162 it('Should list plugins and themes', async function () { 125 it('Should list plugins and themes', async function () {
163 { 126 {
164 const res = await listPlugins({ 127 const body = await command.list({
165 url: server.url,
166 accessToken: server.accessToken,
167 count: 1, 128 count: 1,
168 start: 0, 129 start: 0,
169 pluginType: PluginType.THEME 130 pluginType: PluginType.THEME
170 }) 131 })
171 const data: PeerTubePlugin[] = res.body.data 132 expect(body.total).to.be.at.least(1)
172 133
173 expect(res.body.total).to.be.at.least(1) 134 const data = body.data
174 expect(data).to.have.lengthOf(1) 135 expect(data).to.have.lengthOf(1)
175 expect(data[0].name).to.equal('background-red') 136 expect(data[0].name).to.equal('background-red')
176 } 137 }
177 138
178 { 139 {
179 const res = await listPlugins({ 140 const { data } = await command.list({
180 url: server.url,
181 accessToken: server.accessToken,
182 count: 2, 141 count: 2,
183 start: 0, 142 start: 0,
184 sort: 'name' 143 sort: 'name'
185 }) 144 })
186 const data: PeerTubePlugin[] = res.body.data
187 145
188 expect(data[0].name).to.equal('background-red') 146 expect(data[0].name).to.equal('background-red')
189 expect(data[1].name).to.equal('hello-world') 147 expect(data[1].name).to.equal('hello-world')
190 } 148 }
191 149
192 { 150 {
193 const res = await listPlugins({ 151 const body = await command.list({
194 url: server.url,
195 accessToken: server.accessToken,
196 count: 2, 152 count: 2,
197 start: 1, 153 start: 1,
198 sort: 'name' 154 sort: 'name'
199 }) 155 })
200 const data: PeerTubePlugin[] = res.body.data
201 156
202 expect(data[0].name).to.equal('hello-world') 157 expect(body.data[0].name).to.equal('hello-world')
203 } 158 }
204 }) 159 })
205 160
@@ -208,9 +163,8 @@ describe('Test plugins', function () {
208 }) 163 })
209 164
210 it('Should get public settings', async function () { 165 it('Should get public settings', async function () {
211 const res = await getPublicSettings({ url: server.url, npmName: 'peertube-plugin-hello-world' }) 166 const body = await command.getPublicSettings({ npmName: 'peertube-plugin-hello-world' })
212 167 const publicSettings = body.publicSettings
213 const publicSettings = (res.body as PublicServerSetting).publicSettings
214 168
215 expect(Object.keys(publicSettings)).to.have.lengthOf(1) 169 expect(Object.keys(publicSettings)).to.have.lengthOf(1)
216 expect(Object.keys(publicSettings)).to.deep.equal([ 'user-name' ]) 170 expect(Object.keys(publicSettings)).to.deep.equal([ 'user-name' ])
@@ -222,9 +176,7 @@ describe('Test plugins', function () {
222 'admin-name': 'Cid' 176 'admin-name': 'Cid'
223 } 177 }
224 178
225 await updatePluginSettings({ 179 await command.updateSettings({
226 url: server.url,
227 accessToken: server.accessToken,
228 npmName: 'peertube-plugin-hello-world', 180 npmName: 'peertube-plugin-hello-world',
229 settings 181 settings
230 }) 182 })
@@ -233,18 +185,12 @@ describe('Test plugins', function () {
233 it('Should have watched settings changes', async function () { 185 it('Should have watched settings changes', async function () {
234 this.timeout(10000) 186 this.timeout(10000)
235 187
236 await waitUntilLog(server, 'Settings changed!') 188 await server.servers.waitUntilLog('Settings changed!')
237 }) 189 })
238 190
239 it('Should get a plugin and a theme', async function () { 191 it('Should get a plugin and a theme', async function () {
240 { 192 {
241 const res = await getPlugin({ 193 const plugin = await command.get({ npmName: 'peertube-plugin-hello-world' })
242 url: server.url,
243 accessToken: server.accessToken,
244 npmName: 'peertube-plugin-hello-world'
245 })
246
247 const plugin: PeerTubePlugin = res.body
248 194
249 expect(plugin.type).to.equal(PluginType.PLUGIN) 195 expect(plugin.type).to.equal(PluginType.PLUGIN)
250 expect(plugin.name).to.equal('hello-world') 196 expect(plugin.name).to.equal('hello-world')
@@ -262,13 +208,7 @@ describe('Test plugins', function () {
262 } 208 }
263 209
264 { 210 {
265 const res = await getPlugin({ 211 const plugin = await command.get({ npmName: 'peertube-theme-background-red' })
266 url: server.url,
267 accessToken: server.accessToken,
268 npmName: 'peertube-theme-background-red'
269 })
270
271 const plugin: PeerTubePlugin = res.body
272 212
273 expect(plugin.type).to.equal(PluginType.THEME) 213 expect(plugin.type).to.equal(PluginType.THEME)
274 expect(plugin.name).to.equal('background-red') 214 expect(plugin.name).to.equal('background-red')
@@ -292,101 +232,66 @@ describe('Test plugins', function () {
292 await wait(6000) 232 await wait(6000)
293 233
294 // Fake update our plugin version 234 // Fake update our plugin version
295 await setPluginVersion(server.internalServerNumber, 'hello-world', '0.0.1') 235 await server.sql.setPluginVersion('hello-world', '0.0.1')
296 236
297 // Fake update package.json 237 // Fake update package.json
298 const packageJSON: PluginPackageJson = await getPluginPackageJSON(server, 'peertube-plugin-hello-world') 238 const packageJSON = await command.getPackageJSON('peertube-plugin-hello-world')
299 const oldVersion = packageJSON.version 239 const oldVersion = packageJSON.version
300 240
301 packageJSON.version = '0.0.1' 241 packageJSON.version = '0.0.1'
302 await updatePluginPackageJSON(server, 'peertube-plugin-hello-world', packageJSON) 242 await command.updatePackageJSON('peertube-plugin-hello-world', packageJSON)
303 243
304 // Restart the server to take into account this change 244 // Restart the server to take into account this change
305 killallServers([ server ]) 245 await killallServers([ server ])
306 await reRunServer(server) 246 await server.run()
307 247
308 { 248 {
309 const res = await listPlugins({ 249 const body = await command.list({ pluginType: PluginType.PLUGIN })
310 url: server.url,
311 accessToken: server.accessToken,
312 pluginType: PluginType.PLUGIN
313 })
314
315 const plugin: PeerTubePlugin = res.body.data[0]
316 250
251 const plugin = body.data[0]
317 expect(plugin.version).to.equal('0.0.1') 252 expect(plugin.version).to.equal('0.0.1')
318 expect(plugin.latestVersion).to.exist 253 expect(plugin.latestVersion).to.exist
319 expect(plugin.latestVersion).to.not.equal('0.0.1') 254 expect(plugin.latestVersion).to.not.equal('0.0.1')
320 } 255 }
321 256
322 { 257 {
323 await updatePlugin({ 258 await command.update({ npmName: 'peertube-plugin-hello-world' })
324 url: server.url,
325 accessToken: server.accessToken,
326 npmName: 'peertube-plugin-hello-world'
327 })
328
329 const res = await listPlugins({
330 url: server.url,
331 accessToken: server.accessToken,
332 pluginType: PluginType.PLUGIN
333 })
334 259
335 const plugin: PeerTubePlugin = res.body.data[0] 260 const body = await command.list({ pluginType: PluginType.PLUGIN })
336 261
262 const plugin = body.data[0]
337 expect(plugin.version).to.equal(oldVersion) 263 expect(plugin.version).to.equal(oldVersion)
338 264
339 const updatedPackageJSON: PluginPackageJson = await getPluginPackageJSON(server, 'peertube-plugin-hello-world') 265 const updatedPackageJSON = await command.getPackageJSON('peertube-plugin-hello-world')
340 expect(updatedPackageJSON.version).to.equal(oldVersion) 266 expect(updatedPackageJSON.version).to.equal(oldVersion)
341 } 267 }
342 }) 268 })
343 269
344 it('Should uninstall the plugin', async function () { 270 it('Should uninstall the plugin', async function () {
345 await uninstallPlugin({ 271 await command.uninstall({ npmName: 'peertube-plugin-hello-world' })
346 url: server.url,
347 accessToken: server.accessToken,
348 npmName: 'peertube-plugin-hello-world'
349 })
350
351 const res = await listPlugins({
352 url: server.url,
353 accessToken: server.accessToken,
354 pluginType: PluginType.PLUGIN
355 })
356 272
357 expect(res.body.total).to.equal(0) 273 const body = await command.list({ pluginType: PluginType.PLUGIN })
358 expect(res.body.data).to.have.lengthOf(0) 274 expect(body.total).to.equal(0)
275 expect(body.data).to.have.lengthOf(0)
359 }) 276 })
360 277
361 it('Should list uninstalled plugins', async function () { 278 it('Should list uninstalled plugins', async function () {
362 const res = await listPlugins({ 279 const body = await command.list({ pluginType: PluginType.PLUGIN, uninstalled: true })
363 url: server.url, 280 expect(body.total).to.equal(1)
364 accessToken: server.accessToken, 281 expect(body.data).to.have.lengthOf(1)
365 pluginType: PluginType.PLUGIN,
366 uninstalled: true
367 })
368
369 expect(res.body.total).to.equal(1)
370 expect(res.body.data).to.have.lengthOf(1)
371 282
372 const plugin: PeerTubePlugin = res.body.data[0] 283 const plugin = body.data[0]
373 expect(plugin.name).to.equal('hello-world') 284 expect(plugin.name).to.equal('hello-world')
374 expect(plugin.enabled).to.be.false 285 expect(plugin.enabled).to.be.false
375 expect(plugin.uninstalled).to.be.true 286 expect(plugin.uninstalled).to.be.true
376 }) 287 })
377 288
378 it('Should uninstall the theme', async function () { 289 it('Should uninstall the theme', async function () {
379 await uninstallPlugin({ 290 await command.uninstall({ npmName: 'peertube-theme-background-red' })
380 url: server.url,
381 accessToken: server.accessToken,
382 npmName: 'peertube-theme-background-red'
383 })
384 }) 291 })
385 292
386 it('Should have updated the configuration', async function () { 293 it('Should have updated the configuration', async function () {
387 // get /config (default theme + registered themes + registered plugins) 294 const config = await server.config.getConfig()
388 const res = await getConfig(server.url)
389 const config: ServerConfig = res.body
390 295
391 expect(config.theme.default).to.equal('default') 296 expect(config.theme.default).to.equal('default')
392 297
@@ -398,42 +303,33 @@ describe('Test plugins', function () {
398 }) 303 })
399 304
400 it('Should have updated the user theme', async function () { 305 it('Should have updated the user theme', async function () {
401 const res = await getMyUserInformation(server.url, server.accessToken) 306 const user = await server.users.getMyInfo()
402 expect((res.body as User).theme).to.equal('instance-default') 307 expect(user.theme).to.equal('instance-default')
403 }) 308 })
404 309
405 it('Should not install a broken plugin', async function () { 310 it('Should not install a broken plugin', async function () {
406 this.timeout(60000) 311 this.timeout(60000)
407 312
408 async function check () { 313 async function check () {
409 const res = await listPlugins({ 314 const body = await command.list({ pluginType: PluginType.PLUGIN })
410 url: server.url, 315 const plugins = body.data
411 accessToken: server.accessToken,
412 pluginType: PluginType.PLUGIN
413 })
414
415 const plugins: PeerTubePlugin[] = res.body.data
416
417 expect(plugins.find(p => p.name === 'test-broken')).to.not.exist 316 expect(plugins.find(p => p.name === 'test-broken')).to.not.exist
418 } 317 }
419 318
420 await installPlugin({ 319 await command.install({
421 url: server.url, 320 path: PluginsCommand.getPluginTestPath('-broken'),
422 accessToken: server.accessToken,
423 path: getPluginTestPath('-broken'),
424 expectedStatus: HttpStatusCode.BAD_REQUEST_400 321 expectedStatus: HttpStatusCode.BAD_REQUEST_400
425 }) 322 })
426 323
427 await check() 324 await check()
428 325
429 killallServers([ server ]) 326 await killallServers([ server ])
430 await reRunServer(server) 327 await server.run()
431 328
432 await check() 329 await check()
433 }) 330 })
434 331
435 after(async function () { 332 after(async function () {
436 await closeAllSequelize([ server ])
437 await cleanupTests([ server ]) 333 await cleanupTests([ server ])
438 }) 334 })
439}) 335})
diff --git a/server/tests/api/server/reverse-proxy.ts b/server/tests/api/server/reverse-proxy.ts
index 17d1ee4a5..484f88d67 100644
--- a/server/tests/api/server/reverse-proxy.ts
+++ b/server/tests/api/server/reverse-proxy.ts
@@ -1,16 +1,12 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import { expect } from 'chai'
4import * as chai from 'chai' 4import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers, wait } from '@shared/extra-utils'
5import { cleanupTests, getVideo, registerUser, uploadVideo, userLogin, viewVideo, wait } from '../../../../shared/extra-utils' 5import { HttpStatusCode } from '@shared/models'
6import { flushAndRunServer, setAccessTokensToServers } from '../../../../shared/extra-utils/index'
7import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
8
9const expect = chai.expect
10 6
11describe('Test application behind a reverse proxy', function () { 7describe('Test application behind a reverse proxy', function () {
12 let server = null 8 let server: PeerTubeServer
13 let videoId 9 let videoId: string
14 10
15 before(async function () { 11 before(async function () {
16 this.timeout(30000) 12 this.timeout(30000)
@@ -34,85 +30,85 @@ describe('Test application behind a reverse proxy', function () {
34 } 30 }
35 } 31 }
36 32
37 server = await flushAndRunServer(1, config) 33 server = await createSingleServer(1, config)
38 await setAccessTokensToServers([ server ]) 34 await setAccessTokensToServers([ server ])
39 35
40 const { body } = await uploadVideo(server.url, server.accessToken, {}) 36 const { uuid } = await server.videos.upload()
41 videoId = body.video.uuid 37 videoId = uuid
42 }) 38 })
43 39
44 it('Should view a video only once with the same IP by default', async function () { 40 it('Should view a video only once with the same IP by default', async function () {
45 this.timeout(20000) 41 this.timeout(20000)
46 42
47 await viewVideo(server.url, videoId) 43 await server.videos.view({ id: videoId })
48 await viewVideo(server.url, videoId) 44 await server.videos.view({ id: videoId })
49 45
50 // Wait the repeatable job 46 // Wait the repeatable job
51 await wait(8000) 47 await wait(8000)
52 48
53 const { body } = await getVideo(server.url, videoId) 49 const video = await server.videos.get({ id: videoId })
54 expect(body.views).to.equal(1) 50 expect(video.views).to.equal(1)
55 }) 51 })
56 52
57 it('Should view a video 2 times with the X-Forwarded-For header set', async function () { 53 it('Should view a video 2 times with the X-Forwarded-For header set', async function () {
58 this.timeout(20000) 54 this.timeout(20000)
59 55
60 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.1,127.0.0.1') 56 await server.videos.view({ id: videoId, xForwardedFor: '0.0.0.1,127.0.0.1' })
61 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.2,127.0.0.1') 57 await server.videos.view({ id: videoId, xForwardedFor: '0.0.0.2,127.0.0.1' })
62 58
63 // Wait the repeatable job 59 // Wait the repeatable job
64 await wait(8000) 60 await wait(8000)
65 61
66 const { body } = await getVideo(server.url, videoId) 62 const video = await server.videos.get({ id: videoId })
67 expect(body.views).to.equal(3) 63 expect(video.views).to.equal(3)
68 }) 64 })
69 65
70 it('Should view a video only once with the same client IP in the X-Forwarded-For header', async function () { 66 it('Should view a video only once with the same client IP in the X-Forwarded-For header', async function () {
71 this.timeout(20000) 67 this.timeout(20000)
72 68
73 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.4,0.0.0.3,::ffff:127.0.0.1') 69 await server.videos.view({ id: videoId, xForwardedFor: '0.0.0.4,0.0.0.3,::ffff:127.0.0.1' })
74 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.5,0.0.0.3,127.0.0.1') 70 await server.videos.view({ id: videoId, xForwardedFor: '0.0.0.5,0.0.0.3,127.0.0.1' })
75 71
76 // Wait the repeatable job 72 // Wait the repeatable job
77 await wait(8000) 73 await wait(8000)
78 74
79 const { body } = await getVideo(server.url, videoId) 75 const video = await server.videos.get({ id: videoId })
80 expect(body.views).to.equal(4) 76 expect(video.views).to.equal(4)
81 }) 77 })
82 78
83 it('Should view a video two times with a different client IP in the X-Forwarded-For header', async function () { 79 it('Should view a video two times with a different client IP in the X-Forwarded-For header', async function () {
84 this.timeout(20000) 80 this.timeout(20000)
85 81
86 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.8,0.0.0.6,127.0.0.1') 82 await server.videos.view({ id: videoId, xForwardedFor: '0.0.0.8,0.0.0.6,127.0.0.1' })
87 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.8,0.0.0.7,127.0.0.1') 83 await server.videos.view({ id: videoId, xForwardedFor: '0.0.0.8,0.0.0.7,127.0.0.1' })
88 84
89 // Wait the repeatable job 85 // Wait the repeatable job
90 await wait(8000) 86 await wait(8000)
91 87
92 const { body } = await getVideo(server.url, videoId) 88 const video = await server.videos.get({ id: videoId })
93 expect(body.views).to.equal(6) 89 expect(video.views).to.equal(6)
94 }) 90 })
95 91
96 it('Should rate limit logins', async function () { 92 it('Should rate limit logins', async function () {
97 const user = { username: 'root', password: 'fail' } 93 const user = { username: 'root', password: 'fail' }
98 94
99 for (let i = 0; i < 19; i++) { 95 for (let i = 0; i < 19; i++) {
100 await userLogin(server, user, HttpStatusCode.BAD_REQUEST_400) 96 await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
101 } 97 }
102 98
103 await userLogin(server, user, HttpStatusCode.TOO_MANY_REQUESTS_429) 99 await server.login.login({ user, expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
104 }) 100 })
105 101
106 it('Should rate limit signup', async function () { 102 it('Should rate limit signup', async function () {
107 for (let i = 0; i < 10; i++) { 103 for (let i = 0; i < 10; i++) {
108 try { 104 try {
109 await registerUser(server.url, 'test' + i, 'password') 105 await server.users.register({ username: 'test' + i })
110 } catch { 106 } catch {
111 // empty 107 // empty
112 } 108 }
113 } 109 }
114 110
115 await registerUser(server.url, 'test42', 'password', HttpStatusCode.TOO_MANY_REQUESTS_429) 111 await server.users.register({ username: 'test42', expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
116 }) 112 })
117 113
118 it('Should not rate limit failed signup', async function () { 114 it('Should not rate limit failed signup', async function () {
@@ -121,10 +117,10 @@ describe('Test application behind a reverse proxy', function () {
121 await wait(7000) 117 await wait(7000)
122 118
123 for (let i = 0; i < 3; i++) { 119 for (let i = 0; i < 3; i++) {
124 await registerUser(server.url, 'test' + i, 'password', HttpStatusCode.CONFLICT_409) 120 await server.users.register({ username: 'test' + i, expectedStatus: HttpStatusCode.CONFLICT_409 })
125 } 121 }
126 122
127 await registerUser(server.url, 'test43', 'password', HttpStatusCode.NO_CONTENT_204) 123 await server.users.register({ username: 'test43', expectedStatus: HttpStatusCode.NO_CONTENT_204 })
128 124
129 }) 125 })
130 126
@@ -135,13 +131,13 @@ describe('Test application behind a reverse proxy', function () {
135 131
136 for (let i = 0; i < 100; i++) { 132 for (let i = 0; i < 100; i++) {
137 try { 133 try {
138 await getVideo(server.url, videoId) 134 await server.videos.get({ id: videoId })
139 } catch { 135 } catch {
140 // don't care if it fails 136 // don't care if it fails
141 } 137 }
142 } 138 }
143 139
144 await getVideo(server.url, videoId, HttpStatusCode.TOO_MANY_REQUESTS_429) 140 await server.videos.get({ id: videoId, expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
145 }) 141 })
146 142
147 after(async function () { 143 after(async function () {
diff --git a/server/tests/api/server/services.ts b/server/tests/api/server/services.ts
index ea64e4040..69d030dbb 100644
--- a/server/tests/api/server/services.ts
+++ b/server/tests/api/server/services.ts
@@ -2,23 +2,13 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers, setDefaultVideoChannel } from '@shared/extra-utils'
5import { Video, VideoPlaylistPrivacy } from '@shared/models' 6import { Video, VideoPlaylistPrivacy } from '@shared/models'
6import {
7 addVideoInPlaylist,
8 createVideoPlaylist,
9 getOEmbed,
10 getVideosList,
11 ServerInfo,
12 setAccessTokensToServers,
13 setDefaultVideoChannel,
14 uploadVideo
15} from '../../../../shared/extra-utils'
16import { cleanupTests, flushAndRunServer } from '../../../../shared/extra-utils/server/servers'
17 7
18const expect = chai.expect 8const expect = chai.expect
19 9
20describe('Test services', function () { 10describe('Test services', function () {
21 let server: ServerInfo = null 11 let server: PeerTubeServer = null
22 let playlistUUID: string 12 let playlistUUID: string
23 let playlistDisplayName: string 13 let playlistDisplayName: string
24 let video: Video 14 let video: Video
@@ -26,40 +16,34 @@ describe('Test services', function () {
26 before(async function () { 16 before(async function () {
27 this.timeout(30000) 17 this.timeout(30000)
28 18
29 server = await flushAndRunServer(1) 19 server = await createSingleServer(1)
30 20
31 await setAccessTokensToServers([ server ]) 21 await setAccessTokensToServers([ server ])
32 await setDefaultVideoChannel([ server ]) 22 await setDefaultVideoChannel([ server ])
33 23
34 { 24 {
35 const videoAttributes = { 25 const attributes = { name: 'my super name' }
36 name: 'my super name' 26 await server.videos.upload({ attributes })
37 }
38 await uploadVideo(server.url, server.accessToken, videoAttributes)
39 27
40 const res = await getVideosList(server.url) 28 const { data } = await server.videos.list()
41 video = res.body.data[0] 29 video = data[0]
42 } 30 }
43 31
44 { 32 {
45 const res = await createVideoPlaylist({ 33 const created = await server.playlists.create({
46 url: server.url, 34 attributes: {
47 token: server.accessToken,
48 playlistAttrs: {
49 displayName: 'The Life and Times of Scrooge McDuck', 35 displayName: 'The Life and Times of Scrooge McDuck',
50 privacy: VideoPlaylistPrivacy.PUBLIC, 36 privacy: VideoPlaylistPrivacy.PUBLIC,
51 videoChannelId: server.videoChannel.id 37 videoChannelId: server.store.channel.id
52 } 38 }
53 }) 39 })
54 40
55 playlistUUID = res.body.videoPlaylist.uuid 41 playlistUUID = created.uuid
56 playlistDisplayName = 'The Life and Times of Scrooge McDuck' 42 playlistDisplayName = 'The Life and Times of Scrooge McDuck'
57 43
58 await addVideoInPlaylist({ 44 await server.playlists.addElement({
59 url: server.url, 45 playlistId: created.id,
60 token: server.accessToken, 46 attributes: {
61 playlistId: res.body.videoPlaylist.id,
62 elementAttrs: {
63 videoId: video.id 47 videoId: video.id
64 } 48 }
65 }) 49 })
@@ -70,7 +54,7 @@ describe('Test services', function () {
70 for (const basePath of [ '/videos/watch/', '/w/' ]) { 54 for (const basePath of [ '/videos/watch/', '/w/' ]) {
71 const oembedUrl = 'http://localhost:' + server.port + basePath + video.uuid 55 const oembedUrl = 'http://localhost:' + server.port + basePath + video.uuid
72 56
73 const res = await getOEmbed(server.url, oembedUrl) 57 const res = await server.services.getOEmbed({ oembedUrl })
74 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' + 58 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' +
75 `title="${video.name}" src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` + 59 `title="${video.name}" src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` +
76 'frameborder="0" allowfullscreen></iframe>' 60 'frameborder="0" allowfullscreen></iframe>'
@@ -78,7 +62,7 @@ describe('Test services', function () {
78 62
79 expect(res.body.html).to.equal(expectedHtml) 63 expect(res.body.html).to.equal(expectedHtml)
80 expect(res.body.title).to.equal(video.name) 64 expect(res.body.title).to.equal(video.name)
81 expect(res.body.author_name).to.equal(server.videoChannel.displayName) 65 expect(res.body.author_name).to.equal(server.store.channel.displayName)
82 expect(res.body.width).to.equal(560) 66 expect(res.body.width).to.equal(560)
83 expect(res.body.height).to.equal(315) 67 expect(res.body.height).to.equal(315)
84 expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl) 68 expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl)
@@ -91,14 +75,14 @@ describe('Test services', function () {
91 for (const basePath of [ '/videos/watch/playlist/', '/w/p/' ]) { 75 for (const basePath of [ '/videos/watch/playlist/', '/w/p/' ]) {
92 const oembedUrl = 'http://localhost:' + server.port + basePath + playlistUUID 76 const oembedUrl = 'http://localhost:' + server.port + basePath + playlistUUID
93 77
94 const res = await getOEmbed(server.url, oembedUrl) 78 const res = await server.services.getOEmbed({ oembedUrl })
95 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' + 79 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' +
96 `title="${playlistDisplayName}" src="http://localhost:${server.port}/video-playlists/embed/${playlistUUID}" ` + 80 `title="${playlistDisplayName}" src="http://localhost:${server.port}/video-playlists/embed/${playlistUUID}" ` +
97 'frameborder="0" allowfullscreen></iframe>' 81 'frameborder="0" allowfullscreen></iframe>'
98 82
99 expect(res.body.html).to.equal(expectedHtml) 83 expect(res.body.html).to.equal(expectedHtml)
100 expect(res.body.title).to.equal('The Life and Times of Scrooge McDuck') 84 expect(res.body.title).to.equal('The Life and Times of Scrooge McDuck')
101 expect(res.body.author_name).to.equal(server.videoChannel.displayName) 85 expect(res.body.author_name).to.equal(server.store.channel.displayName)
102 expect(res.body.width).to.equal(560) 86 expect(res.body.width).to.equal(560)
103 expect(res.body.height).to.equal(315) 87 expect(res.body.height).to.equal(315)
104 expect(res.body.thumbnail_url).exist 88 expect(res.body.thumbnail_url).exist
@@ -114,14 +98,14 @@ describe('Test services', function () {
114 const maxHeight = 50 98 const maxHeight = 50
115 const maxWidth = 50 99 const maxWidth = 50
116 100
117 const res = await getOEmbed(server.url, oembedUrl, format, maxHeight, maxWidth) 101 const res = await server.services.getOEmbed({ oembedUrl, format, maxHeight, maxWidth })
118 const expectedHtml = '<iframe width="50" height="50" sandbox="allow-same-origin allow-scripts" ' + 102 const expectedHtml = '<iframe width="50" height="50" sandbox="allow-same-origin allow-scripts" ' +
119 `title="${video.name}" src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` + 103 `title="${video.name}" src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` +
120 'frameborder="0" allowfullscreen></iframe>' 104 'frameborder="0" allowfullscreen></iframe>'
121 105
122 expect(res.body.html).to.equal(expectedHtml) 106 expect(res.body.html).to.equal(expectedHtml)
123 expect(res.body.title).to.equal(video.name) 107 expect(res.body.title).to.equal(video.name)
124 expect(res.body.author_name).to.equal(server.videoChannel.displayName) 108 expect(res.body.author_name).to.equal(server.store.channel.displayName)
125 expect(res.body.height).to.equal(50) 109 expect(res.body.height).to.equal(50)
126 expect(res.body.width).to.equal(50) 110 expect(res.body.width).to.equal(50)
127 expect(res.body).to.not.have.property('thumbnail_url') 111 expect(res.body).to.not.have.property('thumbnail_url')
diff --git a/server/tests/api/server/stats.ts b/server/tests/api/server/stats.ts
index 304181a6d..5ec771429 100644
--- a/server/tests/api/server/stats.ts
+++ b/server/tests/api/server/stats.ts
@@ -3,33 +3,20 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 addVideoChannel,
7 cleanupTests, 6 cleanupTests,
8 createUser, 7 createMultipleServers,
9 createVideoPlaylist,
10 doubleFollow, 8 doubleFollow,
11 flushAndRunMultipleServers, 9 PeerTubeServer,
12 follow, 10 setAccessTokensToServers,
13 ServerInfo, 11 wait,
14 unfollow, 12 waitJobs
15 updateCustomSubConfig, 13} from '@shared/extra-utils'
16 uploadVideo, 14import { ActivityType, VideoPlaylistPrivacy } from '@shared/models'
17 userLogin,
18 viewVideo,
19 wait
20} from '../../../../shared/extra-utils'
21import { setAccessTokensToServers } from '../../../../shared/extra-utils/index'
22import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
23import { getStats } from '../../../../shared/extra-utils/server/stats'
24import { addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments'
25import { ServerStats } from '../../../../shared/models/server/server-stats.model'
26import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
27import { ActivityType } from '@shared/models'
28 15
29const expect = chai.expect 16const expect = chai.expect
30 17
31describe('Test stats (excluding redundancy)', function () { 18describe('Test stats (excluding redundancy)', function () {
32 let servers: ServerInfo[] = [] 19 let servers: PeerTubeServer[] = []
33 let channelId 20 let channelId
34 const user = { 21 const user = {
35 username: 'user1', 22 username: 'user1',
@@ -39,31 +26,29 @@ describe('Test stats (excluding redundancy)', function () {
39 before(async function () { 26 before(async function () {
40 this.timeout(60000) 27 this.timeout(60000)
41 28
42 servers = await flushAndRunMultipleServers(3) 29 servers = await createMultipleServers(3)
43 30
44 await setAccessTokensToServers(servers) 31 await setAccessTokensToServers(servers)
45 32
46 await doubleFollow(servers[0], servers[1]) 33 await doubleFollow(servers[0], servers[1])
47 34
48 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 35 await servers[0].users.create({ username: user.username, password: user.password })
49 36
50 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { fixture: 'video_short.webm' }) 37 const { uuid } = await servers[0].videos.upload({ attributes: { fixture: 'video_short.webm' } })
51 const videoUUID = resVideo.body.video.uuid
52 38
53 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'comment') 39 await servers[0].comments.createThread({ videoId: uuid, text: 'comment' })
54 40
55 await viewVideo(servers[0].url, videoUUID) 41 await servers[0].videos.view({ id: uuid })
56 42
57 // Wait the video views repeatable job 43 // Wait the video views repeatable job
58 await wait(8000) 44 await wait(8000)
59 45
60 await follow(servers[2].url, [ servers[0].url ], servers[2].accessToken) 46 await servers[2].follows.follow({ hosts: [ servers[0].url ] })
61 await waitJobs(servers) 47 await waitJobs(servers)
62 }) 48 })
63 49
64 it('Should have the correct stats on instance 1', async function () { 50 it('Should have the correct stats on instance 1', async function () {
65 const res = await getStats(servers[0].url) 51 const data = await servers[0].stats.get()
66 const data: ServerStats = res.body
67 52
68 expect(data.totalLocalVideoComments).to.equal(1) 53 expect(data.totalLocalVideoComments).to.equal(1)
69 expect(data.totalLocalVideos).to.equal(1) 54 expect(data.totalLocalVideos).to.equal(1)
@@ -78,8 +63,7 @@ describe('Test stats (excluding redundancy)', function () {
78 }) 63 })
79 64
80 it('Should have the correct stats on instance 2', async function () { 65 it('Should have the correct stats on instance 2', async function () {
81 const res = await getStats(servers[1].url) 66 const data = await servers[1].stats.get()
82 const data: ServerStats = res.body
83 67
84 expect(data.totalLocalVideoComments).to.equal(0) 68 expect(data.totalLocalVideoComments).to.equal(0)
85 expect(data.totalLocalVideos).to.equal(0) 69 expect(data.totalLocalVideos).to.equal(0)
@@ -94,8 +78,7 @@ describe('Test stats (excluding redundancy)', function () {
94 }) 78 })
95 79
96 it('Should have the correct stats on instance 3', async function () { 80 it('Should have the correct stats on instance 3', async function () {
97 const res = await getStats(servers[2].url) 81 const data = await servers[2].stats.get()
98 const data: ServerStats = res.body
99 82
100 expect(data.totalLocalVideoComments).to.equal(0) 83 expect(data.totalLocalVideoComments).to.equal(0)
101 expect(data.totalLocalVideos).to.equal(0) 84 expect(data.totalLocalVideos).to.equal(0)
@@ -111,11 +94,10 @@ describe('Test stats (excluding redundancy)', function () {
111 it('Should have the correct total videos stats after an unfollow', async function () { 94 it('Should have the correct total videos stats after an unfollow', async function () {
112 this.timeout(15000) 95 this.timeout(15000)
113 96
114 await unfollow(servers[2].url, servers[2].accessToken, servers[0]) 97 await servers[2].follows.unfollow({ target: servers[0] })
115 await waitJobs(servers) 98 await waitJobs(servers)
116 99
117 const res = await getStats(servers[2].url) 100 const data = await servers[2].stats.get()
118 const data: ServerStats = res.body
119 101
120 expect(data.totalVideos).to.equal(0) 102 expect(data.totalVideos).to.equal(0)
121 }) 103 })
@@ -124,18 +106,18 @@ describe('Test stats (excluding redundancy)', function () {
124 const server = servers[0] 106 const server = servers[0]
125 107
126 { 108 {
127 const res = await getStats(server.url) 109 const data = await server.stats.get()
128 const data: ServerStats = res.body 110
129 expect(data.totalDailyActiveUsers).to.equal(1) 111 expect(data.totalDailyActiveUsers).to.equal(1)
130 expect(data.totalWeeklyActiveUsers).to.equal(1) 112 expect(data.totalWeeklyActiveUsers).to.equal(1)
131 expect(data.totalMonthlyActiveUsers).to.equal(1) 113 expect(data.totalMonthlyActiveUsers).to.equal(1)
132 } 114 }
133 115
134 { 116 {
135 await userLogin(server, user) 117 await server.login.getAccessToken(user)
118
119 const data = await server.stats.get()
136 120
137 const res = await getStats(server.url)
138 const data: ServerStats = res.body
139 expect(data.totalDailyActiveUsers).to.equal(2) 121 expect(data.totalDailyActiveUsers).to.equal(2)
140 expect(data.totalWeeklyActiveUsers).to.equal(2) 122 expect(data.totalWeeklyActiveUsers).to.equal(2)
141 expect(data.totalMonthlyActiveUsers).to.equal(2) 123 expect(data.totalMonthlyActiveUsers).to.equal(2)
@@ -146,33 +128,33 @@ describe('Test stats (excluding redundancy)', function () {
146 const server = servers[0] 128 const server = servers[0]
147 129
148 { 130 {
149 const res = await getStats(server.url) 131 const data = await server.stats.get()
150 const data: ServerStats = res.body 132
151 expect(data.totalLocalDailyActiveVideoChannels).to.equal(1) 133 expect(data.totalLocalDailyActiveVideoChannels).to.equal(1)
152 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(1) 134 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(1)
153 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(1) 135 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(1)
154 } 136 }
155 137
156 { 138 {
157 const channelAttributes = { 139 const attributes = {
158 name: 'stats_channel', 140 name: 'stats_channel',
159 displayName: 'My stats channel' 141 displayName: 'My stats channel'
160 } 142 }
161 const resChannel = await addVideoChannel(server.url, server.accessToken, channelAttributes) 143 const created = await server.channels.create({ attributes })
162 channelId = resChannel.body.videoChannel.id 144 channelId = created.id
145
146 const data = await server.stats.get()
163 147
164 const res = await getStats(server.url)
165 const data: ServerStats = res.body
166 expect(data.totalLocalDailyActiveVideoChannels).to.equal(1) 148 expect(data.totalLocalDailyActiveVideoChannels).to.equal(1)
167 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(1) 149 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(1)
168 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(1) 150 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(1)
169 } 151 }
170 152
171 { 153 {
172 await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.webm', channelId }) 154 await server.videos.upload({ attributes: { fixture: 'video_short.webm', channelId } })
155
156 const data = await server.stats.get()
173 157
174 const res = await getStats(server.url)
175 const data: ServerStats = res.body
176 expect(data.totalLocalDailyActiveVideoChannels).to.equal(2) 158 expect(data.totalLocalDailyActiveVideoChannels).to.equal(2)
177 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(2) 159 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(2)
178 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(2) 160 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(2)
@@ -183,66 +165,62 @@ describe('Test stats (excluding redundancy)', function () {
183 const server = servers[0] 165 const server = servers[0]
184 166
185 { 167 {
186 const resStats = await getStats(server.url) 168 const data = await server.stats.get()
187 const dataStats: ServerStats = resStats.body 169 expect(data.totalLocalPlaylists).to.equal(0)
188 expect(dataStats.totalLocalPlaylists).to.equal(0)
189 } 170 }
190 171
191 { 172 {
192 await createVideoPlaylist({ 173 await server.playlists.create({
193 url: server.url, 174 attributes: {
194 token: server.accessToken,
195 playlistAttrs: {
196 displayName: 'playlist for count', 175 displayName: 'playlist for count',
197 privacy: VideoPlaylistPrivacy.PUBLIC, 176 privacy: VideoPlaylistPrivacy.PUBLIC,
198 videoChannelId: channelId 177 videoChannelId: channelId
199 } 178 }
200 }) 179 })
201 180
202 const resStats = await getStats(server.url) 181 const data = await server.stats.get()
203 const dataStats: ServerStats = resStats.body 182 expect(data.totalLocalPlaylists).to.equal(1)
204 expect(dataStats.totalLocalPlaylists).to.equal(1)
205 } 183 }
206 }) 184 })
207 185
208 it('Should correctly count video file sizes if transcoding is enabled', async function () { 186 it('Should correctly count video file sizes if transcoding is enabled', async function () {
209 this.timeout(60000) 187 this.timeout(60000)
210 188
211 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 189 await servers[0].config.updateCustomSubConfig({
212 transcoding: { 190 newConfig: {
213 enabled: true, 191 transcoding: {
214 webtorrent: { 192 enabled: true,
215 enabled: true 193 webtorrent: {
216 }, 194 enabled: true
217 hls: { 195 },
218 enabled: true 196 hls: {
219 }, 197 enabled: true
220 resolutions: { 198 },
221 '0p': false, 199 resolutions: {
222 '240p': false, 200 '0p': false,
223 '360p': false, 201 '240p': false,
224 '480p': false, 202 '360p': false,
225 '720p': false, 203 '480p': false,
226 '1080p': false, 204 '720p': false,
227 '1440p': false, 205 '1080p': false,
228 '2160p': false 206 '1440p': false,
207 '2160p': false
208 }
229 } 209 }
230 } 210 }
231 }) 211 })
232 212
233 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video', fixture: 'video_short.webm' }) 213 await servers[0].videos.upload({ attributes: { name: 'video', fixture: 'video_short.webm' } })
234 214
235 await waitJobs(servers) 215 await waitJobs(servers)
236 216
237 { 217 {
238 const res = await getStats(servers[1].url) 218 const data = await servers[1].stats.get()
239 const data: ServerStats = res.body
240 expect(data.totalLocalVideoFilesSize).to.equal(0) 219 expect(data.totalLocalVideoFilesSize).to.equal(0)
241 } 220 }
242 221
243 { 222 {
244 const res = await getStats(servers[0].url) 223 const data = await servers[0].stats.get()
245 const data: ServerStats = res.body
246 expect(data.totalLocalVideoFilesSize).to.be.greaterThan(500000) 224 expect(data.totalLocalVideoFilesSize).to.be.greaterThan(500000)
247 expect(data.totalLocalVideoFilesSize).to.be.lessThan(600000) 225 expect(data.totalLocalVideoFilesSize).to.be.lessThan(600000)
248 } 226 }
@@ -251,27 +229,27 @@ describe('Test stats (excluding redundancy)', function () {
251 it('Should have the correct AP stats', async function () { 229 it('Should have the correct AP stats', async function () {
252 this.timeout(60000) 230 this.timeout(60000)
253 231
254 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 232 await servers[0].config.updateCustomSubConfig({
255 transcoding: { 233 newConfig: {
256 enabled: false 234 transcoding: {
235 enabled: false
236 }
257 } 237 }
258 }) 238 })
259 239
260 const res1 = await getStats(servers[1].url) 240 const first = await servers[1].stats.get()
261 const first = res1.body as ServerStats
262 241
263 for (let i = 0; i < 10; i++) { 242 for (let i = 0; i < 10; i++) {
264 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' }) 243 await servers[0].videos.upload({ attributes: { name: 'video' } })
265 } 244 }
266 245
267 await waitJobs(servers) 246 await waitJobs(servers)
268 247
269 await wait(6000) 248 await wait(6000)
270 249
271 const res2 = await getStats(servers[1].url) 250 const second = await servers[1].stats.get()
272 const second: ServerStats = res2.body
273
274 expect(second.totalActivityPubMessagesProcessed).to.be.greaterThan(first.totalActivityPubMessagesProcessed) 251 expect(second.totalActivityPubMessagesProcessed).to.be.greaterThan(first.totalActivityPubMessagesProcessed)
252
275 const apTypes: ActivityType[] = [ 253 const apTypes: ActivityType[] = [
276 'Create', 'Update', 'Delete', 'Follow', 'Accept', 'Announce', 'Undo', 'Like', 'Reject', 'View', 'Dislike', 'Flag' 254 'Create', 'Update', 'Delete', 'Follow', 'Accept', 'Announce', 'Undo', 'Like', 'Reject', 'View', 'Dislike', 'Flag'
277 ] 255 ]
@@ -291,9 +269,7 @@ describe('Test stats (excluding redundancy)', function () {
291 269
292 await wait(6000) 270 await wait(6000)
293 271
294 const res3 = await getStats(servers[1].url) 272 const third = await servers[1].stats.get()
295 const third: ServerStats = res3.body
296
297 expect(third.totalActivityPubMessagesWaiting).to.equal(0) 273 expect(third.totalActivityPubMessagesWaiting).to.equal(0)
298 expect(third.activityPubMessagesProcessedPerSecond).to.be.lessThan(second.activityPubMessagesProcessedPerSecond) 274 expect(third.activityPubMessagesProcessedPerSecond).to.be.lessThan(second.activityPubMessagesProcessedPerSecond)
299 }) 275 })
diff --git a/server/tests/api/server/tracker.ts b/server/tests/api/server/tracker.ts
index 4b86e0b90..f597ac60c 100644
--- a/server/tests/api/server/tracker.ts
+++ b/server/tests/api/server/tracker.ts
@@ -1,36 +1,23 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await,@typescript-eslint/no-floating-promises */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await,@typescript-eslint/no-floating-promises */
2 2
3import * as magnetUtil from 'magnet-uri'
4import 'mocha' 3import 'mocha'
5import { 4import * as magnetUtil from 'magnet-uri'
6 cleanupTests,
7 flushAndRunServer,
8 getVideo,
9 killallServers,
10 reRunServer,
11 ServerInfo,
12 uploadVideo
13} from '../../../../shared/extra-utils'
14import { setAccessTokensToServers } from '../../../../shared/extra-utils/index'
15import { VideoDetails } from '../../../../shared/models/videos'
16import * as WebTorrent from 'webtorrent' 5import * as WebTorrent from 'webtorrent'
6import { cleanupTests, createSingleServer, killallServers, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
17 7
18describe('Test tracker', function () { 8describe('Test tracker', function () {
19 let server: ServerInfo 9 let server: PeerTubeServer
20 let badMagnet: string 10 let badMagnet: string
21 let goodMagnet: string 11 let goodMagnet: string
22 12
23 before(async function () { 13 before(async function () {
24 this.timeout(60000) 14 this.timeout(60000)
25 server = await flushAndRunServer(1) 15 server = await createSingleServer(1)
26 await setAccessTokensToServers([ server ]) 16 await setAccessTokensToServers([ server ])
27 17
28 { 18 {
29 const res = await uploadVideo(server.url, server.accessToken, {}) 19 const { uuid } = await server.videos.upload()
30 const videoUUID = res.body.video.uuid 20 const video = await server.videos.get({ id: uuid })
31
32 const resGet = await getVideo(server.url, videoUUID)
33 const video: VideoDetails = resGet.body
34 goodMagnet = video.files[0].magnetUri 21 goodMagnet = video.files[0].magnetUri
35 22
36 const parsed = magnetUtil.decode(goodMagnet) 23 const parsed = magnetUtil.decode(goodMagnet)
@@ -61,8 +48,7 @@ describe('Test tracker', function () {
61 const errCb = () => done(new Error('Tracker is enabled')) 48 const errCb = () => done(new Error('Tracker is enabled'))
62 49
63 killallServers([ server ]) 50 killallServers([ server ])
64 51 .then(() => server.run({ tracker: { enabled: false } }))
65 reRunServer(server, { tracker: { enabled: false } })
66 .then(() => { 52 .then(() => {
67 const webtorrent = new WebTorrent() 53 const webtorrent = new WebTorrent()
68 54
@@ -86,8 +72,7 @@ describe('Test tracker', function () {
86 this.timeout(20000) 72 this.timeout(20000)
87 73
88 killallServers([ server ]) 74 killallServers([ server ])
89 75 .then(() => server.run())
90 reRunServer(server)
91 .then(() => { 76 .then(() => {
92 const webtorrent = new WebTorrent() 77 const webtorrent = new WebTorrent()
93 78
diff --git a/server/tests/api/users/user-subscriptions.ts b/server/tests/api/users/user-subscriptions.ts
index 60676a37b..77b99886d 100644
--- a/server/tests/api/users/user-subscriptions.ts
+++ b/server/tests/api/users/user-subscriptions.ts
@@ -1,42 +1,30 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 createUser, 7 createMultipleServers,
8 doubleFollow, 8 doubleFollow,
9 flushAndRunMultipleServers, 9 PeerTubeServer,
10 follow, 10 setAccessTokensToServers,
11 getVideosList, 11 SubscriptionsCommand,
12 unfollow, 12 waitJobs
13 updateVideo, 13} from '@shared/extra-utils'
14 userLogin
15} from '../../../../shared/extra-utils'
16import { ServerInfo, uploadVideo } from '../../../../shared/extra-utils/index'
17import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
18import { Video, VideoChannel } from '../../../../shared/models/videos'
19import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
20import {
21 addUserSubscription,
22 areSubscriptionsExist,
23 getUserSubscription,
24 listUserSubscriptions,
25 listUserSubscriptionVideos,
26 removeUserSubscription
27} from '../../../../shared/extra-utils/users/user-subscriptions'
28 14
29const expect = chai.expect 15const expect = chai.expect
30 16
31describe('Test users subscriptions', function () { 17describe('Test users subscriptions', function () {
32 let servers: ServerInfo[] = [] 18 let servers: PeerTubeServer[] = []
33 const users: { accessToken: string }[] = [] 19 const users: { accessToken: string }[] = []
34 let video3UUID: string 20 let video3UUID: string
35 21
22 let command: SubscriptionsCommand
23
36 before(async function () { 24 before(async function () {
37 this.timeout(120000) 25 this.timeout(120000)
38 26
39 servers = await flushAndRunMultipleServers(3) 27 servers = await createMultipleServers(3)
40 28
41 // Get the access tokens 29 // Get the access tokens
42 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
@@ -47,47 +35,50 @@ describe('Test users subscriptions', function () {
47 { 35 {
48 for (const server of servers) { 36 for (const server of servers) {
49 const user = { username: 'user' + server.serverNumber, password: 'password' } 37 const user = { username: 'user' + server.serverNumber, password: 'password' }
50 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 38 await server.users.create({ username: user.username, password: user.password })
51 39
52 const accessToken = await userLogin(server, user) 40 const accessToken = await server.login.getAccessToken(user)
53 users.push({ accessToken }) 41 users.push({ accessToken })
54 42
55 const videoName1 = 'video 1-' + server.serverNumber 43 const videoName1 = 'video 1-' + server.serverNumber
56 await uploadVideo(server.url, accessToken, { name: videoName1 }) 44 await server.videos.upload({ token: accessToken, attributes: { name: videoName1 } })
57 45
58 const videoName2 = 'video 2-' + server.serverNumber 46 const videoName2 = 'video 2-' + server.serverNumber
59 await uploadVideo(server.url, accessToken, { name: videoName2 }) 47 await server.videos.upload({ token: accessToken, attributes: { name: videoName2 } })
60 } 48 }
61 } 49 }
62 50
63 await waitJobs(servers) 51 await waitJobs(servers)
52
53 command = servers[0].subscriptions
64 }) 54 })
65 55
66 it('Should display videos of server 2 on server 1', async function () { 56 it('Should display videos of server 2 on server 1', async function () {
67 const res = await getVideosList(servers[0].url) 57 const { total } = await servers[0].videos.list()
68 58
69 expect(res.body.total).to.equal(4) 59 expect(total).to.equal(4)
70 }) 60 })
71 61
72 it('User of server 1 should follow user of server 3 and root of server 1', async function () { 62 it('User of server 1 should follow user of server 3 and root of server 1', async function () {
73 this.timeout(60000) 63 this.timeout(60000)
74 64
75 await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:' + servers[2].port) 65 await command.add({ token: users[0].accessToken, targetUri: 'user3_channel@localhost:' + servers[2].port })
76 await addUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:' + servers[0].port) 66 await command.add({ token: users[0].accessToken, targetUri: 'root_channel@localhost:' + servers[0].port })
77 67
78 await waitJobs(servers) 68 await waitJobs(servers)
79 69
80 const res = await uploadVideo(servers[2].url, users[2].accessToken, { name: 'video server 3 added after follow' }) 70 const attributes = { name: 'video server 3 added after follow' }
81 video3UUID = res.body.video.uuid 71 const { uuid } = await servers[2].videos.upload({ token: users[2].accessToken, attributes })
72 video3UUID = uuid
82 73
83 await waitJobs(servers) 74 await waitJobs(servers)
84 }) 75 })
85 76
86 it('Should not display videos of server 3 on server 1', async function () { 77 it('Should not display videos of server 3 on server 1', async function () {
87 const res = await getVideosList(servers[0].url) 78 const { total, data } = await servers[0].videos.list()
79 expect(total).to.equal(4)
88 80
89 expect(res.body.total).to.equal(4) 81 for (const video of data) {
90 for (const video of res.body.data) {
91 expect(video.name).to.not.contain('1-3') 82 expect(video.name).to.not.contain('1-3')
92 expect(video.name).to.not.contain('2-3') 83 expect(video.name).to.not.contain('2-3')
93 expect(video.name).to.not.contain('video server 3 added after follow') 84 expect(video.name).to.not.contain('video server 3 added after follow')
@@ -96,17 +87,17 @@ describe('Test users subscriptions', function () {
96 87
97 it('Should list subscriptions', async function () { 88 it('Should list subscriptions', async function () {
98 { 89 {
99 const res = await listUserSubscriptions({ url: servers[0].url, token: servers[0].accessToken }) 90 const body = await command.list()
100 expect(res.body.total).to.equal(0) 91 expect(body.total).to.equal(0)
101 expect(res.body.data).to.be.an('array') 92 expect(body.data).to.be.an('array')
102 expect(res.body.data).to.have.lengthOf(0) 93 expect(body.data).to.have.lengthOf(0)
103 } 94 }
104 95
105 { 96 {
106 const res = await listUserSubscriptions({ url: servers[0].url, token: users[0].accessToken, sort: 'createdAt' }) 97 const body = await command.list({ token: users[0].accessToken, sort: 'createdAt' })
107 expect(res.body.total).to.equal(2) 98 expect(body.total).to.equal(2)
108 99
109 const subscriptions: VideoChannel[] = res.body.data 100 const subscriptions = body.data
110 expect(subscriptions).to.be.an('array') 101 expect(subscriptions).to.be.an('array')
111 expect(subscriptions).to.have.lengthOf(2) 102 expect(subscriptions).to.have.lengthOf(2)
112 103
@@ -117,8 +108,7 @@ describe('Test users subscriptions', function () {
117 108
118 it('Should get subscription', async function () { 109 it('Should get subscription', async function () {
119 { 110 {
120 const res = await getUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:' + servers[2].port) 111 const videoChannel = await command.get({ token: users[0].accessToken, uri: 'user3_channel@localhost:' + servers[2].port })
121 const videoChannel: VideoChannel = res.body
122 112
123 expect(videoChannel.name).to.equal('user3_channel') 113 expect(videoChannel.name).to.equal('user3_channel')
124 expect(videoChannel.host).to.equal('localhost:' + servers[2].port) 114 expect(videoChannel.host).to.equal('localhost:' + servers[2].port)
@@ -128,8 +118,7 @@ describe('Test users subscriptions', function () {
128 } 118 }
129 119
130 { 120 {
131 const res = await getUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:' + servers[0].port) 121 const videoChannel = await command.get({ token: users[0].accessToken, uri: 'root_channel@localhost:' + servers[0].port })
132 const videoChannel: VideoChannel = res.body
133 122
134 expect(videoChannel.name).to.equal('root_channel') 123 expect(videoChannel.name).to.equal('root_channel')
135 expect(videoChannel.host).to.equal('localhost:' + servers[0].port) 124 expect(videoChannel.host).to.equal('localhost:' + servers[0].port)
@@ -147,8 +136,7 @@ describe('Test users subscriptions', function () {
147 'user3_channel@localhost:' + servers[0].port 136 'user3_channel@localhost:' + servers[0].port
148 ] 137 ]
149 138
150 const res = await areSubscriptionsExist(servers[0].url, users[0].accessToken, uris) 139 const body = await command.exist({ token: users[0].accessToken, uris })
151 const body = res.body
152 140
153 expect(body['user3_channel@localhost:' + servers[2].port]).to.be.true 141 expect(body['user3_channel@localhost:' + servers[2].port]).to.be.true
154 expect(body['root2_channel@localhost:' + servers[0].port]).to.be.false 142 expect(body['root2_channel@localhost:' + servers[0].port]).to.be.false
@@ -158,45 +146,31 @@ describe('Test users subscriptions', function () {
158 146
159 it('Should search among subscriptions', async function () { 147 it('Should search among subscriptions', async function () {
160 { 148 {
161 const res = await listUserSubscriptions({ 149 const body = await command.list({ token: users[0].accessToken, sort: '-createdAt', search: 'user3_channel' })
162 url: servers[0].url, 150 expect(body.total).to.equal(1)
163 token: users[0].accessToken, 151 expect(body.data).to.have.lengthOf(1)
164 sort: '-createdAt',
165 search: 'user3_channel'
166 })
167 expect(res.body.total).to.equal(1)
168
169 const subscriptions = res.body.data
170 expect(subscriptions).to.have.lengthOf(1)
171 } 152 }
172 153
173 { 154 {
174 const res = await listUserSubscriptions({ 155 const body = await command.list({ token: users[0].accessToken, sort: '-createdAt', search: 'toto' })
175 url: servers[0].url, 156 expect(body.total).to.equal(0)
176 token: users[0].accessToken, 157 expect(body.data).to.have.lengthOf(0)
177 sort: '-createdAt',
178 search: 'toto'
179 })
180 expect(res.body.total).to.equal(0)
181
182 const subscriptions = res.body.data
183 expect(subscriptions).to.have.lengthOf(0)
184 } 158 }
185 }) 159 })
186 160
187 it('Should list subscription videos', async function () { 161 it('Should list subscription videos', async function () {
188 { 162 {
189 const res = await listUserSubscriptionVideos(servers[0].url, servers[0].accessToken) 163 const body = await command.listVideos()
190 expect(res.body.total).to.equal(0) 164 expect(body.total).to.equal(0)
191 expect(res.body.data).to.be.an('array') 165 expect(body.data).to.be.an('array')
192 expect(res.body.data).to.have.lengthOf(0) 166 expect(body.data).to.have.lengthOf(0)
193 } 167 }
194 168
195 { 169 {
196 const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt') 170 const body = await command.listVideos({ token: users[0].accessToken, sort: 'createdAt' })
197 expect(res.body.total).to.equal(3) 171 expect(body.total).to.equal(3)
198 172
199 const videos: Video[] = res.body.data 173 const videos = body.data
200 expect(videos).to.be.an('array') 174 expect(videos).to.be.an('array')
201 expect(videos).to.have.lengthOf(3) 175 expect(videos).to.have.lengthOf(3)
202 176
@@ -210,22 +184,22 @@ describe('Test users subscriptions', function () {
210 this.timeout(60000) 184 this.timeout(60000)
211 185
212 const videoName = 'video server 1 added after follow' 186 const videoName = 'video server 1 added after follow'
213 await uploadVideo(servers[0].url, servers[0].accessToken, { name: videoName }) 187 await servers[0].videos.upload({ attributes: { name: videoName } })
214 188
215 await waitJobs(servers) 189 await waitJobs(servers)
216 190
217 { 191 {
218 const res = await listUserSubscriptionVideos(servers[0].url, servers[0].accessToken) 192 const body = await command.listVideos()
219 expect(res.body.total).to.equal(0) 193 expect(body.total).to.equal(0)
220 expect(res.body.data).to.be.an('array') 194 expect(body.data).to.be.an('array')
221 expect(res.body.data).to.have.lengthOf(0) 195 expect(body.data).to.have.lengthOf(0)
222 } 196 }
223 197
224 { 198 {
225 const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt') 199 const body = await command.listVideos({ token: users[0].accessToken, sort: 'createdAt' })
226 expect(res.body.total).to.equal(4) 200 expect(body.total).to.equal(4)
227 201
228 const videos: Video[] = res.body.data 202 const videos = body.data
229 expect(videos).to.be.an('array') 203 expect(videos).to.be.an('array')
230 expect(videos).to.have.lengthOf(4) 204 expect(videos).to.have.lengthOf(4)
231 205
@@ -236,10 +210,10 @@ describe('Test users subscriptions', function () {
236 } 210 }
237 211
238 { 212 {
239 const res = await getVideosList(servers[0].url) 213 const { data, total } = await servers[0].videos.list()
214 expect(total).to.equal(5)
240 215
241 expect(res.body.total).to.equal(5) 216 for (const video of data) {
242 for (const video of res.body.data) {
243 expect(video.name).to.not.contain('1-3') 217 expect(video.name).to.not.contain('1-3')
244 expect(video.name).to.not.contain('2-3') 218 expect(video.name).to.not.contain('2-3')
245 expect(video.name).to.not.contain('video server 3 added after follow') 219 expect(video.name).to.not.contain('video server 3 added after follow')
@@ -250,17 +224,16 @@ describe('Test users subscriptions', function () {
250 it('Should have server 1 follow server 3 and display server 3 videos', async function () { 224 it('Should have server 1 follow server 3 and display server 3 videos', async function () {
251 this.timeout(60000) 225 this.timeout(60000)
252 226
253 await follow(servers[0].url, [ servers[2].url ], servers[0].accessToken) 227 await servers[0].follows.follow({ hosts: [ servers[2].url ] })
254 228
255 await waitJobs(servers) 229 await waitJobs(servers)
256 230
257 const res = await getVideosList(servers[0].url) 231 const { data, total } = await servers[0].videos.list()
258 232 expect(total).to.equal(8)
259 expect(res.body.total).to.equal(8)
260 233
261 const names = [ '1-3', '2-3', 'video server 3 added after follow' ] 234 const names = [ '1-3', '2-3', 'video server 3 added after follow' ]
262 for (const name of names) { 235 for (const name of names) {
263 const video = res.body.data.find(v => v.name.indexOf(name) === -1) 236 const video = data.find(v => v.name.includes(name))
264 expect(video).to.not.be.undefined 237 expect(video).to.not.be.undefined
265 } 238 }
266 }) 239 })
@@ -268,14 +241,14 @@ describe('Test users subscriptions', function () {
268 it('Should remove follow server 1 -> server 3 and hide server 3 videos', async function () { 241 it('Should remove follow server 1 -> server 3 and hide server 3 videos', async function () {
269 this.timeout(60000) 242 this.timeout(60000)
270 243
271 await unfollow(servers[0].url, servers[0].accessToken, servers[2]) 244 await servers[0].follows.unfollow({ target: servers[2] })
272 245
273 await waitJobs(servers) 246 await waitJobs(servers)
274 247
275 const res = await getVideosList(servers[0].url) 248 const { total, data } = await servers[0].videos.list()
249 expect(total).to.equal(5)
276 250
277 expect(res.body.total).to.equal(5) 251 for (const video of data) {
278 for (const video of res.body.data) {
279 expect(video.name).to.not.contain('1-3') 252 expect(video.name).to.not.contain('1-3')
280 expect(video.name).to.not.contain('2-3') 253 expect(video.name).to.not.contain('2-3')
281 expect(video.name).to.not.contain('video server 3 added after follow') 254 expect(video.name).to.not.contain('video server 3 added after follow')
@@ -284,17 +257,17 @@ describe('Test users subscriptions', function () {
284 257
285 it('Should still list subscription videos', async function () { 258 it('Should still list subscription videos', async function () {
286 { 259 {
287 const res = await listUserSubscriptionVideos(servers[0].url, servers[0].accessToken) 260 const body = await command.listVideos()
288 expect(res.body.total).to.equal(0) 261 expect(body.total).to.equal(0)
289 expect(res.body.data).to.be.an('array') 262 expect(body.data).to.be.an('array')
290 expect(res.body.data).to.have.lengthOf(0) 263 expect(body.data).to.have.lengthOf(0)
291 } 264 }
292 265
293 { 266 {
294 const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt') 267 const body = await command.listVideos({ token: users[0].accessToken, sort: 'createdAt' })
295 expect(res.body.total).to.equal(4) 268 expect(body.total).to.equal(4)
296 269
297 const videos: Video[] = res.body.data 270 const videos = body.data
298 expect(videos).to.be.an('array') 271 expect(videos).to.be.an('array')
299 expect(videos).to.have.lengthOf(4) 272 expect(videos).to.have.lengthOf(4)
300 273
@@ -308,58 +281,55 @@ describe('Test users subscriptions', function () {
308 it('Should update a video of server 3 and see the updated video on server 1', async function () { 281 it('Should update a video of server 3 and see the updated video on server 1', async function () {
309 this.timeout(30000) 282 this.timeout(30000)
310 283
311 await updateVideo(servers[2].url, users[2].accessToken, video3UUID, { name: 'video server 3 added after follow updated' }) 284 await servers[2].videos.update({ id: video3UUID, attributes: { name: 'video server 3 added after follow updated' } })
312 285
313 await waitJobs(servers) 286 await waitJobs(servers)
314 287
315 const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt') 288 const body = await command.listVideos({ token: users[0].accessToken, sort: 'createdAt' })
316 const videos: Video[] = res.body.data 289 expect(body.data[2].name).to.equal('video server 3 added after follow updated')
317 expect(videos[2].name).to.equal('video server 3 added after follow updated')
318 }) 290 })
319 291
320 it('Should remove user of server 3 subscription', async function () { 292 it('Should remove user of server 3 subscription', async function () {
321 this.timeout(30000) 293 this.timeout(30000)
322 294
323 await removeUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:' + servers[2].port) 295 await command.remove({ token: users[0].accessToken, uri: 'user3_channel@localhost:' + servers[2].port })
324 296
325 await waitJobs(servers) 297 await waitJobs(servers)
326 }) 298 })
327 299
328 it('Should not display its videos anymore', async function () { 300 it('Should not display its videos anymore', async function () {
329 { 301 const body = await command.listVideos({ token: users[0].accessToken, sort: 'createdAt' })
330 const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt') 302 expect(body.total).to.equal(1)
331 expect(res.body.total).to.equal(1)
332 303
333 const videos: Video[] = res.body.data 304 const videos = body.data
334 expect(videos).to.be.an('array') 305 expect(videos).to.be.an('array')
335 expect(videos).to.have.lengthOf(1) 306 expect(videos).to.have.lengthOf(1)
336 307
337 expect(videos[0].name).to.equal('video server 1 added after follow') 308 expect(videos[0].name).to.equal('video server 1 added after follow')
338 }
339 }) 309 })
340 310
341 it('Should remove the root subscription and not display the videos anymore', async function () { 311 it('Should remove the root subscription and not display the videos anymore', async function () {
342 this.timeout(30000) 312 this.timeout(30000)
343 313
344 await removeUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:' + servers[0].port) 314 await command.remove({ token: users[0].accessToken, uri: 'root_channel@localhost:' + servers[0].port })
345 315
346 await waitJobs(servers) 316 await waitJobs(servers)
347 317
348 { 318 {
349 const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt') 319 const body = await command.list({ token: users[0].accessToken, sort: 'createdAt' })
350 expect(res.body.total).to.equal(0) 320 expect(body.total).to.equal(0)
351 321
352 const videos: Video[] = res.body.data 322 const videos = body.data
353 expect(videos).to.be.an('array') 323 expect(videos).to.be.an('array')
354 expect(videos).to.have.lengthOf(0) 324 expect(videos).to.have.lengthOf(0)
355 } 325 }
356 }) 326 })
357 327
358 it('Should correctly display public videos on server 1', async function () { 328 it('Should correctly display public videos on server 1', async function () {
359 const res = await getVideosList(servers[0].url) 329 const { total, data } = await servers[0].videos.list()
330 expect(total).to.equal(5)
360 331
361 expect(res.body.total).to.equal(5) 332 for (const video of data) {
362 for (const video of res.body.data) {
363 expect(video.name).to.not.contain('1-3') 333 expect(video.name).to.not.contain('1-3')
364 expect(video.name).to.not.contain('2-3') 334 expect(video.name).to.not.contain('2-3')
365 expect(video.name).to.not.contain('video server 3 added after follow updated') 335 expect(video.name).to.not.contain('video server 3 added after follow updated')
@@ -369,15 +339,15 @@ describe('Test users subscriptions', function () {
369 it('Should follow user of server 3 again', async function () { 339 it('Should follow user of server 3 again', async function () {
370 this.timeout(60000) 340 this.timeout(60000)
371 341
372 await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:' + servers[2].port) 342 await command.add({ token: users[0].accessToken, targetUri: 'user3_channel@localhost:' + servers[2].port })
373 343
374 await waitJobs(servers) 344 await waitJobs(servers)
375 345
376 { 346 {
377 const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt') 347 const body = await command.listVideos({ token: users[0].accessToken, sort: 'createdAt' })
378 expect(res.body.total).to.equal(3) 348 expect(body.total).to.equal(3)
379 349
380 const videos: Video[] = res.body.data 350 const videos = body.data
381 expect(videos).to.be.an('array') 351 expect(videos).to.be.an('array')
382 expect(videos).to.have.lengthOf(3) 352 expect(videos).to.have.lengthOf(3)
383 353
@@ -387,10 +357,10 @@ describe('Test users subscriptions', function () {
387 } 357 }
388 358
389 { 359 {
390 const res = await getVideosList(servers[0].url) 360 const { total, data } = await servers[0].videos.list()
361 expect(total).to.equal(5)
391 362
392 expect(res.body.total).to.equal(5) 363 for (const video of data) {
393 for (const video of res.body.data) {
394 expect(video.name).to.not.contain('1-3') 364 expect(video.name).to.not.contain('1-3')
395 expect(video.name).to.not.contain('2-3') 365 expect(video.name).to.not.contain('2-3')
396 expect(video.name).to.not.contain('video server 3 added after follow updated') 366 expect(video.name).to.not.contain('video server 3 added after follow updated')
diff --git a/server/tests/api/users/users-multiple-servers.ts b/server/tests/api/users/users-multiple-servers.ts
index f60c66e4b..d0ca82b07 100644
--- a/server/tests/api/users/users-multiple-servers.ts
+++ b/server/tests/api/users/users-multiple-servers.ts
@@ -1,34 +1,30 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { Account } from '../../../../shared/models/actors' 4import * as chai from 'chai'
6import { 5import {
6 checkActorFilesWereRemoved,
7 checkTmpIsEmpty, 7 checkTmpIsEmpty,
8 checkVideoFilesWereRemoved, 8 checkVideoFilesWereRemoved,
9 cleanupTests, 9 cleanupTests,
10 createUser, 10 createMultipleServers,
11 doubleFollow, 11 doubleFollow,
12 flushAndRunMultipleServers, 12 PeerTubeServer,
13 getAccountVideos, 13 saveVideoInServers,
14 getVideoChannelsList, 14 setAccessTokensToServers,
15 removeUser, 15 testImage,
16 updateMyUser, 16 waitJobs
17 userLogin 17} from '@shared/extra-utils'
18} from '../../../../shared/extra-utils' 18import { MyUser } from '@shared/models'
19import { getMyUserInformation, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../../../shared/extra-utils/index'
20import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../../../shared/extra-utils/users/accounts'
21import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
22import { User } from '../../../../shared/models/users'
23import { VideoChannel } from '../../../../shared/models/videos'
24import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
25 19
26const expect = chai.expect 20const expect = chai.expect
27 21
28describe('Test users with multiple servers', function () { 22describe('Test users with multiple servers', function () {
29 let servers: ServerInfo[] = [] 23 let servers: PeerTubeServer[] = []
30 let user: User 24
25 let user: MyUser
31 let userId: number 26 let userId: number
27
32 let videoUUID: string 28 let videoUUID: string
33 let userAccessToken: string 29 let userAccessToken: string
34 let userAvatarFilename: string 30 let userAvatarFilename: string
@@ -36,7 +32,7 @@ describe('Test users with multiple servers', function () {
36 before(async function () { 32 before(async function () {
37 this.timeout(120_000) 33 this.timeout(120_000)
38 34
39 servers = await flushAndRunMultipleServers(3) 35 servers = await createMultipleServers(3)
40 36
41 // Get the access tokens 37 // Get the access tokens
42 await setAccessTokensToServers(servers) 38 await setAccessTokensToServers(servers)
@@ -49,43 +45,31 @@ describe('Test users with multiple servers', function () {
49 await doubleFollow(servers[1], servers[2]) 45 await doubleFollow(servers[1], servers[2])
50 46
51 // The root user of server 1 is propagated to servers 2 and 3 47 // The root user of server 1 is propagated to servers 2 and 3
52 await uploadVideo(servers[0].url, servers[0].accessToken, {}) 48 await servers[0].videos.upload()
53 49
54 { 50 {
55 const user = { 51 const username = 'user1'
56 username: 'user1', 52 const created = await servers[0].users.create({ username })
57 password: 'password' 53 userId = created.id
58 } 54 userAccessToken = await servers[0].login.getAccessToken(username)
59 const res = await createUser({
60 url: servers[0].url,
61 accessToken: servers[0].accessToken,
62 username: user.username,
63 password: user.password
64 })
65 userId = res.body.user.id
66 userAccessToken = await userLogin(servers[0], user)
67 } 55 }
68 56
69 { 57 {
70 const resVideo = await uploadVideo(servers[0].url, userAccessToken, {}) 58 const { uuid } = await servers[0].videos.upload({ token: userAccessToken })
71 videoUUID = resVideo.body.video.uuid 59 videoUUID = uuid
72 }
73 60
74 await waitJobs(servers) 61 await waitJobs(servers)
62
63 await saveVideoInServers(servers, videoUUID)
64 }
75 }) 65 })
76 66
77 it('Should be able to update my display name', async function () { 67 it('Should be able to update my display name', async function () {
78 this.timeout(10000) 68 this.timeout(10000)
79 69
80 await updateMyUser({ 70 await servers[0].users.updateMe({ displayName: 'my super display name' })
81 url: servers[0].url,
82 accessToken: servers[0].accessToken,
83 displayName: 'my super display name'
84 })
85
86 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
87 user = res.body
88 71
72 user = await servers[0].users.getMyInfo()
89 expect(user.account.displayName).to.equal('my super display name') 73 expect(user.account.displayName).to.equal('my super display name')
90 74
91 await waitJobs(servers) 75 await waitJobs(servers)
@@ -94,14 +78,9 @@ describe('Test users with multiple servers', function () {
94 it('Should be able to update my description', async function () { 78 it('Should be able to update my description', async function () {
95 this.timeout(10_000) 79 this.timeout(10_000)
96 80
97 await updateMyUser({ 81 await servers[0].users.updateMe({ description: 'my super description updated' })
98 url: servers[0].url,
99 accessToken: servers[0].accessToken,
100 description: 'my super description updated'
101 })
102 82
103 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) 83 user = await servers[0].users.getMyInfo()
104 user = res.body
105 expect(user.account.displayName).to.equal('my super display name') 84 expect(user.account.displayName).to.equal('my super display name')
106 expect(user.account.description).to.equal('my super description updated') 85 expect(user.account.description).to.equal('my super description updated')
107 86
@@ -113,15 +92,9 @@ describe('Test users with multiple servers', function () {
113 92
114 const fixture = 'avatar2.png' 93 const fixture = 'avatar2.png'
115 94
116 await updateMyAvatar({ 95 await servers[0].users.updateMyAvatar({ fixture })
117 url: servers[0].url,
118 accessToken: servers[0].accessToken,
119 fixture
120 })
121
122 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
123 user = res.body
124 96
97 user = await servers[0].users.getMyInfo()
125 userAvatarFilename = user.account.avatar.path 98 userAvatarFilename = user.account.avatar.path
126 99
127 await testImage(servers[0].url, 'avatar2-resized', userAvatarFilename, '.png') 100 await testImage(servers[0].url, 'avatar2-resized', userAvatarFilename, '.png')
@@ -133,13 +106,12 @@ describe('Test users with multiple servers', function () {
133 let createdAt: string | Date 106 let createdAt: string | Date
134 107
135 for (const server of servers) { 108 for (const server of servers) {
136 const resAccounts = await getAccountsList(server.url, '-createdAt') 109 const body = await server.accounts.list({ sort: '-createdAt' })
137 110
138 const resList = resAccounts.body.data.find(a => a.name === 'root' && a.host === 'localhost:' + servers[0].port) as Account 111 const resList = body.data.find(a => a.name === 'root' && a.host === 'localhost:' + servers[0].port)
139 expect(resList).not.to.be.undefined 112 expect(resList).not.to.be.undefined
140 113
141 const resAccount = await getAccount(server.url, resList.name + '@' + resList.host) 114 const account = await server.accounts.get({ accountName: resList.name + '@' + resList.host })
142 const account = resAccount.body as Account
143 115
144 if (!createdAt) createdAt = account.createdAt 116 if (!createdAt) createdAt = account.createdAt
145 117
@@ -161,31 +133,29 @@ describe('Test users with multiple servers', function () {
161 133
162 it('Should list account videos', async function () { 134 it('Should list account videos', async function () {
163 for (const server of servers) { 135 for (const server of servers) {
164 const res = await getAccountVideos(server.url, server.accessToken, 'user1@localhost:' + servers[0].port, 0, 5) 136 const { total, data } = await server.videos.listByAccount({ handle: 'user1@localhost:' + servers[0].port })
165 137
166 expect(res.body.total).to.equal(1) 138 expect(total).to.equal(1)
167 expect(res.body.data).to.be.an('array') 139 expect(data).to.be.an('array')
168 expect(res.body.data).to.have.lengthOf(1) 140 expect(data).to.have.lengthOf(1)
169 expect(res.body.data[0].uuid).to.equal(videoUUID) 141 expect(data[0].uuid).to.equal(videoUUID)
170 } 142 }
171 }) 143 })
172 144
173 it('Should search through account videos', async function () { 145 it('Should search through account videos', async function () {
174 this.timeout(10_000) 146 this.timeout(10_000)
175 147
176 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'Kami no chikara' }) 148 const created = await servers[0].videos.upload({ token: userAccessToken, attributes: { name: 'Kami no chikara' } })
177 149
178 await waitJobs(servers) 150 await waitJobs(servers)
179 151
180 for (const server of servers) { 152 for (const server of servers) {
181 const res = await getAccountVideos(server.url, server.accessToken, 'user1@localhost:' + servers[0].port, 0, 5, undefined, { 153 const { total, data } = await server.videos.listByAccount({ handle: 'user1@localhost:' + servers[0].port, search: 'Kami' })
182 search: 'Kami' 154
183 }) 155 expect(total).to.equal(1)
184 156 expect(data).to.be.an('array')
185 expect(res.body.total).to.equal(1) 157 expect(data).to.have.lengthOf(1)
186 expect(res.body.data).to.be.an('array') 158 expect(data[0].uuid).to.equal(created.uuid)
187 expect(res.body.data).to.have.lengthOf(1)
188 expect(res.body.data[0].uuid).to.equal(resVideo.body.video.uuid)
189 } 159 }
190 }) 160 })
191 161
@@ -193,32 +163,28 @@ describe('Test users with multiple servers', function () {
193 this.timeout(10_000) 163 this.timeout(10_000)
194 164
195 for (const server of servers) { 165 for (const server of servers) {
196 const resAccounts = await getAccountsList(server.url, '-createdAt') 166 const body = await server.accounts.list({ sort: '-createdAt' })
197 167
198 const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port) as Account 168 const accountDeleted = body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port)
199 expect(accountDeleted).not.to.be.undefined 169 expect(accountDeleted).not.to.be.undefined
200 170
201 const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) 171 const { data } = await server.channels.list()
202 const videoChannelDeleted = resVideoChannels.body.data.find(a => { 172 const videoChannelDeleted = data.find(a => a.displayName === 'Main user1 channel' && a.host === 'localhost:' + servers[0].port)
203 return a.displayName === 'Main user1 channel' && a.host === 'localhost:' + servers[0].port
204 }) as VideoChannel
205 expect(videoChannelDeleted).not.to.be.undefined 173 expect(videoChannelDeleted).not.to.be.undefined
206 } 174 }
207 175
208 await removeUser(servers[0].url, userId, servers[0].accessToken) 176 await servers[0].users.remove({ userId })
209 177
210 await waitJobs(servers) 178 await waitJobs(servers)
211 179
212 for (const server of servers) { 180 for (const server of servers) {
213 const resAccounts = await getAccountsList(server.url, '-createdAt') 181 const body = await server.accounts.list({ sort: '-createdAt' })
214 182
215 const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port) as Account 183 const accountDeleted = body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port)
216 expect(accountDeleted).to.be.undefined 184 expect(accountDeleted).to.be.undefined
217 185
218 const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) 186 const { data } = await server.channels.list()
219 const videoChannelDeleted = resVideoChannels.body.data.find(a => { 187 const videoChannelDeleted = data.find(a => a.name === 'Main user1 channel' && a.host === 'localhost:' + servers[0].port)
220 return a.name === 'Main user1 channel' && a.host === 'localhost:' + servers[0].port
221 }) as VideoChannel
222 expect(videoChannelDeleted).to.be.undefined 188 expect(videoChannelDeleted).to.be.undefined
223 } 189 }
224 }) 190 })
@@ -231,7 +197,7 @@ describe('Test users with multiple servers', function () {
231 197
232 it('Should not have video files', async () => { 198 it('Should not have video files', async () => {
233 for (const server of servers) { 199 for (const server of servers) {
234 await checkVideoFilesWereRemoved(videoUUID, server.internalServerNumber) 200 await checkVideoFilesWereRemoved({ server, video: server.store.videoDetails })
235 } 201 }
236 }) 202 })
237 203
diff --git a/server/tests/api/users/users-verification.ts b/server/tests/api/users/users-verification.ts
index e0f2f2112..f54463359 100644
--- a/server/tests/api/users/users-verification.ts
+++ b/server/tests/api/users/users-verification.ts
@@ -1,30 +1,14 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { 4import * as chai from 'chai'
6 cleanupTests, 5import { cleanupTests, createSingleServer, MockSmtpServer, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
7 flushAndRunServer, 6import { HttpStatusCode } from '@shared/models'
8 getMyUserInformation,
9 getUserInformation,
10 login,
11 registerUser,
12 ServerInfo,
13 updateCustomSubConfig,
14 updateMyUser,
15 userLogin,
16 verifyEmail
17} from '../../../../shared/extra-utils'
18import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
19import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
20import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
21import { User } from '../../../../shared/models/users'
22import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
23 7
24const expect = chai.expect 8const expect = chai.expect
25 9
26describe('Test users account verification', function () { 10describe('Test users account verification', function () {
27 let server: ServerInfo 11 let server: PeerTubeServer
28 let userId: number 12 let userId: number
29 let userAccessToken: string 13 let userAccessToken: string
30 let verificationString: string 14 let verificationString: string
@@ -50,7 +34,7 @@ describe('Test users account verification', function () {
50 port 34 port
51 } 35 }
52 } 36 }
53 server = await flushAndRunServer(1, overrideConfig) 37 server = await createSingleServer(1, overrideConfig)
54 38
55 await setAccessTokensToServers([ server ]) 39 await setAccessTokensToServers([ server ])
56 }) 40 })
@@ -58,15 +42,17 @@ describe('Test users account verification', function () {
58 it('Should register user and send verification email if verification required', async function () { 42 it('Should register user and send verification email if verification required', async function () {
59 this.timeout(30000) 43 this.timeout(30000)
60 44
61 await updateCustomSubConfig(server.url, server.accessToken, { 45 await server.config.updateCustomSubConfig({
62 signup: { 46 newConfig: {
63 enabled: true, 47 signup: {
64 requiresEmailVerification: true, 48 enabled: true,
65 limit: 10 49 requiresEmailVerification: true,
50 limit: 10
51 }
66 } 52 }
67 }) 53 })
68 54
69 await registerUser(server.url, user1.username, user1.password) 55 await server.users.register(user1)
70 56
71 await waitJobs(server) 57 await waitJobs(server)
72 expectedEmailsLength++ 58 expectedEmailsLength++
@@ -85,23 +71,23 @@ describe('Test users account verification', function () {
85 71
86 userId = parseInt(userIdMatches[1], 10) 72 userId = parseInt(userIdMatches[1], 10)
87 73
88 const resUserInfo = await getUserInformation(server.url, server.accessToken, userId) 74 const body = await server.users.get({ userId })
89 expect(resUserInfo.body.emailVerified).to.be.false 75 expect(body.emailVerified).to.be.false
90 }) 76 })
91 77
92 it('Should not allow login for user with unverified email', async function () { 78 it('Should not allow login for user with unverified email', async function () {
93 const resLogin = await login(server.url, server.client, user1, HttpStatusCode.BAD_REQUEST_400) 79 const { detail } = await server.login.login({ user: user1, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
94 expect(resLogin.body.detail).to.contain('User email is not verified.') 80 expect(detail).to.contain('User email is not verified.')
95 }) 81 })
96 82
97 it('Should verify the user via email and allow login', async function () { 83 it('Should verify the user via email and allow login', async function () {
98 await verifyEmail(server.url, userId, verificationString) 84 await server.users.verifyEmail({ userId, verificationString })
99 85
100 const res = await login(server.url, server.client, user1) 86 const body = await server.login.login({ user: user1 })
101 userAccessToken = res.body.access_token 87 userAccessToken = body.access_token
102 88
103 const resUserVerified = await getUserInformation(server.url, server.accessToken, userId) 89 const user = await server.users.get({ userId })
104 expect(resUserVerified.body.emailVerified).to.be.true 90 expect(user.emailVerified).to.be.true
105 }) 91 })
106 92
107 it('Should be able to change the user email', async function () { 93 it('Should be able to change the user email', async function () {
@@ -110,9 +96,8 @@ describe('Test users account verification', function () {
110 let updateVerificationString: string 96 let updateVerificationString: string
111 97
112 { 98 {
113 await updateMyUser({ 99 await server.users.updateMe({
114 url: server.url, 100 token: userAccessToken,
115 accessToken: userAccessToken,
116 email: 'updated@example.com', 101 email: 'updated@example.com',
117 currentPassword: user1.password 102 currentPassword: user1.password
118 }) 103 })
@@ -128,19 +113,15 @@ describe('Test users account verification', function () {
128 } 113 }
129 114
130 { 115 {
131 const res = await getMyUserInformation(server.url, userAccessToken) 116 const me = await server.users.getMyInfo({ token: userAccessToken })
132 const me: User = res.body
133
134 expect(me.email).to.equal('user_1@example.com') 117 expect(me.email).to.equal('user_1@example.com')
135 expect(me.pendingEmail).to.equal('updated@example.com') 118 expect(me.pendingEmail).to.equal('updated@example.com')
136 } 119 }
137 120
138 { 121 {
139 await verifyEmail(server.url, userId, updateVerificationString, true) 122 await server.users.verifyEmail({ userId, verificationString: updateVerificationString, isPendingEmail: true })
140
141 const res = await getMyUserInformation(server.url, userAccessToken)
142 const me: User = res.body
143 123
124 const me = await server.users.getMyInfo({ token: userAccessToken })
144 expect(me.email).to.equal('updated@example.com') 125 expect(me.email).to.equal('updated@example.com')
145 expect(me.pendingEmail).to.be.null 126 expect(me.pendingEmail).to.be.null
146 } 127 }
@@ -148,35 +129,39 @@ describe('Test users account verification', function () {
148 129
149 it('Should register user not requiring email verification if setting not enabled', async function () { 130 it('Should register user not requiring email verification if setting not enabled', async function () {
150 this.timeout(5000) 131 this.timeout(5000)
151 await updateCustomSubConfig(server.url, server.accessToken, { 132 await server.config.updateCustomSubConfig({
152 signup: { 133 newConfig: {
153 enabled: true, 134 signup: {
154 requiresEmailVerification: false, 135 enabled: true,
155 limit: 10 136 requiresEmailVerification: false,
137 limit: 10
138 }
156 } 139 }
157 }) 140 })
158 141
159 await registerUser(server.url, user2.username, user2.password) 142 await server.users.register(user2)
160 143
161 await waitJobs(server) 144 await waitJobs(server)
162 expect(emails).to.have.lengthOf(expectedEmailsLength) 145 expect(emails).to.have.lengthOf(expectedEmailsLength)
163 146
164 const accessToken = await userLogin(server, user2) 147 const accessToken = await server.login.getAccessToken(user2)
165 148
166 const resMyUserInfo = await getMyUserInformation(server.url, accessToken) 149 const user = await server.users.getMyInfo({ token: accessToken })
167 expect(resMyUserInfo.body.emailVerified).to.be.null 150 expect(user.emailVerified).to.be.null
168 }) 151 })
169 152
170 it('Should allow login for user with unverified email when setting later enabled', async function () { 153 it('Should allow login for user with unverified email when setting later enabled', async function () {
171 await updateCustomSubConfig(server.url, server.accessToken, { 154 await server.config.updateCustomSubConfig({
172 signup: { 155 newConfig: {
173 enabled: true, 156 signup: {
174 requiresEmailVerification: true, 157 enabled: true,
175 limit: 10 158 requiresEmailVerification: true,
159 limit: 10
160 }
176 } 161 }
177 }) 162 })
178 163
179 await userLogin(server, user2) 164 await server.login.getAccessToken(user2)
180 }) 165 })
181 166
182 after(async function () { 167 after(async function () {
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts
index 87ba775f6..1419ae820 100644
--- a/server/tests/api/users/users.ts
+++ b/server/tests/api/users/users.ts
@@ -2,63 +2,24 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { AbuseState, AbuseUpdate, MyUser, User, UserRole, Video, VideoPlaylistType } from '@shared/models'
6import { CustomConfig, OAuth2ErrorCode } from '@shared/models/server'
7import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
8import { 5import {
9 addVideoCommentThread,
10 blockUser,
11 cleanupTests, 6 cleanupTests,
12 closeAllSequelize, 7 createSingleServer,
13 createUser,
14 deleteMe,
15 flushAndRunServer,
16 getAccountRatings,
17 getAdminAbusesList,
18 getBlacklistedVideosList,
19 getCustomConfig,
20 getMyUserInformation,
21 getMyUserVideoQuotaUsed,
22 getMyUserVideoRating,
23 getUserInformation,
24 getUsersList,
25 getUsersListPaginationAndSort,
26 getVideoChannel,
27 getVideosList,
28 installPlugin,
29 killallServers, 8 killallServers,
30 login,
31 makePutBodyRequest, 9 makePutBodyRequest,
32 rateVideo, 10 PeerTubeServer,
33 registerUserWithChannel, 11 setAccessTokensToServers,
34 removeUser,
35 removeVideo,
36 reportAbuse,
37 reRunServer,
38 ServerInfo,
39 setTokenField,
40 testImage, 12 testImage,
41 unblockUser,
42 updateAbuse,
43 updateCustomSubConfig,
44 updateMyAvatar,
45 updateMyUser,
46 updateUser,
47 uploadVideo,
48 userLogin,
49 waitJobs 13 waitJobs
50} from '../../../../shared/extra-utils' 14} from '@shared/extra-utils'
51import { follow } from '../../../../shared/extra-utils/server/follows' 15import { AbuseState, HttpStatusCode, OAuth2ErrorCode, UserAdminFlag, UserRole, Video, VideoPlaylistType } from '@shared/models'
52import { logout, refreshToken, setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
53import { getMyVideos } from '../../../../shared/extra-utils/videos/videos'
54import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
55 16
56const expect = chai.expect 17const expect = chai.expect
57 18
58describe('Test users', function () { 19describe('Test users', function () {
59 let server: ServerInfo 20 let server: PeerTubeServer
60 let accessToken: string 21 let token: string
61 let accessTokenUser: string 22 let userToken: string
62 let videoId: number 23 let videoId: number
63 let userId: number 24 let userId: number
64 const user = { 25 const user = {
@@ -69,7 +30,7 @@ describe('Test users', function () {
69 before(async function () { 30 before(async function () {
70 this.timeout(30000) 31 this.timeout(30000)
71 32
72 server = await flushAndRunServer(1, { 33 server = await createSingleServer(1, {
73 rates_limit: { 34 rates_limit: {
74 login: { 35 login: {
75 max: 30 36 max: 30
@@ -79,7 +40,7 @@ describe('Test users', function () {
79 40
80 await setAccessTokensToServers([ server ]) 41 await setAccessTokensToServers([ server ])
81 42
82 await installPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-theme-background-red' }) 43 await server.plugins.install({ npmName: 'peertube-theme-background-red' })
83 }) 44 })
84 45
85 describe('OAuth client', function () { 46 describe('OAuth client', function () {
@@ -90,158 +51,156 @@ describe('Test users', function () {
90 it('Should remove the last client') 51 it('Should remove the last client')
91 52
92 it('Should not login with an invalid client id', async function () { 53 it('Should not login with an invalid client id', async function () {
93 const client = { id: 'client', secret: server.client.secret } 54 const client = { id: 'client', secret: server.store.client.secret }
94 const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400) 55 const body = await server.login.login({ client, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
95 56
96 expect(res.body.code).to.equal(OAuth2ErrorCode.INVALID_CLIENT) 57 expect(body.code).to.equal(OAuth2ErrorCode.INVALID_CLIENT)
97 expect(res.body.error).to.contain('client is invalid') 58 expect(body.error).to.contain('client is invalid')
98 expect(res.body.type.startsWith('https://')).to.be.true 59 expect(body.type.startsWith('https://')).to.be.true
99 expect(res.body.type).to.contain(OAuth2ErrorCode.INVALID_CLIENT) 60 expect(body.type).to.contain(OAuth2ErrorCode.INVALID_CLIENT)
100 }) 61 })
101 62
102 it('Should not login with an invalid client secret', async function () { 63 it('Should not login with an invalid client secret', async function () {
103 const client = { id: server.client.id, secret: 'coucou' } 64 const client = { id: server.store.client.id, secret: 'coucou' }
104 const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400) 65 const body = await server.login.login({ client, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
105 66
106 expect(res.body.code).to.equal(OAuth2ErrorCode.INVALID_CLIENT) 67 expect(body.code).to.equal(OAuth2ErrorCode.INVALID_CLIENT)
107 expect(res.body.error).to.contain('client is invalid') 68 expect(body.error).to.contain('client is invalid')
108 expect(res.body.type.startsWith('https://')).to.be.true 69 expect(body.type.startsWith('https://')).to.be.true
109 expect(res.body.type).to.contain(OAuth2ErrorCode.INVALID_CLIENT) 70 expect(body.type).to.contain(OAuth2ErrorCode.INVALID_CLIENT)
110 }) 71 })
111 }) 72 })
112 73
113 describe('Login', function () { 74 describe('Login', function () {
114 75
115 it('Should not login with an invalid username', async function () { 76 it('Should not login with an invalid username', async function () {
116 const user = { username: 'captain crochet', password: server.user.password } 77 const user = { username: 'captain crochet', password: server.store.user.password }
117 const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400) 78 const body = await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
118 79
119 expect(res.body.code).to.equal(OAuth2ErrorCode.INVALID_GRANT) 80 expect(body.code).to.equal(OAuth2ErrorCode.INVALID_GRANT)
120 expect(res.body.error).to.contain('credentials are invalid') 81 expect(body.error).to.contain('credentials are invalid')
121 expect(res.body.type.startsWith('https://')).to.be.true 82 expect(body.type.startsWith('https://')).to.be.true
122 expect(res.body.type).to.contain(OAuth2ErrorCode.INVALID_GRANT) 83 expect(body.type).to.contain(OAuth2ErrorCode.INVALID_GRANT)
123 }) 84 })
124 85
125 it('Should not login with an invalid password', async function () { 86 it('Should not login with an invalid password', async function () {
126 const user = { username: server.user.username, password: 'mew_three' } 87 const user = { username: server.store.user.username, password: 'mew_three' }
127 const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400) 88 const body = await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
128 89
129 expect(res.body.code).to.equal(OAuth2ErrorCode.INVALID_GRANT) 90 expect(body.code).to.equal(OAuth2ErrorCode.INVALID_GRANT)
130 expect(res.body.error).to.contain('credentials are invalid') 91 expect(body.error).to.contain('credentials are invalid')
131 expect(res.body.type.startsWith('https://')).to.be.true 92 expect(body.type.startsWith('https://')).to.be.true
132 expect(res.body.type).to.contain(OAuth2ErrorCode.INVALID_GRANT) 93 expect(body.type).to.contain(OAuth2ErrorCode.INVALID_GRANT)
133 }) 94 })
134 95
135 it('Should not be able to upload a video', async function () { 96 it('Should not be able to upload a video', async function () {
136 accessToken = 'my_super_token' 97 token = 'my_super_token'
137 98
138 const videoAttributes = {} 99 await server.videos.upload({ token, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
139 await uploadVideo(server.url, accessToken, videoAttributes, HttpStatusCode.UNAUTHORIZED_401)
140 }) 100 })
141 101
142 it('Should not be able to follow', async function () { 102 it('Should not be able to follow', async function () {
143 accessToken = 'my_super_token' 103 token = 'my_super_token'
144 await follow(server.url, [ 'http://example.com' ], accessToken, HttpStatusCode.UNAUTHORIZED_401) 104
105 await server.follows.follow({
106 hosts: [ 'http://example.com' ],
107 token,
108 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
109 })
145 }) 110 })
146 111
147 it('Should not be able to unfollow') 112 it('Should not be able to unfollow')
148 113
149 it('Should be able to login', async function () { 114 it('Should be able to login', async function () {
150 const res = await login(server.url, server.client, server.user, HttpStatusCode.OK_200) 115 const body = await server.login.login({ expectedStatus: HttpStatusCode.OK_200 })
151 116
152 accessToken = res.body.access_token 117 token = body.access_token
153 }) 118 })
154 119
155 it('Should be able to login with an insensitive username', async function () { 120 it('Should be able to login with an insensitive username', async function () {
156 const user = { username: 'RoOt', password: server.user.password } 121 const user = { username: 'RoOt', password: server.store.user.password }
157 await login(server.url, server.client, user, HttpStatusCode.OK_200) 122 await server.login.login({ user, expectedStatus: HttpStatusCode.OK_200 })
158 123
159 const user2 = { username: 'rOoT', password: server.user.password } 124 const user2 = { username: 'rOoT', password: server.store.user.password }
160 await login(server.url, server.client, user2, HttpStatusCode.OK_200) 125 await server.login.login({ user: user2, expectedStatus: HttpStatusCode.OK_200 })
161 126
162 const user3 = { username: 'ROOt', password: server.user.password } 127 const user3 = { username: 'ROOt', password: server.store.user.password }
163 await login(server.url, server.client, user3, HttpStatusCode.OK_200) 128 await server.login.login({ user: user3, expectedStatus: HttpStatusCode.OK_200 })
164 }) 129 })
165 }) 130 })
166 131
167 describe('Upload', function () { 132 describe('Upload', function () {
168 133
169 it('Should upload the video with the correct token', async function () { 134 it('Should upload the video with the correct token', async function () {
170 const videoAttributes = {} 135 await server.videos.upload({ token })
171 await uploadVideo(server.url, accessToken, videoAttributes) 136 const { data } = await server.videos.list()
172 const res = await getVideosList(server.url) 137 const video = data[0]
173 const video = res.body.data[0]
174 138
175 expect(video.account.name).to.equal('root') 139 expect(video.account.name).to.equal('root')
176 videoId = video.id 140 videoId = video.id
177 }) 141 })
178 142
179 it('Should upload the video again with the correct token', async function () { 143 it('Should upload the video again with the correct token', async function () {
180 const videoAttributes = {} 144 await server.videos.upload({ token })
181 await uploadVideo(server.url, accessToken, videoAttributes)
182 }) 145 })
183 }) 146 })
184 147
185 describe('Ratings', function () { 148 describe('Ratings', function () {
186 149
187 it('Should retrieve a video rating', async function () { 150 it('Should retrieve a video rating', async function () {
188 await rateVideo(server.url, accessToken, videoId, 'like') 151 await server.videos.rate({ id: videoId, rating: 'like' })
189 const res = await getMyUserVideoRating(server.url, accessToken, videoId) 152 const rating = await server.users.getMyRating({ token, videoId })
190 const rating = res.body
191 153
192 expect(rating.videoId).to.equal(videoId) 154 expect(rating.videoId).to.equal(videoId)
193 expect(rating.rating).to.equal('like') 155 expect(rating.rating).to.equal('like')
194 }) 156 })
195 157
196 it('Should retrieve ratings list', async function () { 158 it('Should retrieve ratings list', async function () {
197 await rateVideo(server.url, accessToken, videoId, 'like') 159 await server.videos.rate({ id: videoId, rating: 'like' })
198 160
199 const res = await getAccountRatings(server.url, server.user.username, server.accessToken, null, HttpStatusCode.OK_200) 161 const body = await server.accounts.listRatings({ accountName: server.store.user.username })
200 const ratings = res.body
201 162
202 expect(ratings.total).to.equal(1) 163 expect(body.total).to.equal(1)
203 expect(ratings.data[0].video.id).to.equal(videoId) 164 expect(body.data[0].video.id).to.equal(videoId)
204 expect(ratings.data[0].rating).to.equal('like') 165 expect(body.data[0].rating).to.equal('like')
205 }) 166 })
206 167
207 it('Should retrieve ratings list by rating type', async function () { 168 it('Should retrieve ratings list by rating type', async function () {
208 { 169 {
209 const res = await getAccountRatings(server.url, server.user.username, server.accessToken, 'like') 170 const body = await server.accounts.listRatings({ accountName: server.store.user.username, rating: 'like' })
210 const ratings = res.body 171 expect(body.data.length).to.equal(1)
211 expect(ratings.data.length).to.equal(1)
212 } 172 }
213 173
214 { 174 {
215 const res = await getAccountRatings(server.url, server.user.username, server.accessToken, 'dislike') 175 const body = await server.accounts.listRatings({ accountName: server.store.user.username, rating: 'dislike' })
216 const ratings = res.body 176 expect(body.data.length).to.equal(0)
217 expect(ratings.data.length).to.equal(0)
218 } 177 }
219 }) 178 })
220 }) 179 })
221 180
222 describe('Remove video', function () { 181 describe('Remove video', function () {
223 it('Should not be able to remove the video with an incorrect token', async function () { 182 it('Should not be able to remove the video with an incorrect token', async function () {
224 await removeVideo(server.url, 'bad_token', videoId, HttpStatusCode.UNAUTHORIZED_401) 183 await server.videos.remove({ token: 'bad_token', id: videoId, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
225 }) 184 })
226 185
227 it('Should not be able to remove the video with the token of another account') 186 it('Should not be able to remove the video with the token of another account')
228 187
229 it('Should be able to remove the video with the correct token', async function () { 188 it('Should be able to remove the video with the correct token', async function () {
230 await removeVideo(server.url, accessToken, videoId) 189 await server.videos.remove({ token, id: videoId })
231 }) 190 })
232 }) 191 })
233 192
234 describe('Logout', function () { 193 describe('Logout', function () {
235 it('Should logout (revoke token)', async function () { 194 it('Should logout (revoke token)', async function () {
236 await logout(server.url, server.accessToken) 195 await server.login.logout({ token: server.accessToken })
237 }) 196 })
238 197
239 it('Should not be able to get the user information', async function () { 198 it('Should not be able to get the user information', async function () {
240 await getMyUserInformation(server.url, server.accessToken, HttpStatusCode.UNAUTHORIZED_401) 199 await server.users.getMyInfo({ expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
241 }) 200 })
242 201
243 it('Should not be able to upload a video', async function () { 202 it('Should not be able to upload a video', async function () {
244 await uploadVideo(server.url, server.accessToken, { name: 'video' }, HttpStatusCode.UNAUTHORIZED_401) 203 await server.videos.upload({ attributes: { name: 'video' }, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
245 }) 204 })
246 205
247 it('Should not be able to rate a video', async function () { 206 it('Should not be able to rate a video', async function () {
@@ -255,79 +214,70 @@ describe('Test users', function () {
255 path: path + videoId, 214 path: path + videoId,
256 token: 'wrong token', 215 token: 'wrong token',
257 fields: data, 216 fields: data,
258 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 217 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
259 } 218 }
260 await makePutBodyRequest(options) 219 await makePutBodyRequest(options)
261 }) 220 })
262 221
263 it('Should be able to login again', async function () { 222 it('Should be able to login again', async function () {
264 const res = await login(server.url, server.client, server.user) 223 const body = await server.login.login()
265 server.accessToken = res.body.access_token 224 server.accessToken = body.access_token
266 server.refreshToken = res.body.refresh_token 225 server.refreshToken = body.refresh_token
267 }) 226 })
268 227
269 it('Should be able to get my user information again', async function () { 228 it('Should be able to get my user information again', async function () {
270 await getMyUserInformation(server.url, server.accessToken) 229 await server.users.getMyInfo()
271 }) 230 })
272 231
273 it('Should have an expired access token', async function () { 232 it('Should have an expired access token', async function () {
274 this.timeout(15000) 233 this.timeout(15000)
275 234
276 await setTokenField(server.internalServerNumber, server.accessToken, 'accessTokenExpiresAt', new Date().toISOString()) 235 await server.sql.setTokenField(server.accessToken, 'accessTokenExpiresAt', new Date().toISOString())
277 await setTokenField(server.internalServerNumber, server.accessToken, 'refreshTokenExpiresAt', new Date().toISOString()) 236 await server.sql.setTokenField(server.accessToken, 'refreshTokenExpiresAt', new Date().toISOString())
278 237
279 killallServers([ server ]) 238 await killallServers([ server ])
280 await reRunServer(server) 239 await server.run()
281 240
282 await getMyUserInformation(server.url, server.accessToken, 401) 241 await server.users.getMyInfo({ expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
283 }) 242 })
284 243
285 it('Should not be able to refresh an access token with an expired refresh token', async function () { 244 it('Should not be able to refresh an access token with an expired refresh token', async function () {
286 await refreshToken(server, server.refreshToken, 400) 245 await server.login.refreshToken({ refreshToken: server.refreshToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
287 }) 246 })
288 247
289 it('Should refresh the token', async function () { 248 it('Should refresh the token', async function () {
290 this.timeout(15000) 249 this.timeout(15000)
291 250
292 const futureDate = new Date(new Date().getTime() + 1000 * 60).toISOString() 251 const futureDate = new Date(new Date().getTime() + 1000 * 60).toISOString()
293 await setTokenField(server.internalServerNumber, server.accessToken, 'refreshTokenExpiresAt', futureDate) 252 await server.sql.setTokenField(server.accessToken, 'refreshTokenExpiresAt', futureDate)
294 253
295 killallServers([ server ]) 254 await killallServers([ server ])
296 await reRunServer(server) 255 await server.run()
297 256
298 const res = await refreshToken(server, server.refreshToken) 257 const res = await server.login.refreshToken({ refreshToken: server.refreshToken })
299 server.accessToken = res.body.access_token 258 server.accessToken = res.body.access_token
300 server.refreshToken = res.body.refresh_token 259 server.refreshToken = res.body.refresh_token
301 }) 260 })
302 261
303 it('Should be able to get my user information again', async function () { 262 it('Should be able to get my user information again', async function () {
304 await getMyUserInformation(server.url, server.accessToken) 263 await server.users.getMyInfo()
305 }) 264 })
306 }) 265 })
307 266
308 describe('Creating a user', function () { 267 describe('Creating a user', function () {
309 268
310 it('Should be able to create a new user', async function () { 269 it('Should be able to create a new user', async function () {
311 await createUser({ 270 await server.users.create({ ...user, videoQuota: 2 * 1024 * 1024, adminFlags: UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST })
312 url: server.url,
313 accessToken: accessToken,
314 username: user.username,
315 password: user.password,
316 videoQuota: 2 * 1024 * 1024,
317 adminFlags: UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST
318 })
319 }) 271 })
320 272
321 it('Should be able to login with this user', async function () { 273 it('Should be able to login with this user', async function () {
322 accessTokenUser = await userLogin(server, user) 274 userToken = await server.login.getAccessToken(user)
323 }) 275 })
324 276
325 it('Should be able to get user information', async function () { 277 it('Should be able to get user information', async function () {
326 const res1 = await getMyUserInformation(server.url, accessTokenUser) 278 const userMe = await server.users.getMyInfo({ token: userToken })
327 const userMe: MyUser = res1.body
328 279
329 const res2 = await getUserInformation(server.url, server.accessToken, userMe.id, true) 280 const userGet = await server.users.get({ userId: userMe.id, withStats: true })
330 const userGet: User = res2.body
331 281
332 for (const user of [ userMe, userGet ]) { 282 for (const user of [ userMe, userGet ]) {
333 expect(user.username).to.equal('user_1') 283 expect(user.username).to.equal('user_1')
@@ -363,34 +313,28 @@ describe('Test users', function () {
363 it('Should be able to upload a video with this user', async function () { 313 it('Should be able to upload a video with this user', async function () {
364 this.timeout(10000) 314 this.timeout(10000)
365 315
366 const videoAttributes = { 316 const attributes = {
367 name: 'super user video', 317 name: 'super user video',
368 fixture: 'video_short.webm' 318 fixture: 'video_short.webm'
369 } 319 }
370 await uploadVideo(server.url, accessTokenUser, videoAttributes) 320 await server.videos.upload({ token: userToken, attributes })
371 }) 321 })
372 322
373 it('Should have video quota updated', async function () { 323 it('Should have video quota updated', async function () {
374 const res = await getMyUserVideoQuotaUsed(server.url, accessTokenUser) 324 const quota = await server.users.getMyQuotaUsed({ token: userToken })
375 const data = res.body 325 expect(quota.videoQuotaUsed).to.equal(218910)
376
377 expect(data.videoQuotaUsed).to.equal(218910)
378
379 const resUsers = await getUsersList(server.url, server.accessToken)
380 326
381 const users: User[] = resUsers.body.data 327 const { data } = await server.users.list()
382 const tmpUser = users.find(u => u.username === user.username) 328 const tmpUser = data.find(u => u.username === user.username)
383 expect(tmpUser.videoQuotaUsed).to.equal(218910) 329 expect(tmpUser.videoQuotaUsed).to.equal(218910)
384 }) 330 })
385 331
386 it('Should be able to list my videos', async function () { 332 it('Should be able to list my videos', async function () {
387 const res = await getMyVideos(server.url, accessTokenUser, 0, 5) 333 const { total, data } = await server.videos.listMyVideos({ token: userToken })
388 expect(res.body.total).to.equal(1) 334 expect(total).to.equal(1)
335 expect(data).to.have.lengthOf(1)
389 336
390 const videos = res.body.data 337 const video: Video = data[0]
391 expect(videos).to.have.lengthOf(1)
392
393 const video: Video = videos[0]
394 expect(video.name).to.equal('super user video') 338 expect(video.name).to.equal('super user video')
395 expect(video.thumbnailPath).to.not.be.null 339 expect(video.thumbnailPath).to.not.be.null
396 expect(video.previewPath).to.not.be.null 340 expect(video.previewPath).to.not.be.null
@@ -398,19 +342,15 @@ describe('Test users', function () {
398 342
399 it('Should be able to search in my videos', async function () { 343 it('Should be able to search in my videos', async function () {
400 { 344 {
401 const res = await getMyVideos(server.url, accessTokenUser, 0, 5, '-createdAt', 'user video') 345 const { total, data } = await server.videos.listMyVideos({ token: userToken, sort: '-createdAt', search: 'user video' })
402 expect(res.body.total).to.equal(1) 346 expect(total).to.equal(1)
403 347 expect(data).to.have.lengthOf(1)
404 const videos = res.body.data
405 expect(videos).to.have.lengthOf(1)
406 } 348 }
407 349
408 { 350 {
409 const res = await getMyVideos(server.url, accessTokenUser, 0, 5, '-createdAt', 'toto') 351 const { total, data } = await server.videos.listMyVideos({ token: userToken, sort: '-createdAt', search: 'toto' })
410 expect(res.body.total).to.equal(0) 352 expect(total).to.equal(0)
411 353 expect(data).to.have.lengthOf(0)
412 const videos = res.body.data
413 expect(videos).to.have.lengthOf(0)
414 } 354 }
415 }) 355 })
416 356
@@ -418,28 +358,25 @@ describe('Test users', function () {
418 this.timeout(60000) 358 this.timeout(60000)
419 359
420 { 360 {
421 const res = await getCustomConfig(server.url, server.accessToken) 361 const config = await server.config.getCustomConfig()
422 const config = res.body as CustomConfig
423 config.transcoding.webtorrent.enabled = false 362 config.transcoding.webtorrent.enabled = false
424 config.transcoding.hls.enabled = true 363 config.transcoding.hls.enabled = true
425 config.transcoding.enabled = true 364 config.transcoding.enabled = true
426 await updateCustomSubConfig(server.url, server.accessToken, config) 365 await server.config.updateCustomSubConfig({ newConfig: config })
427 } 366 }
428 367
429 { 368 {
430 const videoAttributes = { 369 const attributes = {
431 name: 'super user video 2', 370 name: 'super user video 2',
432 fixture: 'video_short.webm' 371 fixture: 'video_short.webm'
433 } 372 }
434 await uploadVideo(server.url, accessTokenUser, videoAttributes) 373 await server.videos.upload({ token: userToken, attributes })
435 374
436 await waitJobs([ server ]) 375 await waitJobs([ server ])
437 } 376 }
438 377
439 { 378 {
440 const res = await getMyUserVideoQuotaUsed(server.url, accessTokenUser) 379 const data = await server.users.getMyQuotaUsed({ token: userToken })
441 const data = res.body
442
443 expect(data.videoQuotaUsed).to.be.greaterThan(220000) 380 expect(data.videoQuotaUsed).to.be.greaterThan(220000)
444 } 381 }
445 }) 382 })
@@ -448,21 +385,18 @@ describe('Test users', function () {
448 describe('Users listing', function () { 385 describe('Users listing', function () {
449 386
450 it('Should list all the users', async function () { 387 it('Should list all the users', async function () {
451 const res = await getUsersList(server.url, server.accessToken) 388 const { data, total } = await server.users.list()
452 const result = res.body
453 const total = result.total
454 const users = result.data
455 389
456 expect(total).to.equal(2) 390 expect(total).to.equal(2)
457 expect(users).to.be.an('array') 391 expect(data).to.be.an('array')
458 expect(users.length).to.equal(2) 392 expect(data.length).to.equal(2)
459 393
460 const user = users[0] 394 const user = data[0]
461 expect(user.username).to.equal('user_1') 395 expect(user.username).to.equal('user_1')
462 expect(user.email).to.equal('user_1@example.com') 396 expect(user.email).to.equal('user_1@example.com')
463 expect(user.nsfwPolicy).to.equal('display') 397 expect(user.nsfwPolicy).to.equal('display')
464 398
465 const rootUser = users[1] 399 const rootUser = data[1]
466 expect(rootUser.username).to.equal('root') 400 expect(rootUser.username).to.equal('root')
467 expect(rootUser.email).to.equal('admin' + server.internalServerNumber + '@example.com') 401 expect(rootUser.email).to.equal('admin' + server.internalServerNumber + '@example.com')
468 expect(user.nsfwPolicy).to.equal('display') 402 expect(user.nsfwPolicy).to.equal('display')
@@ -474,16 +408,12 @@ describe('Test users', function () {
474 }) 408 })
475 409
476 it('Should list only the first user by username asc', async function () { 410 it('Should list only the first user by username asc', async function () {
477 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 1, 'username') 411 const { total, data } = await server.users.list({ start: 0, count: 1, sort: 'username' })
478
479 const result = res.body
480 const total = result.total
481 const users = result.data
482 412
483 expect(total).to.equal(2) 413 expect(total).to.equal(2)
484 expect(users.length).to.equal(1) 414 expect(data.length).to.equal(1)
485 415
486 const user = users[0] 416 const user = data[0]
487 expect(user.username).to.equal('root') 417 expect(user.username).to.equal('root')
488 expect(user.email).to.equal('admin' + server.internalServerNumber + '@example.com') 418 expect(user.email).to.equal('admin' + server.internalServerNumber + '@example.com')
489 expect(user.roleLabel).to.equal('Administrator') 419 expect(user.roleLabel).to.equal('Administrator')
@@ -491,111 +421,90 @@ describe('Test users', function () {
491 }) 421 })
492 422
493 it('Should list only the first user by username desc', async function () { 423 it('Should list only the first user by username desc', async function () {
494 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 1, '-username') 424 const { total, data } = await server.users.list({ start: 0, count: 1, sort: '-username' })
495 const result = res.body
496 const total = result.total
497 const users = result.data
498 425
499 expect(total).to.equal(2) 426 expect(total).to.equal(2)
500 expect(users.length).to.equal(1) 427 expect(data.length).to.equal(1)
501 428
502 const user = users[0] 429 const user = data[0]
503 expect(user.username).to.equal('user_1') 430 expect(user.username).to.equal('user_1')
504 expect(user.email).to.equal('user_1@example.com') 431 expect(user.email).to.equal('user_1@example.com')
505 expect(user.nsfwPolicy).to.equal('display') 432 expect(user.nsfwPolicy).to.equal('display')
506 }) 433 })
507 434
508 it('Should list only the second user by createdAt desc', async function () { 435 it('Should list only the second user by createdAt desc', async function () {
509 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 1, '-createdAt') 436 const { data, total } = await server.users.list({ start: 0, count: 1, sort: '-createdAt' })
510 const result = res.body
511 const total = result.total
512 const users = result.data
513
514 expect(total).to.equal(2) 437 expect(total).to.equal(2)
515 expect(users.length).to.equal(1)
516 438
517 const user = users[0] 439 expect(data.length).to.equal(1)
440
441 const user = data[0]
518 expect(user.username).to.equal('user_1') 442 expect(user.username).to.equal('user_1')
519 expect(user.email).to.equal('user_1@example.com') 443 expect(user.email).to.equal('user_1@example.com')
520 expect(user.nsfwPolicy).to.equal('display') 444 expect(user.nsfwPolicy).to.equal('display')
521 }) 445 })
522 446
523 it('Should list all the users by createdAt asc', async function () { 447 it('Should list all the users by createdAt asc', async function () {
524 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt') 448 const { data, total } = await server.users.list({ start: 0, count: 2, sort: 'createdAt' })
525 const result = res.body
526 const total = result.total
527 const users = result.data
528 449
529 expect(total).to.equal(2) 450 expect(total).to.equal(2)
530 expect(users.length).to.equal(2) 451 expect(data.length).to.equal(2)
531 452
532 expect(users[0].username).to.equal('root') 453 expect(data[0].username).to.equal('root')
533 expect(users[0].email).to.equal('admin' + server.internalServerNumber + '@example.com') 454 expect(data[0].email).to.equal('admin' + server.internalServerNumber + '@example.com')
534 expect(users[0].nsfwPolicy).to.equal('display') 455 expect(data[0].nsfwPolicy).to.equal('display')
535 456
536 expect(users[1].username).to.equal('user_1') 457 expect(data[1].username).to.equal('user_1')
537 expect(users[1].email).to.equal('user_1@example.com') 458 expect(data[1].email).to.equal('user_1@example.com')
538 expect(users[1].nsfwPolicy).to.equal('display') 459 expect(data[1].nsfwPolicy).to.equal('display')
539 }) 460 })
540 461
541 it('Should search user by username', async function () { 462 it('Should search user by username', async function () {
542 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', 'oot') 463 const { data, total } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', search: 'oot' })
543 const users = res.body.data as User[] 464 expect(total).to.equal(1)
544 465 expect(data.length).to.equal(1)
545 expect(res.body.total).to.equal(1) 466 expect(data[0].username).to.equal('root')
546 expect(users.length).to.equal(1)
547
548 expect(users[0].username).to.equal('root')
549 }) 467 })
550 468
551 it('Should search user by email', async function () { 469 it('Should search user by email', async function () {
552 { 470 {
553 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', 'r_1@exam') 471 const { total, data } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', search: 'r_1@exam' })
554 const users = res.body.data as User[] 472 expect(total).to.equal(1)
555 473 expect(data.length).to.equal(1)
556 expect(res.body.total).to.equal(1) 474 expect(data[0].username).to.equal('user_1')
557 expect(users.length).to.equal(1) 475 expect(data[0].email).to.equal('user_1@example.com')
558
559 expect(users[0].username).to.equal('user_1')
560 expect(users[0].email).to.equal('user_1@example.com')
561 } 476 }
562 477
563 { 478 {
564 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', 'example') 479 const { total, data } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', search: 'example' })
565 const users = res.body.data as User[] 480 expect(total).to.equal(2)
566 481 expect(data.length).to.equal(2)
567 expect(res.body.total).to.equal(2) 482 expect(data[0].username).to.equal('root')
568 expect(users.length).to.equal(2) 483 expect(data[1].username).to.equal('user_1')
569
570 expect(users[0].username).to.equal('root')
571 expect(users[1].username).to.equal('user_1')
572 } 484 }
573 }) 485 })
574 }) 486 })
575 487
576 describe('Update my account', function () { 488 describe('Update my account', function () {
489
577 it('Should update my password', async function () { 490 it('Should update my password', async function () {
578 await updateMyUser({ 491 await server.users.updateMe({
579 url: server.url, 492 token: userToken,
580 accessToken: accessTokenUser,
581 currentPassword: 'super password', 493 currentPassword: 'super password',
582 password: 'new password' 494 password: 'new password'
583 }) 495 })
584 user.password = 'new password' 496 user.password = 'new password'
585 497
586 await userLogin(server, user, HttpStatusCode.OK_200) 498 await server.login.login({ user })
587 }) 499 })
588 500
589 it('Should be able to change the NSFW display attribute', async function () { 501 it('Should be able to change the NSFW display attribute', async function () {
590 await updateMyUser({ 502 await server.users.updateMe({
591 url: server.url, 503 token: userToken,
592 accessToken: accessTokenUser,
593 nsfwPolicy: 'do_not_list' 504 nsfwPolicy: 'do_not_list'
594 }) 505 })
595 506
596 const res = await getMyUserInformation(server.url, accessTokenUser) 507 const user = await server.users.getMyInfo({ token: userToken })
597 const user = res.body
598
599 expect(user.username).to.equal('user_1') 508 expect(user.username).to.equal('user_1')
600 expect(user.email).to.equal('user_1@example.com') 509 expect(user.email).to.equal('user_1@example.com')
601 expect(user.nsfwPolicy).to.equal('do_not_list') 510 expect(user.nsfwPolicy).to.equal('do_not_list')
@@ -606,42 +515,33 @@ describe('Test users', function () {
606 }) 515 })
607 516
608 it('Should be able to change the autoPlayVideo attribute', async function () { 517 it('Should be able to change the autoPlayVideo attribute', async function () {
609 await updateMyUser({ 518 await server.users.updateMe({
610 url: server.url, 519 token: userToken,
611 accessToken: accessTokenUser,
612 autoPlayVideo: false 520 autoPlayVideo: false
613 }) 521 })
614 522
615 const res = await getMyUserInformation(server.url, accessTokenUser) 523 const user = await server.users.getMyInfo({ token: userToken })
616 const user = res.body
617
618 expect(user.autoPlayVideo).to.be.false 524 expect(user.autoPlayVideo).to.be.false
619 }) 525 })
620 526
621 it('Should be able to change the autoPlayNextVideo attribute', async function () { 527 it('Should be able to change the autoPlayNextVideo attribute', async function () {
622 await updateMyUser({ 528 await server.users.updateMe({
623 url: server.url, 529 token: userToken,
624 accessToken: accessTokenUser,
625 autoPlayNextVideo: true 530 autoPlayNextVideo: true
626 }) 531 })
627 532
628 const res = await getMyUserInformation(server.url, accessTokenUser) 533 const user = await server.users.getMyInfo({ token: userToken })
629 const user = res.body
630
631 expect(user.autoPlayNextVideo).to.be.true 534 expect(user.autoPlayNextVideo).to.be.true
632 }) 535 })
633 536
634 it('Should be able to change the email attribute', async function () { 537 it('Should be able to change the email attribute', async function () {
635 await updateMyUser({ 538 await server.users.updateMe({
636 url: server.url, 539 token: userToken,
637 accessToken: accessTokenUser,
638 currentPassword: 'new password', 540 currentPassword: 'new password',
639 email: 'updated@example.com' 541 email: 'updated@example.com'
640 }) 542 })
641 543
642 const res = await getMyUserInformation(server.url, accessTokenUser) 544 const user = await server.users.getMyInfo({ token: userToken })
643 const user = res.body
644
645 expect(user.username).to.equal('user_1') 545 expect(user.username).to.equal('user_1')
646 expect(user.email).to.equal('updated@example.com') 546 expect(user.email).to.equal('updated@example.com')
647 expect(user.nsfwPolicy).to.equal('do_not_list') 547 expect(user.nsfwPolicy).to.equal('do_not_list')
@@ -654,15 +554,9 @@ describe('Test users', function () {
654 it('Should be able to update my avatar with a gif', async function () { 554 it('Should be able to update my avatar with a gif', async function () {
655 const fixture = 'avatar.gif' 555 const fixture = 'avatar.gif'
656 556
657 await updateMyAvatar({ 557 await server.users.updateMyAvatar({ token: userToken, fixture })
658 url: server.url,
659 accessToken: accessTokenUser,
660 fixture
661 })
662
663 const res = await getMyUserInformation(server.url, accessTokenUser)
664 const user = res.body
665 558
559 const user = await server.users.getMyInfo({ token: userToken })
666 await testImage(server.url, 'avatar-resized', user.account.avatar.path, '.gif') 560 await testImage(server.url, 'avatar-resized', user.account.avatar.path, '.gif')
667 }) 561 })
668 562
@@ -670,29 +564,17 @@ describe('Test users', function () {
670 for (const extension of [ '.png', '.gif' ]) { 564 for (const extension of [ '.png', '.gif' ]) {
671 const fixture = 'avatar' + extension 565 const fixture = 'avatar' + extension
672 566
673 await updateMyAvatar({ 567 await server.users.updateMyAvatar({ token: userToken, fixture })
674 url: server.url,
675 accessToken: accessTokenUser,
676 fixture
677 })
678
679 const res = await getMyUserInformation(server.url, accessTokenUser)
680 const user = res.body
681 568
569 const user = await server.users.getMyInfo({ token: userToken })
682 await testImage(server.url, 'avatar-resized', user.account.avatar.path, extension) 570 await testImage(server.url, 'avatar-resized', user.account.avatar.path, extension)
683 } 571 }
684 }) 572 })
685 573
686 it('Should be able to update my display name', async function () { 574 it('Should be able to update my display name', async function () {
687 await updateMyUser({ 575 await server.users.updateMe({ token: userToken, displayName: 'new display name' })
688 url: server.url,
689 accessToken: accessTokenUser,
690 displayName: 'new display name'
691 })
692
693 const res = await getMyUserInformation(server.url, accessTokenUser)
694 const user = res.body
695 576
577 const user = await server.users.getMyInfo({ token: userToken })
696 expect(user.username).to.equal('user_1') 578 expect(user.username).to.equal('user_1')
697 expect(user.email).to.equal('updated@example.com') 579 expect(user.email).to.equal('updated@example.com')
698 expect(user.nsfwPolicy).to.equal('do_not_list') 580 expect(user.nsfwPolicy).to.equal('do_not_list')
@@ -703,15 +585,9 @@ describe('Test users', function () {
703 }) 585 })
704 586
705 it('Should be able to update my description', async function () { 587 it('Should be able to update my description', async function () {
706 await updateMyUser({ 588 await server.users.updateMe({ token: userToken, description: 'my super description updated' })
707 url: server.url,
708 accessToken: accessTokenUser,
709 description: 'my super description updated'
710 })
711
712 const res = await getMyUserInformation(server.url, accessTokenUser)
713 const user: User = res.body
714 589
590 const user = await server.users.getMyInfo({ token: userToken })
715 expect(user.username).to.equal('user_1') 591 expect(user.username).to.equal('user_1')
716 expect(user.email).to.equal('updated@example.com') 592 expect(user.email).to.equal('updated@example.com')
717 expect(user.nsfwPolicy).to.equal('do_not_list') 593 expect(user.nsfwPolicy).to.equal('do_not_list')
@@ -725,30 +601,21 @@ describe('Test users', function () {
725 601
726 it('Should be able to update my theme', async function () { 602 it('Should be able to update my theme', async function () {
727 for (const theme of [ 'background-red', 'default', 'instance-default' ]) { 603 for (const theme of [ 'background-red', 'default', 'instance-default' ]) {
728 await updateMyUser({ 604 await server.users.updateMe({ token: userToken, theme })
729 url: server.url,
730 accessToken: accessTokenUser,
731 theme
732 })
733 605
734 const res = await getMyUserInformation(server.url, accessTokenUser) 606 const user = await server.users.getMyInfo({ token: userToken })
735 const body: User = res.body 607 expect(user.theme).to.equal(theme)
736
737 expect(body.theme).to.equal(theme)
738 } 608 }
739 }) 609 })
740 610
741 it('Should be able to update my modal preferences', async function () { 611 it('Should be able to update my modal preferences', async function () {
742 await updateMyUser({ 612 await server.users.updateMe({
743 url: server.url, 613 token: userToken,
744 accessToken: accessTokenUser,
745 noInstanceConfigWarningModal: true, 614 noInstanceConfigWarningModal: true,
746 noWelcomeModal: true 615 noWelcomeModal: true
747 }) 616 })
748 617
749 const res = await getMyUserInformation(server.url, accessTokenUser) 618 const user = await server.users.getMyInfo({ token: userToken })
750 const user: User = res.body
751
752 expect(user.noWelcomeModal).to.be.true 619 expect(user.noWelcomeModal).to.be.true
753 expect(user.noInstanceConfigWarningModal).to.be.true 620 expect(user.noInstanceConfigWarningModal).to.be.true
754 }) 621 })
@@ -756,10 +623,9 @@ describe('Test users', function () {
756 623
757 describe('Updating another user', function () { 624 describe('Updating another user', function () {
758 it('Should be able to update another user', async function () { 625 it('Should be able to update another user', async function () {
759 await updateUser({ 626 await server.users.update({
760 url: server.url,
761 userId, 627 userId,
762 accessToken, 628 token,
763 email: 'updated2@example.com', 629 email: 'updated2@example.com',
764 emailVerified: true, 630 emailVerified: true,
765 videoQuota: 42, 631 videoQuota: 42,
@@ -768,8 +634,7 @@ describe('Test users', function () {
768 pluginAuth: 'toto' 634 pluginAuth: 'toto'
769 }) 635 })
770 636
771 const res = await getUserInformation(server.url, accessToken, userId) 637 const user = await server.users.get({ token, userId })
772 const user = res.body as User
773 638
774 expect(user.username).to.equal('user_1') 639 expect(user.username).to.equal('user_1')
775 expect(user.email).to.equal('updated2@example.com') 640 expect(user.email).to.equal('updated2@example.com')
@@ -783,57 +648,50 @@ describe('Test users', function () {
783 }) 648 })
784 649
785 it('Should reset the auth plugin', async function () { 650 it('Should reset the auth plugin', async function () {
786 await updateUser({ url: server.url, userId, accessToken, pluginAuth: null }) 651 await server.users.update({ userId, token, pluginAuth: null })
787 652
788 const res = await getUserInformation(server.url, accessToken, userId) 653 const user = await server.users.get({ token, userId })
789 const user = res.body as User
790 expect(user.pluginAuth).to.be.null 654 expect(user.pluginAuth).to.be.null
791 }) 655 })
792 656
793 it('Should have removed the user token', async function () { 657 it('Should have removed the user token', async function () {
794 await getMyUserVideoQuotaUsed(server.url, accessTokenUser, HttpStatusCode.UNAUTHORIZED_401) 658 await server.users.getMyQuotaUsed({ token: userToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
795 659
796 accessTokenUser = await userLogin(server, user) 660 userToken = await server.login.getAccessToken(user)
797 }) 661 })
798 662
799 it('Should be able to update another user password', async function () { 663 it('Should be able to update another user password', async function () {
800 await updateUser({ 664 await server.users.update({ userId, token, password: 'password updated' })
801 url: server.url,
802 userId,
803 accessToken,
804 password: 'password updated'
805 })
806 665
807 await getMyUserVideoQuotaUsed(server.url, accessTokenUser, HttpStatusCode.UNAUTHORIZED_401) 666 await server.users.getMyQuotaUsed({ token: userToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
808 667
809 await userLogin(server, user, HttpStatusCode.BAD_REQUEST_400) 668 await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
810 669
811 user.password = 'password updated' 670 user.password = 'password updated'
812 accessTokenUser = await userLogin(server, user) 671 userToken = await server.login.getAccessToken(user)
813 }) 672 })
814 }) 673 })
815 674
816 describe('Video blacklists', function () { 675 describe('Video blacklists', function () {
817 it('Should be able to list video blacklist by a moderator', async function () { 676 it('Should be able to list video blacklist by a moderator', async function () {
818 await getBlacklistedVideosList({ url: server.url, token: accessTokenUser }) 677 await server.blacklist.list({ token: userToken })
819 }) 678 })
820 }) 679 })
821 680
822 describe('Remove a user', function () { 681 describe('Remove a user', function () {
823 it('Should be able to remove this user', async function () { 682 it('Should be able to remove this user', async function () {
824 await removeUser(server.url, userId, accessToken) 683 await server.users.remove({ userId, token })
825 }) 684 })
826 685
827 it('Should not be able to login with this user', async function () { 686 it('Should not be able to login with this user', async function () {
828 await userLogin(server, user, HttpStatusCode.BAD_REQUEST_400) 687 await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
829 }) 688 })
830 689
831 it('Should not have videos of this user', async function () { 690 it('Should not have videos of this user', async function () {
832 const res = await getVideosList(server.url) 691 const { data, total } = await server.videos.list()
833 692 expect(total).to.equal(1)
834 expect(res.body.total).to.equal(1)
835 693
836 const video = res.body.data[0] 694 const video = data[0]
837 expect(video.account.name).to.equal('root') 695 expect(video.account.name).to.equal('root')
838 }) 696 })
839 }) 697 })
@@ -845,7 +703,7 @@ describe('Test users', function () {
845 const user = { displayName: 'super user 15', username: 'user_15', password: 'my super password' } 703 const user = { displayName: 'super user 15', username: 'user_15', password: 'my super password' }
846 const channel = { name: 'my_user_15_channel', displayName: 'my channel rocks' } 704 const channel = { name: 'my_user_15_channel', displayName: 'my channel rocks' }
847 705
848 await registerUserWithChannel({ url: server.url, user, channel }) 706 await server.users.register({ ...user, channel })
849 }) 707 })
850 708
851 it('Should be able to login with this registered user', async function () { 709 it('Should be able to login with this registered user', async function () {
@@ -854,40 +712,36 @@ describe('Test users', function () {
854 password: 'my super password' 712 password: 'my super password'
855 } 713 }
856 714
857 user15AccessToken = await userLogin(server, user15) 715 user15AccessToken = await server.login.getAccessToken(user15)
858 }) 716 })
859 717
860 it('Should have the correct display name', async function () { 718 it('Should have the correct display name', async function () {
861 const res = await getMyUserInformation(server.url, user15AccessToken) 719 const user = await server.users.getMyInfo({ token: user15AccessToken })
862 const user: User = res.body
863
864 expect(user.account.displayName).to.equal('super user 15') 720 expect(user.account.displayName).to.equal('super user 15')
865 }) 721 })
866 722
867 it('Should have the correct video quota', async function () { 723 it('Should have the correct video quota', async function () {
868 const res = await getMyUserInformation(server.url, user15AccessToken) 724 const user = await server.users.getMyInfo({ token: user15AccessToken })
869 const user = res.body
870
871 expect(user.videoQuota).to.equal(5 * 1024 * 1024) 725 expect(user.videoQuota).to.equal(5 * 1024 * 1024)
872 }) 726 })
873 727
874 it('Should have created the channel', async function () { 728 it('Should have created the channel', async function () {
875 const res = await getVideoChannel(server.url, 'my_user_15_channel') 729 const { displayName } = await server.channels.get({ channelName: 'my_user_15_channel' })
876 730
877 expect(res.body.displayName).to.equal('my channel rocks') 731 expect(displayName).to.equal('my channel rocks')
878 }) 732 })
879 733
880 it('Should remove me', async function () { 734 it('Should remove me', async function () {
881 { 735 {
882 const res = await getUsersList(server.url, server.accessToken) 736 const { data } = await server.users.list()
883 expect(res.body.data.find(u => u.username === 'user_15')).to.not.be.undefined 737 expect(data.find(u => u.username === 'user_15')).to.not.be.undefined
884 } 738 }
885 739
886 await deleteMe(server.url, user15AccessToken) 740 await server.users.deleteMe({ token: user15AccessToken })
887 741
888 { 742 {
889 const res = await getUsersList(server.url, server.accessToken) 743 const { data } = await server.users.list()
890 expect(res.body.data.find(u => u.username === 'user_15')).to.be.undefined 744 expect(data.find(u => u.username === 'user_15')).to.be.undefined
891 } 745 }
892 }) 746 })
893 }) 747 })
@@ -901,49 +755,40 @@ describe('Test users', function () {
901 } 755 }
902 756
903 it('Should block a user', async function () { 757 it('Should block a user', async function () {
904 const resUser = await createUser({ 758 const user = await server.users.create({ ...user16 })
905 url: server.url, 759 user16Id = user.id
906 accessToken: server.accessToken,
907 username: user16.username,
908 password: user16.password
909 })
910 user16Id = resUser.body.user.id
911 760
912 user16AccessToken = await userLogin(server, user16) 761 user16AccessToken = await server.login.getAccessToken(user16)
913 762
914 await getMyUserInformation(server.url, user16AccessToken, HttpStatusCode.OK_200) 763 await server.users.getMyInfo({ token: user16AccessToken, expectedStatus: HttpStatusCode.OK_200 })
915 await blockUser(server.url, user16Id, server.accessToken) 764 await server.users.banUser({ userId: user16Id })
916 765
917 await getMyUserInformation(server.url, user16AccessToken, HttpStatusCode.UNAUTHORIZED_401) 766 await server.users.getMyInfo({ token: user16AccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
918 await userLogin(server, user16, HttpStatusCode.BAD_REQUEST_400) 767 await server.login.login({ user: user16, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
919 }) 768 })
920 769
921 it('Should search user by banned status', async function () { 770 it('Should search user by banned status', async function () {
922 { 771 {
923 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', undefined, true) 772 const { data, total } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', blocked: true })
924 const users = res.body.data as User[] 773 expect(total).to.equal(1)
774 expect(data.length).to.equal(1)
925 775
926 expect(res.body.total).to.equal(1) 776 expect(data[0].username).to.equal(user16.username)
927 expect(users.length).to.equal(1)
928
929 expect(users[0].username).to.equal(user16.username)
930 } 777 }
931 778
932 { 779 {
933 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', undefined, false) 780 const { data, total } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', blocked: false })
934 const users = res.body.data as User[] 781 expect(total).to.equal(1)
935 782 expect(data.length).to.equal(1)
936 expect(res.body.total).to.equal(1)
937 expect(users.length).to.equal(1)
938 783
939 expect(users[0].username).to.not.equal(user16.username) 784 expect(data[0].username).to.not.equal(user16.username)
940 } 785 }
941 }) 786 })
942 787
943 it('Should unblock a user', async function () { 788 it('Should unblock a user', async function () {
944 await unblockUser(server.url, user16Id, server.accessToken) 789 await server.users.unbanUser({ userId: user16Id })
945 user16AccessToken = await userLogin(server, user16) 790 user16AccessToken = await server.login.getAccessToken(user16)
946 await getMyUserInformation(server.url, user16AccessToken, HttpStatusCode.OK_200) 791 await server.users.getMyInfo({ token: user16AccessToken, expectedStatus: HttpStatusCode.OK_200 })
947 }) 792 })
948 }) 793 })
949 794
@@ -956,19 +801,12 @@ describe('Test users', function () {
956 username: 'user_17', 801 username: 'user_17',
957 password: 'my super password' 802 password: 'my super password'
958 } 803 }
959 const resUser = await createUser({ 804 const created = await server.users.create({ ...user17 })
960 url: server.url,
961 accessToken: server.accessToken,
962 username: user17.username,
963 password: user17.password
964 })
965 805
966 user17Id = resUser.body.user.id 806 user17Id = created.id
967 user17AccessToken = await userLogin(server, user17) 807 user17AccessToken = await server.login.getAccessToken(user17)
968
969 const res = await getUserInformation(server.url, server.accessToken, user17Id, true)
970 const user: User = res.body
971 808
809 const user = await server.users.get({ userId: user17Id, withStats: true })
972 expect(user.videosCount).to.equal(0) 810 expect(user.videosCount).to.equal(0)
973 expect(user.videoCommentsCount).to.equal(0) 811 expect(user.videoCommentsCount).to.equal(0)
974 expect(user.abusesCount).to.equal(0) 812 expect(user.abusesCount).to.equal(0)
@@ -977,54 +815,43 @@ describe('Test users', function () {
977 }) 815 })
978 816
979 it('Should report correct videos count', async function () { 817 it('Should report correct videos count', async function () {
980 const videoAttributes = { 818 const attributes = { name: 'video to test user stats' }
981 name: 'video to test user stats' 819 await server.videos.upload({ token: user17AccessToken, attributes })
982 }
983 await uploadVideo(server.url, user17AccessToken, videoAttributes)
984 const res1 = await getVideosList(server.url)
985 videoId = res1.body.data.find(video => video.name === videoAttributes.name).id
986 820
987 const res2 = await getUserInformation(server.url, server.accessToken, user17Id, true) 821 const { data } = await server.videos.list()
988 const user: User = res2.body 822 videoId = data.find(video => video.name === attributes.name).id
989 823
824 const user = await server.users.get({ userId: user17Id, withStats: true })
990 expect(user.videosCount).to.equal(1) 825 expect(user.videosCount).to.equal(1)
991 }) 826 })
992 827
993 it('Should report correct video comments for user', async function () { 828 it('Should report correct video comments for user', async function () {
994 const text = 'super comment' 829 const text = 'super comment'
995 await addVideoCommentThread(server.url, user17AccessToken, videoId, text) 830 await server.comments.createThread({ token: user17AccessToken, videoId, text })
996
997 const res = await getUserInformation(server.url, server.accessToken, user17Id, true)
998 const user: User = res.body
999 831
832 const user = await server.users.get({ userId: user17Id, withStats: true })
1000 expect(user.videoCommentsCount).to.equal(1) 833 expect(user.videoCommentsCount).to.equal(1)
1001 }) 834 })
1002 835
1003 it('Should report correct abuses counts', async function () { 836 it('Should report correct abuses counts', async function () {
1004 const reason = 'my super bad reason' 837 const reason = 'my super bad reason'
1005 await reportAbuse({ url: server.url, token: user17AccessToken, videoId, reason }) 838 await server.abuses.report({ token: user17AccessToken, videoId, reason })
1006
1007 const res1 = await getAdminAbusesList({ url: server.url, token: server.accessToken })
1008 const abuseId = res1.body.data[0].id
1009 839
1010 const res2 = await getUserInformation(server.url, server.accessToken, user17Id, true) 840 const body1 = await server.abuses.getAdminList()
1011 const user2: User = res2.body 841 const abuseId = body1.data[0].id
1012 842
843 const user2 = await server.users.get({ userId: user17Id, withStats: true })
1013 expect(user2.abusesCount).to.equal(1) // number of incriminations 844 expect(user2.abusesCount).to.equal(1) // number of incriminations
1014 expect(user2.abusesCreatedCount).to.equal(1) // number of reports created 845 expect(user2.abusesCreatedCount).to.equal(1) // number of reports created
1015 846
1016 const body: AbuseUpdate = { state: AbuseState.ACCEPTED } 847 await server.abuses.update({ abuseId, body: { state: AbuseState.ACCEPTED } })
1017 await updateAbuse(server.url, server.accessToken, abuseId, body)
1018
1019 const res3 = await getUserInformation(server.url, server.accessToken, user17Id, true)
1020 const user3: User = res3.body
1021 848
849 const user3 = await server.users.get({ userId: user17Id, withStats: true })
1022 expect(user3.abusesAcceptedCount).to.equal(1) // number of reports created accepted 850 expect(user3.abusesAcceptedCount).to.equal(1) // number of reports created accepted
1023 }) 851 })
1024 }) 852 })
1025 853
1026 after(async function () { 854 after(async function () {
1027 await closeAllSequelize([ server ])
1028 await cleanupTests([ server ]) 855 await cleanupTests([ server ])
1029 }) 856 })
1030}) 857})
diff --git a/server/tests/api/videos/audio-only.ts b/server/tests/api/videos/audio-only.ts
index 7ddbd5cd9..7fac6e738 100644
--- a/server/tests/api/videos/audio-only.ts
+++ b/server/tests/api/videos/audio-only.ts
@@ -2,26 +2,16 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { join } from 'path'
6import { getAudioStream, getVideoStreamSize } from '@server/helpers/ffprobe-utils' 5import { getAudioStream, getVideoStreamSize } from '@server/helpers/ffprobe-utils'
7import { 6import { cleanupTests, createMultipleServers, doubleFollow, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
8 buildServerDirectory,
9 cleanupTests,
10 doubleFollow,
11 flushAndRunMultipleServers,
12 getVideo,
13 ServerInfo,
14 setAccessTokensToServers,
15 uploadVideo,
16 waitJobs
17} from '../../../../shared/extra-utils'
18import { VideoDetails } from '../../../../shared/models/videos'
19 7
20const expect = chai.expect 8const expect = chai.expect
21 9
22describe('Test audio only video transcoding', function () { 10describe('Test audio only video transcoding', function () {
23 let servers: ServerInfo[] = [] 11 let servers: PeerTubeServer[] = []
24 let videoUUID: string 12 let videoUUID: string
13 let webtorrentAudioFileUrl: string
14 let fragmentedAudioFileUrl: string
25 15
26 before(async function () { 16 before(async function () {
27 this.timeout(120000) 17 this.timeout(120000)
@@ -47,7 +37,7 @@ describe('Test audio only video transcoding', function () {
47 } 37 }
48 } 38 }
49 } 39 }
50 servers = await flushAndRunMultipleServers(2, configOverride) 40 servers = await createMultipleServers(2, configOverride)
51 41
52 // Get the access tokens 42 // Get the access tokens
53 await setAccessTokensToServers(servers) 43 await setAccessTokensToServers(servers)
@@ -59,15 +49,13 @@ describe('Test audio only video transcoding', function () {
59 it('Should upload a video and transcode it', async function () { 49 it('Should upload a video and transcode it', async function () {
60 this.timeout(120000) 50 this.timeout(120000)
61 51
62 const resUpload = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'audio only' }) 52 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'audio only' } })
63 videoUUID = resUpload.body.video.uuid 53 videoUUID = uuid
64 54
65 await waitJobs(servers) 55 await waitJobs(servers)
66 56
67 for (const server of servers) { 57 for (const server of servers) {
68 const res = await getVideo(server.url, videoUUID) 58 const video = await server.videos.get({ id: videoUUID })
69 const video: VideoDetails = res.body
70
71 expect(video.streamingPlaylists).to.have.lengthOf(1) 59 expect(video.streamingPlaylists).to.have.lengthOf(1)
72 60
73 for (const files of [ video.files, video.streamingPlaylists[0].files ]) { 61 for (const files of [ video.files, video.streamingPlaylists[0].files ]) {
@@ -76,13 +64,18 @@ describe('Test audio only video transcoding', function () {
76 expect(files[1].resolution.id).to.equal(240) 64 expect(files[1].resolution.id).to.equal(240)
77 expect(files[2].resolution.id).to.equal(0) 65 expect(files[2].resolution.id).to.equal(0)
78 } 66 }
67
68 if (server.serverNumber === 1) {
69 webtorrentAudioFileUrl = video.files[2].fileUrl
70 fragmentedAudioFileUrl = video.streamingPlaylists[0].files[2].fileUrl
71 }
79 } 72 }
80 }) 73 })
81 74
82 it('0p transcoded video should not have video', async function () { 75 it('0p transcoded video should not have video', async function () {
83 const paths = [ 76 const paths = [
84 buildServerDirectory(servers[0], join('videos', videoUUID + '-0.mp4')), 77 servers[0].servers.buildWebTorrentFilePath(webtorrentAudioFileUrl),
85 buildServerDirectory(servers[0], join('streaming-playlists', 'hls', videoUUID, videoUUID + '-0-fragmented.mp4')) 78 servers[0].servers.buildFragmentedFilePath(videoUUID, fragmentedAudioFileUrl)
86 ] 79 ]
87 80
88 for (const path of paths) { 81 for (const path of paths) {
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts
index a8c8a889b..f9220e4b3 100644
--- a/server/tests/api/videos/multiple-servers.ts
+++ b/server/tests/api/videos/multiple-servers.ts
@@ -3,49 +3,29 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import * as request from 'supertest' 5import * as request from 'supertest'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { 6import {
8 addVideoChannel,
9 buildAbsoluteFixturePath, 7 buildAbsoluteFixturePath,
10 checkTmpIsEmpty, 8 checkTmpIsEmpty,
11 checkVideoFilesWereRemoved, 9 checkVideoFilesWereRemoved,
12 cleanupTests, 10 cleanupTests,
13 completeVideoCheck, 11 completeVideoCheck,
14 createUser, 12 createMultipleServers,
15 dateIsValid, 13 dateIsValid,
16 doubleFollow, 14 doubleFollow,
17 flushAndRunMultipleServers, 15 PeerTubeServer,
18 getLocalVideos, 16 saveVideoInServers,
19 getVideo,
20 getVideoChannelsList,
21 getVideosList,
22 rateVideo,
23 removeVideo,
24 ServerInfo,
25 setAccessTokensToServers, 17 setAccessTokensToServers,
26 testImage, 18 testImage,
27 updateVideo,
28 uploadVideo,
29 userLogin,
30 viewVideo,
31 wait, 19 wait,
20 waitJobs,
32 webtorrentAdd 21 webtorrentAdd
33} from '../../../../shared/extra-utils' 22} from '@shared/extra-utils'
34import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 23import { HttpStatusCode, VideoCommentThreadTree, VideoPrivacy } from '@shared/models'
35import {
36 addVideoCommentReply,
37 addVideoCommentThread,
38 deleteVideoComment,
39 findCommentId,
40 getVideoCommentThreads,
41 getVideoThreadComments
42} from '../../../../shared/extra-utils/videos/video-comments'
43import { VideoComment, VideoCommentThreadTree, VideoPrivacy } from '../../../../shared/models/videos'
44 24
45const expect = chai.expect 25const expect = chai.expect
46 26
47describe('Test multiple servers', function () { 27describe('Test multiple servers', function () {
48 let servers: ServerInfo[] = [] 28 let servers: PeerTubeServer[] = []
49 const toRemove = [] 29 const toRemove = []
50 let videoUUID = '' 30 let videoUUID = ''
51 let videoChannelId: number 31 let videoChannelId: number
@@ -53,7 +33,7 @@ describe('Test multiple servers', function () {
53 before(async function () { 33 before(async function () {
54 this.timeout(120000) 34 this.timeout(120000)
55 35
56 servers = await flushAndRunMultipleServers(3) 36 servers = await createMultipleServers(3)
57 37
58 // Get the access tokens 38 // Get the access tokens
59 await setAccessTokensToServers(servers) 39 await setAccessTokensToServers(servers)
@@ -64,9 +44,9 @@ describe('Test multiple servers', function () {
64 displayName: 'my channel', 44 displayName: 'my channel',
65 description: 'super channel' 45 description: 'super channel'
66 } 46 }
67 await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel) 47 await servers[0].channels.create({ attributes: videoChannel })
68 const channelRes = await getVideoChannelsList(servers[0].url, 0, 1) 48 const { data } = await servers[0].channels.list({ start: 0, count: 1 })
69 videoChannelId = channelRes.body.data[0].id 49 videoChannelId = data[0].id
70 } 50 }
71 51
72 // Server 1 and server 2 follow each other 52 // Server 1 and server 2 follow each other
@@ -79,10 +59,9 @@ describe('Test multiple servers', function () {
79 59
80 it('Should not have videos for all servers', async function () { 60 it('Should not have videos for all servers', async function () {
81 for (const server of servers) { 61 for (const server of servers) {
82 const res = await getVideosList(server.url) 62 const { data } = await server.videos.list()
83 const videos = res.body.data 63 expect(data).to.be.an('array')
84 expect(videos).to.be.an('array') 64 expect(data.length).to.equal(0)
85 expect(videos.length).to.equal(0)
86 } 65 }
87 }) 66 })
88 67
@@ -90,7 +69,7 @@ describe('Test multiple servers', function () {
90 it('Should upload the video on server 1 and propagate on each server', async function () { 69 it('Should upload the video on server 1 and propagate on each server', async function () {
91 this.timeout(25000) 70 this.timeout(25000)
92 71
93 const videoAttributes = { 72 const attributes = {
94 name: 'my super name for server 1', 73 name: 'my super name for server 1',
95 category: 5, 74 category: 5,
96 licence: 4, 75 licence: 4,
@@ -103,7 +82,7 @@ describe('Test multiple servers', function () {
103 channelId: videoChannelId, 82 channelId: videoChannelId,
104 fixture: 'video_short1.webm' 83 fixture: 'video_short1.webm'
105 } 84 }
106 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 85 await servers[0].videos.upload({ attributes })
107 86
108 await waitJobs(servers) 87 await waitJobs(servers)
109 88
@@ -146,14 +125,13 @@ describe('Test multiple servers', function () {
146 ] 125 ]
147 } 126 }
148 127
149 const res = await getVideosList(server.url) 128 const { data } = await server.videos.list()
150 const videos = res.body.data 129 expect(data).to.be.an('array')
151 expect(videos).to.be.an('array') 130 expect(data.length).to.equal(1)
152 expect(videos.length).to.equal(1) 131 const video = data[0]
153 const video = videos[0]
154 132
155 await completeVideoCheck(server.url, video, checkAttributes) 133 await completeVideoCheck(server, video, checkAttributes)
156 publishedAt = video.publishedAt 134 publishedAt = video.publishedAt as string
157 } 135 }
158 }) 136 })
159 137
@@ -164,10 +142,10 @@ describe('Test multiple servers', function () {
164 username: 'user1', 142 username: 'user1',
165 password: 'super_password' 143 password: 'super_password'
166 } 144 }
167 await createUser({ url: servers[1].url, accessToken: servers[1].accessToken, username: user.username, password: user.password }) 145 await servers[1].users.create({ username: user.username, password: user.password })
168 const userAccessToken = await userLogin(servers[1], user) 146 const userAccessToken = await servers[1].login.getAccessToken(user)
169 147
170 const videoAttributes = { 148 const attributes = {
171 name: 'my super name for server 2', 149 name: 'my super name for server 2',
172 category: 4, 150 category: 4,
173 licence: 3, 151 licence: 3,
@@ -180,7 +158,7 @@ describe('Test multiple servers', function () {
180 thumbnailfile: 'thumbnail.jpg', 158 thumbnailfile: 'thumbnail.jpg',
181 previewfile: 'preview.jpg' 159 previewfile: 'preview.jpg'
182 } 160 }
183 await uploadVideo(servers[1].url, userAccessToken, videoAttributes, HttpStatusCode.OK_200, 'resumable') 161 await servers[1].videos.upload({ token: userAccessToken, attributes, mode: 'resumable' })
184 162
185 // Transcoding 163 // Transcoding
186 await waitJobs(servers) 164 await waitJobs(servers)
@@ -235,65 +213,67 @@ describe('Test multiple servers', function () {
235 previewfile: 'preview' 213 previewfile: 'preview'
236 } 214 }
237 215
238 const res = await getVideosList(server.url) 216 const { data } = await server.videos.list()
239 const videos = res.body.data 217 expect(data).to.be.an('array')
240 expect(videos).to.be.an('array') 218 expect(data.length).to.equal(2)
241 expect(videos.length).to.equal(2) 219 const video = data[1]
242 const video = videos[1]
243 220
244 await completeVideoCheck(server.url, video, checkAttributes) 221 await completeVideoCheck(server, video, checkAttributes)
245 } 222 }
246 }) 223 })
247 224
248 it('Should upload two videos on server 3 and propagate on each server', async function () { 225 it('Should upload two videos on server 3 and propagate on each server', async function () {
249 this.timeout(45000) 226 this.timeout(45000)
250 227
251 const videoAttributes1 = { 228 {
252 name: 'my super name for server 3', 229 const attributes = {
253 category: 6, 230 name: 'my super name for server 3',
254 licence: 5, 231 category: 6,
255 language: 'de', 232 licence: 5,
256 nsfw: true, 233 language: 'de',
257 description: 'my super description for server 3', 234 nsfw: true,
258 support: 'my super support text for server 3', 235 description: 'my super description for server 3',
259 tags: [ 'tag1p3' ], 236 support: 'my super support text for server 3',
260 fixture: 'video_short3.webm' 237 tags: [ 'tag1p3' ],
238 fixture: 'video_short3.webm'
239 }
240 await servers[2].videos.upload({ attributes })
261 } 241 }
262 await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes1) 242
263 243 {
264 const videoAttributes2 = { 244 const attributes = {
265 name: 'my super name for server 3-2', 245 name: 'my super name for server 3-2',
266 category: 7, 246 category: 7,
267 licence: 6, 247 licence: 6,
268 language: 'ko', 248 language: 'ko',
269 nsfw: false, 249 nsfw: false,
270 description: 'my super description for server 3-2', 250 description: 'my super description for server 3-2',
271 support: 'my super support text for server 3-2', 251 support: 'my super support text for server 3-2',
272 tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ], 252 tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
273 fixture: 'video_short.webm' 253 fixture: 'video_short.webm'
254 }
255 await servers[2].videos.upload({ attributes })
274 } 256 }
275 await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes2)
276 257
277 await waitJobs(servers) 258 await waitJobs(servers)
278 259
279 // All servers should have this video 260 // All servers should have this video
280 for (const server of servers) { 261 for (const server of servers) {
281 const isLocal = server.url === 'http://localhost:' + servers[2].port 262 const isLocal = server.url === 'http://localhost:' + servers[2].port
282 const res = await getVideosList(server.url) 263 const { data } = await server.videos.list()
283 264
284 const videos = res.body.data 265 expect(data).to.be.an('array')
285 expect(videos).to.be.an('array') 266 expect(data.length).to.equal(4)
286 expect(videos.length).to.equal(4)
287 267
288 // We not sure about the order of the two last uploads 268 // We not sure about the order of the two last uploads
289 let video1 = null 269 let video1 = null
290 let video2 = null 270 let video2 = null
291 if (videos[2].name === 'my super name for server 3') { 271 if (data[2].name === 'my super name for server 3') {
292 video1 = videos[2] 272 video1 = data[2]
293 video2 = videos[3] 273 video2 = data[3]
294 } else { 274 } else {
295 video1 = videos[3] 275 video1 = data[3]
296 video2 = videos[2] 276 video2 = data[2]
297 } 277 }
298 278
299 const checkAttributesVideo1 = { 279 const checkAttributesVideo1 = {
@@ -328,7 +308,7 @@ describe('Test multiple servers', function () {
328 } 308 }
329 ] 309 ]
330 } 310 }
331 await completeVideoCheck(server.url, video1, checkAttributesVideo1) 311 await completeVideoCheck(server, video1, checkAttributesVideo1)
332 312
333 const checkAttributesVideo2 = { 313 const checkAttributesVideo2 = {
334 name: 'my super name for server 3-2', 314 name: 'my super name for server 3-2',
@@ -362,38 +342,38 @@ describe('Test multiple servers', function () {
362 } 342 }
363 ] 343 ]
364 } 344 }
365 await completeVideoCheck(server.url, video2, checkAttributesVideo2) 345 await completeVideoCheck(server, video2, checkAttributesVideo2)
366 } 346 }
367 }) 347 })
368 }) 348 })
369 349
370 describe('It should list local videos', function () { 350 describe('It should list local videos', function () {
371 it('Should list only local videos on server 1', async function () { 351 it('Should list only local videos on server 1', async function () {
372 const { body } = await getLocalVideos(servers[0].url) 352 const { data, total } = await servers[0].videos.list({ filter: 'local' })
373 353
374 expect(body.total).to.equal(1) 354 expect(total).to.equal(1)
375 expect(body.data).to.be.an('array') 355 expect(data).to.be.an('array')
376 expect(body.data.length).to.equal(1) 356 expect(data.length).to.equal(1)
377 expect(body.data[0].name).to.equal('my super name for server 1') 357 expect(data[0].name).to.equal('my super name for server 1')
378 }) 358 })
379 359
380 it('Should list only local videos on server 2', async function () { 360 it('Should list only local videos on server 2', async function () {
381 const { body } = await getLocalVideos(servers[1].url) 361 const { data, total } = await servers[1].videos.list({ filter: 'local' })
382 362
383 expect(body.total).to.equal(1) 363 expect(total).to.equal(1)
384 expect(body.data).to.be.an('array') 364 expect(data).to.be.an('array')
385 expect(body.data.length).to.equal(1) 365 expect(data.length).to.equal(1)
386 expect(body.data[0].name).to.equal('my super name for server 2') 366 expect(data[0].name).to.equal('my super name for server 2')
387 }) 367 })
388 368
389 it('Should list only local videos on server 3', async function () { 369 it('Should list only local videos on server 3', async function () {
390 const { body } = await getLocalVideos(servers[2].url) 370 const { data, total } = await servers[2].videos.list({ filter: 'local' })
391 371
392 expect(body.total).to.equal(2) 372 expect(total).to.equal(2)
393 expect(body.data).to.be.an('array') 373 expect(data).to.be.an('array')
394 expect(body.data.length).to.equal(2) 374 expect(data.length).to.equal(2)
395 expect(body.data[0].name).to.equal('my super name for server 3') 375 expect(data[0].name).to.equal('my super name for server 3')
396 expect(body.data[1].name).to.equal('my super name for server 3-2') 376 expect(data[1].name).to.equal('my super name for server 3-2')
397 }) 377 })
398 }) 378 })
399 379
@@ -401,15 +381,13 @@ describe('Test multiple servers', function () {
401 it('Should add the file 1 by asking server 3', async function () { 381 it('Should add the file 1 by asking server 3', async function () {
402 this.timeout(10000) 382 this.timeout(10000)
403 383
404 const res = await getVideosList(servers[2].url) 384 const { data } = await servers[2].videos.list()
405
406 const video = res.body.data[0]
407 toRemove.push(res.body.data[2])
408 toRemove.push(res.body.data[3])
409 385
410 const res2 = await getVideo(servers[2].url, video.id) 386 const video = data[0]
411 const videoDetails = res2.body 387 toRemove.push(data[2])
388 toRemove.push(data[3])
412 389
390 const videoDetails = await servers[2].videos.get({ id: video.id })
413 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true) 391 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
414 expect(torrent.files).to.be.an('array') 392 expect(torrent.files).to.be.an('array')
415 expect(torrent.files.length).to.equal(1) 393 expect(torrent.files.length).to.equal(1)
@@ -419,11 +397,10 @@ describe('Test multiple servers', function () {
419 it('Should add the file 2 by asking server 1', async function () { 397 it('Should add the file 2 by asking server 1', async function () {
420 this.timeout(10000) 398 this.timeout(10000)
421 399
422 const res = await getVideosList(servers[0].url) 400 const { data } = await servers[0].videos.list()
423 401
424 const video = res.body.data[1] 402 const video = data[1]
425 const res2 = await getVideo(servers[0].url, video.id) 403 const videoDetails = await servers[0].videos.get({ id: video.id })
426 const videoDetails = res2.body
427 404
428 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true) 405 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
429 expect(torrent.files).to.be.an('array') 406 expect(torrent.files).to.be.an('array')
@@ -434,11 +411,10 @@ describe('Test multiple servers', function () {
434 it('Should add the file 3 by asking server 2', async function () { 411 it('Should add the file 3 by asking server 2', async function () {
435 this.timeout(10000) 412 this.timeout(10000)
436 413
437 const res = await getVideosList(servers[1].url) 414 const { data } = await servers[1].videos.list()
438 415
439 const video = res.body.data[2] 416 const video = data[2]
440 const res2 = await getVideo(servers[1].url, video.id) 417 const videoDetails = await servers[1].videos.get({ id: video.id })
441 const videoDetails = res2.body
442 418
443 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true) 419 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
444 expect(torrent.files).to.be.an('array') 420 expect(torrent.files).to.be.an('array')
@@ -449,11 +425,10 @@ describe('Test multiple servers', function () {
449 it('Should add the file 3-2 by asking server 1', async function () { 425 it('Should add the file 3-2 by asking server 1', async function () {
450 this.timeout(10000) 426 this.timeout(10000)
451 427
452 const res = await getVideosList(servers[0].url) 428 const { data } = await servers[0].videos.list()
453 429
454 const video = res.body.data[3] 430 const video = data[3]
455 const res2 = await getVideo(servers[0].url, video.id) 431 const videoDetails = await servers[0].videos.get({ id: video.id })
456 const videoDetails = res2.body
457 432
458 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri) 433 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri)
459 expect(torrent.files).to.be.an('array') 434 expect(torrent.files).to.be.an('array')
@@ -464,11 +439,10 @@ describe('Test multiple servers', function () {
464 it('Should add the file 2 in 360p by asking server 1', async function () { 439 it('Should add the file 2 in 360p by asking server 1', async function () {
465 this.timeout(10000) 440 this.timeout(10000)
466 441
467 const res = await getVideosList(servers[0].url) 442 const { data } = await servers[0].videos.list()
468 443
469 const video = res.body.data.find(v => v.name === 'my super name for server 2') 444 const video = data.find(v => v.name === 'my super name for server 2')
470 const res2 = await getVideo(servers[0].url, video.id) 445 const videoDetails = await servers[0].videos.get({ id: video.id })
471 const videoDetails = res2.body
472 446
473 const file = videoDetails.files.find(f => f.resolution.id === 360) 447 const file = videoDetails.files.find(f => f.resolution.id === 360)
474 expect(file).not.to.be.undefined 448 expect(file).not.to.be.undefined
@@ -487,30 +461,36 @@ describe('Test multiple servers', function () {
487 let remoteVideosServer3 = [] 461 let remoteVideosServer3 = []
488 462
489 before(async function () { 463 before(async function () {
490 const res1 = await getVideosList(servers[0].url) 464 {
491 remoteVideosServer1 = res1.body.data.filter(video => video.isLocal === false).map(video => video.uuid) 465 const { data } = await servers[0].videos.list()
466 remoteVideosServer1 = data.filter(video => video.isLocal === false).map(video => video.uuid)
467 }
492 468
493 const res2 = await getVideosList(servers[1].url) 469 {
494 remoteVideosServer2 = res2.body.data.filter(video => video.isLocal === false).map(video => video.uuid) 470 const { data } = await servers[1].videos.list()
471 remoteVideosServer2 = data.filter(video => video.isLocal === false).map(video => video.uuid)
472 }
495 473
496 const res3 = await getVideosList(servers[2].url) 474 {
497 localVideosServer3 = res3.body.data.filter(video => video.isLocal === true).map(video => video.uuid) 475 const { data } = await servers[2].videos.list()
498 remoteVideosServer3 = res3.body.data.filter(video => video.isLocal === false).map(video => video.uuid) 476 localVideosServer3 = data.filter(video => video.isLocal === true).map(video => video.uuid)
477 remoteVideosServer3 = data.filter(video => video.isLocal === false).map(video => video.uuid)
478 }
499 }) 479 })
500 480
501 it('Should view multiple videos on owned servers', async function () { 481 it('Should view multiple videos on owned servers', async function () {
502 this.timeout(30000) 482 this.timeout(30000)
503 483
504 await viewVideo(servers[2].url, localVideosServer3[0]) 484 await servers[2].videos.view({ id: localVideosServer3[0] })
505 await wait(1000) 485 await wait(1000)
506 486
507 await viewVideo(servers[2].url, localVideosServer3[0]) 487 await servers[2].videos.view({ id: localVideosServer3[0] })
508 await viewVideo(servers[2].url, localVideosServer3[1]) 488 await servers[2].videos.view({ id: localVideosServer3[1] })
509 489
510 await wait(1000) 490 await wait(1000)
511 491
512 await viewVideo(servers[2].url, localVideosServer3[0]) 492 await servers[2].videos.view({ id: localVideosServer3[0] })
513 await viewVideo(servers[2].url, localVideosServer3[0]) 493 await servers[2].videos.view({ id: localVideosServer3[0] })
514 494
515 await waitJobs(servers) 495 await waitJobs(servers)
516 496
@@ -520,11 +500,10 @@ describe('Test multiple servers', function () {
520 await waitJobs(servers) 500 await waitJobs(servers)
521 501
522 for (const server of servers) { 502 for (const server of servers) {
523 const res = await getVideosList(server.url) 503 const { data } = await server.videos.list()
524 504
525 const videos = res.body.data 505 const video0 = data.find(v => v.uuid === localVideosServer3[0])
526 const video0 = videos.find(v => v.uuid === localVideosServer3[0]) 506 const video1 = data.find(v => v.uuid === localVideosServer3[1])
527 const video1 = videos.find(v => v.uuid === localVideosServer3[1])
528 507
529 expect(video0.views).to.equal(3) 508 expect(video0.views).to.equal(3)
530 expect(video1.views).to.equal(1) 509 expect(video1.views).to.equal(1)
@@ -535,16 +514,16 @@ describe('Test multiple servers', function () {
535 this.timeout(45000) 514 this.timeout(45000)
536 515
537 const tasks: Promise<any>[] = [] 516 const tasks: Promise<any>[] = []
538 tasks.push(viewVideo(servers[0].url, remoteVideosServer1[0])) 517 tasks.push(servers[0].videos.view({ id: remoteVideosServer1[0] }))
539 tasks.push(viewVideo(servers[1].url, remoteVideosServer2[0])) 518 tasks.push(servers[1].videos.view({ id: remoteVideosServer2[0] }))
540 tasks.push(viewVideo(servers[1].url, remoteVideosServer2[0])) 519 tasks.push(servers[1].videos.view({ id: remoteVideosServer2[0] }))
541 tasks.push(viewVideo(servers[2].url, remoteVideosServer3[0])) 520 tasks.push(servers[2].videos.view({ id: remoteVideosServer3[0] }))
542 tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1])) 521 tasks.push(servers[2].videos.view({ id: remoteVideosServer3[1] }))
543 tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1])) 522 tasks.push(servers[2].videos.view({ id: remoteVideosServer3[1] }))
544 tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1])) 523 tasks.push(servers[2].videos.view({ id: remoteVideosServer3[1] }))
545 tasks.push(viewVideo(servers[2].url, localVideosServer3[1])) 524 tasks.push(servers[2].videos.view({ id: localVideosServer3[1] }))
546 tasks.push(viewVideo(servers[2].url, localVideosServer3[1])) 525 tasks.push(servers[2].videos.view({ id: localVideosServer3[1] }))
547 tasks.push(viewVideo(servers[2].url, localVideosServer3[1])) 526 tasks.push(servers[2].videos.view({ id: localVideosServer3[1] }))
548 527
549 await Promise.all(tasks) 528 await Promise.all(tasks)
550 529
@@ -558,18 +537,16 @@ describe('Test multiple servers', function () {
558 let baseVideos = null 537 let baseVideos = null
559 538
560 for (const server of servers) { 539 for (const server of servers) {
561 const res = await getVideosList(server.url) 540 const { data } = await server.videos.list()
562
563 const videos = res.body.data
564 541
565 // Initialize base videos for future comparisons 542 // Initialize base videos for future comparisons
566 if (baseVideos === null) { 543 if (baseVideos === null) {
567 baseVideos = videos 544 baseVideos = data
568 continue 545 continue
569 } 546 }
570 547
571 for (const baseVideo of baseVideos) { 548 for (const baseVideo of baseVideos) {
572 const sameVideo = videos.find(video => video.name === baseVideo.name) 549 const sameVideo = data.find(video => video.name === baseVideo.name)
573 expect(baseVideo.views).to.equal(sameVideo.views) 550 expect(baseVideo.views).to.equal(sameVideo.views)
574 } 551 }
575 } 552 }
@@ -578,35 +555,34 @@ describe('Test multiple servers', function () {
578 it('Should like and dislikes videos on different services', async function () { 555 it('Should like and dislikes videos on different services', async function () {
579 this.timeout(50000) 556 this.timeout(50000)
580 557
581 await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like') 558 await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'like' })
582 await wait(500) 559 await wait(500)
583 await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'dislike') 560 await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'dislike' })
584 await wait(500) 561 await wait(500)
585 await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like') 562 await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'like' })
586 await rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'like') 563 await servers[2].videos.rate({ id: localVideosServer3[1], rating: 'like' })
587 await wait(500) 564 await wait(500)
588 await rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'dislike') 565 await servers[2].videos.rate({ id: localVideosServer3[1], rating: 'dislike' })
589 await rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[1], 'dislike') 566 await servers[2].videos.rate({ id: remoteVideosServer3[1], rating: 'dislike' })
590 await wait(500) 567 await wait(500)
591 await rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[0], 'like') 568 await servers[2].videos.rate({ id: remoteVideosServer3[0], rating: 'like' })
592 569
593 await waitJobs(servers) 570 await waitJobs(servers)
594 await wait(5000) 571 await wait(5000)
572 await waitJobs(servers)
595 573
596 let baseVideos = null 574 let baseVideos = null
597 for (const server of servers) { 575 for (const server of servers) {
598 const res = await getVideosList(server.url) 576 const { data } = await server.videos.list()
599
600 const videos = res.body.data
601 577
602 // Initialize base videos for future comparisons 578 // Initialize base videos for future comparisons
603 if (baseVideos === null) { 579 if (baseVideos === null) {
604 baseVideos = videos 580 baseVideos = data
605 continue 581 continue
606 } 582 }
607 583
608 for (const baseVideo of baseVideos) { 584 for (const baseVideo of baseVideos) {
609 const sameVideo = videos.find(video => video.name === baseVideo.name) 585 const sameVideo = data.find(video => video.name === baseVideo.name)
610 expect(baseVideo.likes).to.equal(sameVideo.likes) 586 expect(baseVideo.likes).to.equal(sameVideo.likes)
611 expect(baseVideo.dislikes).to.equal(sameVideo.dislikes) 587 expect(baseVideo.dislikes).to.equal(sameVideo.dislikes)
612 } 588 }
@@ -632,7 +608,7 @@ describe('Test multiple servers', function () {
632 previewfile: 'preview.jpg' 608 previewfile: 'preview.jpg'
633 } 609 }
634 610
635 await updateVideo(servers[2].url, servers[2].accessToken, toRemove[0].id, attributes) 611 await servers[2].videos.update({ id: toRemove[0].id, attributes })
636 612
637 await waitJobs(servers) 613 await waitJobs(servers)
638 }) 614 })
@@ -641,10 +617,9 @@ describe('Test multiple servers', function () {
641 this.timeout(10000) 617 this.timeout(10000)
642 618
643 for (const server of servers) { 619 for (const server of servers) {
644 const res = await getVideosList(server.url) 620 const { data } = await server.videos.list()
645 621
646 const videos = res.body.data 622 const videoUpdated = data.find(video => video.name === 'my super video updated')
647 const videoUpdated = videos.find(video => video.name === 'my super video updated')
648 expect(!!videoUpdated).to.be.true 623 expect(!!videoUpdated).to.be.true
649 624
650 const isLocal = server.url === 'http://localhost:' + servers[2].port 625 const isLocal = server.url === 'http://localhost:' + servers[2].port
@@ -683,49 +658,46 @@ describe('Test multiple servers', function () {
683 thumbnailfile: 'thumbnail', 658 thumbnailfile: 'thumbnail',
684 previewfile: 'preview' 659 previewfile: 'preview'
685 } 660 }
686 await completeVideoCheck(server.url, videoUpdated, checkAttributes) 661 await completeVideoCheck(server, videoUpdated, checkAttributes)
687 } 662 }
688 }) 663 })
689 664
690 it('Should remove the videos 3 and 3-2 by asking server 3', async function () { 665 it('Should remove the videos 3 and 3-2 by asking server 3 and correctly delete files', async function () {
691 this.timeout(10000) 666 this.timeout(30000)
692 667
693 await removeVideo(servers[2].url, servers[2].accessToken, toRemove[0].id) 668 for (const id of [ toRemove[0].id, toRemove[1].id ]) {
694 await removeVideo(servers[2].url, servers[2].accessToken, toRemove[1].id) 669 await saveVideoInServers(servers, id)
695 670
696 await waitJobs(servers) 671 await servers[2].videos.remove({ id })
697 })
698 672
699 it('Should not have files of videos 3 and 3-2 on each server', async function () { 673 await waitJobs(servers)
700 for (const server of servers) { 674
701 await checkVideoFilesWereRemoved(toRemove[0].uuid, server.internalServerNumber) 675 for (const server of servers) {
702 await checkVideoFilesWereRemoved(toRemove[1].uuid, server.internalServerNumber) 676 await checkVideoFilesWereRemoved({ server, video: server.store.videoDetails })
677 }
703 } 678 }
704 }) 679 })
705 680
706 it('Should have videos 1 and 3 on each server', async function () { 681 it('Should have videos 1 and 3 on each server', async function () {
707 for (const server of servers) { 682 for (const server of servers) {
708 const res = await getVideosList(server.url) 683 const { data } = await server.videos.list()
709 684
710 const videos = res.body.data 685 expect(data).to.be.an('array')
711 expect(videos).to.be.an('array') 686 expect(data.length).to.equal(2)
712 expect(videos.length).to.equal(2) 687 expect(data[0].name).not.to.equal(data[1].name)
713 expect(videos[0].name).not.to.equal(videos[1].name) 688 expect(data[0].name).not.to.equal(toRemove[0].name)
714 expect(videos[0].name).not.to.equal(toRemove[0].name) 689 expect(data[1].name).not.to.equal(toRemove[0].name)
715 expect(videos[1].name).not.to.equal(toRemove[0].name) 690 expect(data[0].name).not.to.equal(toRemove[1].name)
716 expect(videos[0].name).not.to.equal(toRemove[1].name) 691 expect(data[1].name).not.to.equal(toRemove[1].name)
717 expect(videos[1].name).not.to.equal(toRemove[1].name) 692
718 693 videoUUID = data.find(video => video.name === 'my super name for server 1').uuid
719 videoUUID = videos.find(video => video.name === 'my super name for server 1').uuid
720 } 694 }
721 }) 695 })
722 696
723 it('Should get the same video by UUID on each server', async function () { 697 it('Should get the same video by UUID on each server', async function () {
724 let baseVideo = null 698 let baseVideo = null
725 for (const server of servers) { 699 for (const server of servers) {
726 const res = await getVideo(server.url, videoUUID) 700 const video = await server.videos.get({ id: videoUUID })
727
728 const video = res.body
729 701
730 if (baseVideo === null) { 702 if (baseVideo === null) {
731 baseVideo = video 703 baseVideo = video
@@ -748,8 +720,7 @@ describe('Test multiple servers', function () {
748 720
749 it('Should get the preview from each server', async function () { 721 it('Should get the preview from each server', async function () {
750 for (const server of servers) { 722 for (const server of servers) {
751 const res = await getVideo(server.url, videoUUID) 723 const video = await server.videos.get({ id: videoUUID })
752 const video = res.body
753 724
754 await testImage(server.url, 'video_short1-preview.webm', video.previewPath) 725 await testImage(server.url, 'video_short1-preview.webm', video.previewPath)
755 } 726 }
@@ -764,36 +735,36 @@ describe('Test multiple servers', function () {
764 735
765 { 736 {
766 const text = 'my super first comment' 737 const text = 'my super first comment'
767 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, text) 738 await servers[0].comments.createThread({ videoId: videoUUID, text })
768 } 739 }
769 740
770 { 741 {
771 const text = 'my super second comment' 742 const text = 'my super second comment'
772 await addVideoCommentThread(servers[2].url, servers[2].accessToken, videoUUID, text) 743 await servers[2].comments.createThread({ videoId: videoUUID, text })
773 } 744 }
774 745
775 await waitJobs(servers) 746 await waitJobs(servers)
776 747
777 { 748 {
778 const threadId = await findCommentId(servers[1].url, videoUUID, 'my super first comment') 749 const threadId = await servers[1].comments.findCommentId({ videoId: videoUUID, text: 'my super first comment' })
779 750
780 const text = 'my super answer to thread 1' 751 const text = 'my super answer to thread 1'
781 await addVideoCommentReply(servers[1].url, servers[1].accessToken, videoUUID, threadId, text) 752 await servers[1].comments.addReply({ videoId: videoUUID, toCommentId: threadId, text })
782 } 753 }
783 754
784 await waitJobs(servers) 755 await waitJobs(servers)
785 756
786 { 757 {
787 const threadId = await findCommentId(servers[2].url, videoUUID, 'my super first comment') 758 const threadId = await servers[2].comments.findCommentId({ videoId: videoUUID, text: 'my super first comment' })
788 759
789 const res2 = await getVideoThreadComments(servers[2].url, videoUUID, threadId) 760 const body = await servers[2].comments.getThread({ videoId: videoUUID, threadId })
790 const childCommentId = res2.body.children[0].comment.id 761 const childCommentId = body.children[0].comment.id
791 762
792 const text3 = 'my second answer to thread 1' 763 const text3 = 'my second answer to thread 1'
793 await addVideoCommentReply(servers[2].url, servers[2].accessToken, videoUUID, threadId, text3) 764 await servers[2].comments.addReply({ videoId: videoUUID, toCommentId: threadId, text: text3 })
794 765
795 const text2 = 'my super answer to answer of thread 1' 766 const text2 = 'my super answer to answer of thread 1'
796 await addVideoCommentReply(servers[2].url, servers[2].accessToken, videoUUID, childCommentId, text2) 767 await servers[2].comments.addReply({ videoId: videoUUID, toCommentId: childCommentId, text: text2 })
797 } 768 }
798 769
799 await waitJobs(servers) 770 await waitJobs(servers)
@@ -801,14 +772,14 @@ describe('Test multiple servers', function () {
801 772
802 it('Should have these threads', async function () { 773 it('Should have these threads', async function () {
803 for (const server of servers) { 774 for (const server of servers) {
804 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 775 const body = await server.comments.listThreads({ videoId: videoUUID })
805 776
806 expect(res.body.total).to.equal(2) 777 expect(body.total).to.equal(2)
807 expect(res.body.data).to.be.an('array') 778 expect(body.data).to.be.an('array')
808 expect(res.body.data).to.have.lengthOf(2) 779 expect(body.data).to.have.lengthOf(2)
809 780
810 { 781 {
811 const comment: VideoComment = res.body.data.find(c => c.text === 'my super first comment') 782 const comment = body.data.find(c => c.text === 'my super first comment')
812 expect(comment).to.not.be.undefined 783 expect(comment).to.not.be.undefined
813 expect(comment.inReplyToCommentId).to.be.null 784 expect(comment.inReplyToCommentId).to.be.null
814 expect(comment.account.name).to.equal('root') 785 expect(comment.account.name).to.equal('root')
@@ -819,7 +790,7 @@ describe('Test multiple servers', function () {
819 } 790 }
820 791
821 { 792 {
822 const comment: VideoComment = res.body.data.find(c => c.text === 'my super second comment') 793 const comment = body.data.find(c => c.text === 'my super second comment')
823 expect(comment).to.not.be.undefined 794 expect(comment).to.not.be.undefined
824 expect(comment.inReplyToCommentId).to.be.null 795 expect(comment.inReplyToCommentId).to.be.null
825 expect(comment.account.name).to.equal('root') 796 expect(comment.account.name).to.equal('root')
@@ -833,12 +804,11 @@ describe('Test multiple servers', function () {
833 804
834 it('Should have these comments', async function () { 805 it('Should have these comments', async function () {
835 for (const server of servers) { 806 for (const server of servers) {
836 const res1 = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 807 const body = await server.comments.listThreads({ videoId: videoUUID })
837 const threadId = res1.body.data.find(c => c.text === 'my super first comment').id 808 const threadId = body.data.find(c => c.text === 'my super first comment').id
838 809
839 const res2 = await getVideoThreadComments(server.url, videoUUID, threadId) 810 const tree = await server.comments.getThread({ videoId: videoUUID, threadId })
840 811
841 const tree: VideoCommentThreadTree = res2.body
842 expect(tree.comment.text).equal('my super first comment') 812 expect(tree.comment.text).equal('my super first comment')
843 expect(tree.comment.account.name).equal('root') 813 expect(tree.comment.account.name).equal('root')
844 expect(tree.comment.account.host).equal('localhost:' + servers[0].port) 814 expect(tree.comment.account.host).equal('localhost:' + servers[0].port)
@@ -867,19 +837,17 @@ describe('Test multiple servers', function () {
867 it('Should delete a reply', async function () { 837 it('Should delete a reply', async function () {
868 this.timeout(10000) 838 this.timeout(10000)
869 839
870 await deleteVideoComment(servers[2].url, servers[2].accessToken, videoUUID, childOfFirstChild.comment.id) 840 await servers[2].comments.delete({ videoId: videoUUID, commentId: childOfFirstChild.comment.id })
871 841
872 await waitJobs(servers) 842 await waitJobs(servers)
873 }) 843 })
874 844
875 it('Should have this comment marked as deleted', async function () { 845 it('Should have this comment marked as deleted', async function () {
876 for (const server of servers) { 846 for (const server of servers) {
877 const res1 = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 847 const { data } = await server.comments.listThreads({ videoId: videoUUID })
878 const threadId = res1.body.data.find(c => c.text === 'my super first comment').id 848 const threadId = data.find(c => c.text === 'my super first comment').id
879
880 const res2 = await getVideoThreadComments(server.url, videoUUID, threadId)
881 849
882 const tree: VideoCommentThreadTree = res2.body 850 const tree = await server.comments.getThread({ videoId: videoUUID, threadId })
883 expect(tree.comment.text).equal('my super first comment') 851 expect(tree.comment.text).equal('my super first comment')
884 852
885 const firstChild = tree.children[0] 853 const firstChild = tree.children[0]
@@ -900,23 +868,23 @@ describe('Test multiple servers', function () {
900 it('Should delete the thread comments', async function () { 868 it('Should delete the thread comments', async function () {
901 this.timeout(10000) 869 this.timeout(10000)
902 870
903 const res = await getVideoCommentThreads(servers[0].url, videoUUID, 0, 5) 871 const { data } = await servers[0].comments.listThreads({ videoId: videoUUID })
904 const threadId = res.body.data.find(c => c.text === 'my super first comment').id 872 const commentId = data.find(c => c.text === 'my super first comment').id
905 await deleteVideoComment(servers[0].url, servers[0].accessToken, videoUUID, threadId) 873 await servers[0].comments.delete({ videoId: videoUUID, commentId })
906 874
907 await waitJobs(servers) 875 await waitJobs(servers)
908 }) 876 })
909 877
910 it('Should have the threads marked as deleted on other servers too', async function () { 878 it('Should have the threads marked as deleted on other servers too', async function () {
911 for (const server of servers) { 879 for (const server of servers) {
912 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 880 const body = await server.comments.listThreads({ videoId: videoUUID })
913 881
914 expect(res.body.total).to.equal(2) 882 expect(body.total).to.equal(2)
915 expect(res.body.data).to.be.an('array') 883 expect(body.data).to.be.an('array')
916 expect(res.body.data).to.have.lengthOf(2) 884 expect(body.data).to.have.lengthOf(2)
917 885
918 { 886 {
919 const comment: VideoComment = res.body.data[0] 887 const comment = body.data[0]
920 expect(comment).to.not.be.undefined 888 expect(comment).to.not.be.undefined
921 expect(comment.inReplyToCommentId).to.be.null 889 expect(comment.inReplyToCommentId).to.be.null
922 expect(comment.account.name).to.equal('root') 890 expect(comment.account.name).to.equal('root')
@@ -927,7 +895,7 @@ describe('Test multiple servers', function () {
927 } 895 }
928 896
929 { 897 {
930 const deletedComment: VideoComment = res.body.data[1] 898 const deletedComment = body.data[1]
931 expect(deletedComment).to.not.be.undefined 899 expect(deletedComment).to.not.be.undefined
932 expect(deletedComment.isDeleted).to.be.true 900 expect(deletedComment.isDeleted).to.be.true
933 expect(deletedComment.deletedAt).to.not.be.null 901 expect(deletedComment.deletedAt).to.not.be.null
@@ -945,22 +913,22 @@ describe('Test multiple servers', function () {
945 it('Should delete a remote thread by the origin server', async function () { 913 it('Should delete a remote thread by the origin server', async function () {
946 this.timeout(5000) 914 this.timeout(5000)
947 915
948 const res = await getVideoCommentThreads(servers[0].url, videoUUID, 0, 5) 916 const { data } = await servers[0].comments.listThreads({ videoId: videoUUID })
949 const threadId = res.body.data.find(c => c.text === 'my super second comment').id 917 const commentId = data.find(c => c.text === 'my super second comment').id
950 await deleteVideoComment(servers[0].url, servers[0].accessToken, videoUUID, threadId) 918 await servers[0].comments.delete({ videoId: videoUUID, commentId })
951 919
952 await waitJobs(servers) 920 await waitJobs(servers)
953 }) 921 })
954 922
955 it('Should have the threads marked as deleted on other servers too', async function () { 923 it('Should have the threads marked as deleted on other servers too', async function () {
956 for (const server of servers) { 924 for (const server of servers) {
957 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 925 const body = await server.comments.listThreads({ videoId: videoUUID })
958 926
959 expect(res.body.total).to.equal(2) 927 expect(body.total).to.equal(2)
960 expect(res.body.data).to.have.lengthOf(2) 928 expect(body.data).to.have.lengthOf(2)
961 929
962 { 930 {
963 const comment: VideoComment = res.body.data[0] 931 const comment = body.data[0]
964 expect(comment.text).to.equal('') 932 expect(comment.text).to.equal('')
965 expect(comment.isDeleted).to.be.true 933 expect(comment.isDeleted).to.be.true
966 expect(comment.createdAt).to.not.be.null 934 expect(comment.createdAt).to.not.be.null
@@ -970,7 +938,7 @@ describe('Test multiple servers', function () {
970 } 938 }
971 939
972 { 940 {
973 const comment: VideoComment = res.body.data[1] 941 const comment = body.data[1]
974 expect(comment.text).to.equal('') 942 expect(comment.text).to.equal('')
975 expect(comment.isDeleted).to.be.true 943 expect(comment.isDeleted).to.be.true
976 expect(comment.createdAt).to.not.be.null 944 expect(comment.createdAt).to.not.be.null
@@ -989,17 +957,17 @@ describe('Test multiple servers', function () {
989 downloadEnabled: false 957 downloadEnabled: false
990 } 958 }
991 959
992 await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, attributes) 960 await servers[0].videos.update({ id: videoUUID, attributes })
993 961
994 await waitJobs(servers) 962 await waitJobs(servers)
995 963
996 for (const server of servers) { 964 for (const server of servers) {
997 const res = await getVideo(server.url, videoUUID) 965 const video = await server.videos.get({ id: videoUUID })
998 expect(res.body.commentsEnabled).to.be.false 966 expect(video.commentsEnabled).to.be.false
999 expect(res.body.downloadEnabled).to.be.false 967 expect(video.downloadEnabled).to.be.false
1000 968
1001 const text = 'my super forbidden comment' 969 const text = 'my super forbidden comment'
1002 await addVideoCommentThread(server.url, server.accessToken, videoUUID, text, HttpStatusCode.CONFLICT_409) 970 await server.comments.createThread({ videoId: videoUUID, text, expectedStatus: HttpStatusCode.CONFLICT_409 })
1003 } 971 }
1004 }) 972 })
1005 }) 973 })
@@ -1024,8 +992,8 @@ describe('Test multiple servers', function () {
1024 await waitJobs(servers) 992 await waitJobs(servers)
1025 993
1026 for (const server of servers) { 994 for (const server of servers) {
1027 const res = await getVideosList(server.url) 995 const { data } = await server.videos.list()
1028 const video = res.body.data.find(v => v.name === 'minimum parameters') 996 const video = data.find(v => v.name === 'minimum parameters')
1029 997
1030 const isLocal = server.url === 'http://localhost:' + servers[1].port 998 const isLocal = server.url === 'http://localhost:' + servers[1].port
1031 const checkAttributes = { 999 const checkAttributes = {
@@ -1072,7 +1040,7 @@ describe('Test multiple servers', function () {
1072 } 1040 }
1073 ] 1041 ]
1074 } 1042 }
1075 await completeVideoCheck(server.url, video, checkAttributes) 1043 await completeVideoCheck(server, video, checkAttributes)
1076 } 1044 }
1077 }) 1045 })
1078 }) 1046 })
diff --git a/server/tests/api/videos/resumable-upload.ts b/server/tests/api/videos/resumable-upload.ts
index 4fc3317df..857859fd3 100644
--- a/server/tests/api/videos/resumable-upload.ts
+++ b/server/tests/api/videos/resumable-upload.ts
@@ -4,22 +4,15 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { pathExists, readdir, stat } from 'fs-extra' 5import { pathExists, readdir, stat } from 'fs-extra'
6import { join } from 'path' 6import { join } from 'path'
7import { HttpStatusCode } from '@shared/core-utils'
8import { 7import {
9 buildAbsoluteFixturePath, 8 buildAbsoluteFixturePath,
10 buildServerDirectory,
11 cleanupTests, 9 cleanupTests,
12 flushAndRunServer, 10 createSingleServer,
13 getMyUserInformation, 11 PeerTubeServer,
14 prepareResumableUpload,
15 sendDebugCommand,
16 sendResumableChunks,
17 ServerInfo,
18 setAccessTokensToServers, 12 setAccessTokensToServers,
19 setDefaultVideoChannel, 13 setDefaultVideoChannel
20 updateUser
21} from '@shared/extra-utils' 14} from '@shared/extra-utils'
22import { MyUser, VideoPrivacy } from '@shared/models' 15import { HttpStatusCode, VideoPrivacy } from '@shared/models'
23 16
24const expect = chai.expect 17const expect = chai.expect
25 18
@@ -27,7 +20,7 @@ const expect = chai.expect
27 20
28describe('Test resumable upload', function () { 21describe('Test resumable upload', function () {
29 const defaultFixture = 'video_short.mp4' 22 const defaultFixture = 'video_short.mp4'
30 let server: ServerInfo 23 let server: PeerTubeServer
31 let rootId: number 24 let rootId: number
32 25
33 async function buildSize (fixture: string, size?: number) { 26 async function buildSize (fixture: string, size?: number) {
@@ -42,14 +35,14 @@ describe('Test resumable upload', function () {
42 35
43 const attributes = { 36 const attributes = {
44 name: 'video', 37 name: 'video',
45 channelId: server.videoChannel.id, 38 channelId: server.store.channel.id,
46 privacy: VideoPrivacy.PUBLIC, 39 privacy: VideoPrivacy.PUBLIC,
47 fixture: defaultFixture 40 fixture: defaultFixture
48 } 41 }
49 42
50 const mimetype = 'video/mp4' 43 const mimetype = 'video/mp4'
51 44
52 const res = await prepareResumableUpload({ url: server.url, token: server.accessToken, attributes, size, mimetype }) 45 const res = await server.videos.prepareResumableUpload({ attributes, size, mimetype })
53 46
54 return res.header['location'].split('?')[1] 47 return res.header['location'].split('?')[1]
55 } 48 }
@@ -67,15 +60,13 @@ describe('Test resumable upload', function () {
67 const size = await buildSize(defaultFixture, options.size) 60 const size = await buildSize(defaultFixture, options.size)
68 const absoluteFilePath = buildAbsoluteFixturePath(defaultFixture) 61 const absoluteFilePath = buildAbsoluteFixturePath(defaultFixture)
69 62
70 return sendResumableChunks({ 63 return server.videos.sendResumableChunks({
71 url: server.url,
72 token: server.accessToken,
73 pathUploadId, 64 pathUploadId,
74 videoFilePath: absoluteFilePath, 65 videoFilePath: absoluteFilePath,
75 size, 66 size,
76 contentLength, 67 contentLength,
77 contentRangeBuilder, 68 contentRangeBuilder,
78 specialStatus: expectedStatus 69 expectedStatus
79 }) 70 })
80 } 71 }
81 72
@@ -83,7 +74,7 @@ describe('Test resumable upload', function () {
83 const uploadId = uploadIdArg.replace(/^upload_id=/, '') 74 const uploadId = uploadIdArg.replace(/^upload_id=/, '')
84 75
85 const subPath = join('tmp', 'resumable-uploads', uploadId) 76 const subPath = join('tmp', 'resumable-uploads', uploadId)
86 const filePath = buildServerDirectory(server, subPath) 77 const filePath = server.servers.buildDirectory(subPath)
87 const exists = await pathExists(filePath) 78 const exists = await pathExists(filePath)
88 79
89 if (expectedSize === null) { 80 if (expectedSize === null) {
@@ -98,7 +89,7 @@ describe('Test resumable upload', function () {
98 89
99 async function countResumableUploads () { 90 async function countResumableUploads () {
100 const subPath = join('tmp', 'resumable-uploads') 91 const subPath = join('tmp', 'resumable-uploads')
101 const filePath = buildServerDirectory(server, subPath) 92 const filePath = server.servers.buildDirectory(subPath)
102 93
103 const files = await readdir(filePath) 94 const files = await readdir(filePath)
104 return files.length 95 return files.length
@@ -107,19 +98,14 @@ describe('Test resumable upload', function () {
107 before(async function () { 98 before(async function () {
108 this.timeout(30000) 99 this.timeout(30000)
109 100
110 server = await flushAndRunServer(1) 101 server = await createSingleServer(1)
111 await setAccessTokensToServers([ server ]) 102 await setAccessTokensToServers([ server ])
112 await setDefaultVideoChannel([ server ]) 103 await setDefaultVideoChannel([ server ])
113 104
114 const res = await getMyUserInformation(server.url, server.accessToken) 105 const body = await server.users.getMyInfo()
115 rootId = (res.body as MyUser).id 106 rootId = body.id
116 107
117 await updateUser({ 108 await server.users.update({ userId: rootId, videoQuota: 10_000_000 })
118 url: server.url,
119 userId: rootId,
120 accessToken: server.accessToken,
121 videoQuota: 10_000_000
122 })
123 }) 109 })
124 110
125 describe('Directory cleaning', function () { 111 describe('Directory cleaning', function () {
@@ -138,13 +124,13 @@ describe('Test resumable upload', function () {
138 }) 124 })
139 125
140 it('Should not delete recent uploads', async function () { 126 it('Should not delete recent uploads', async function () {
141 await sendDebugCommand(server.url, server.accessToken, { command: 'remove-dandling-resumable-uploads' }) 127 await server.debug.sendCommand({ body: { command: 'remove-dandling-resumable-uploads' } })
142 128
143 expect(await countResumableUploads()).to.equal(2) 129 expect(await countResumableUploads()).to.equal(2)
144 }) 130 })
145 131
146 it('Should delete old uploads', async function () { 132 it('Should delete old uploads', async function () {
147 await sendDebugCommand(server.url, server.accessToken, { command: 'remove-dandling-resumable-uploads' }) 133 await server.debug.sendCommand({ body: { command: 'remove-dandling-resumable-uploads' } })
148 134
149 expect(await countResumableUploads()).to.equal(0) 135 expect(await countResumableUploads()).to.equal(0)
150 }) 136 })
@@ -160,8 +146,7 @@ describe('Test resumable upload', function () {
160 }) 146 })
161 147
162 it('Should not accept more chunks than expected', async function () { 148 it('Should not accept more chunks than expected', async function () {
163 const size = 100 149 const uploadId = await prepareUpload(100)
164 const uploadId = await prepareUpload(size)
165 150
166 await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.CONFLICT_409 }) 151 await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.CONFLICT_409 })
167 await checkFileSize(uploadId, 0) 152 await checkFileSize(uploadId, 0)
@@ -170,8 +155,14 @@ describe('Test resumable upload', function () {
170 it('Should not accept more chunks than expected with an invalid content length/content range', async function () { 155 it('Should not accept more chunks than expected with an invalid content length/content range', async function () {
171 const uploadId = await prepareUpload(1500) 156 const uploadId = await prepareUpload(1500)
172 157
173 await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.BAD_REQUEST_400, contentLength: 1000 }) 158 // Content length check seems to have changed in v16
174 await checkFileSize(uploadId, 0) 159 if (process.version.startsWith('v16')) {
160 await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.CONFLICT_409, contentLength: 1000 })
161 await checkFileSize(uploadId, 1000)
162 } else {
163 await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.BAD_REQUEST_400, contentLength: 1000 })
164 await checkFileSize(uploadId, 0)
165 }
175 }) 166 })
176 167
177 it('Should not accept more chunks than expected with an invalid content length', async function () { 168 it('Should not accept more chunks than expected with an invalid content length', async function () {
@@ -179,8 +170,13 @@ describe('Test resumable upload', function () {
179 170
180 const size = 1000 171 const size = 1000
181 172
182 const contentRangeBuilder = start => `bytes ${start}-${start + size - 1}/${size}` 173 // Content length check seems to have changed in v16
183 await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.BAD_REQUEST_400, contentRangeBuilder, contentLength: size }) 174 const expectedStatus = process.version.startsWith('v16')
175 ? HttpStatusCode.CONFLICT_409
176 : HttpStatusCode.BAD_REQUEST_400
177
178 const contentRangeBuilder = (start: number) => `bytes ${start}-${start + size - 1}/${size}`
179 await sendChunks({ pathUploadId: uploadId, expectedStatus, contentRangeBuilder, contentLength: size })
184 await checkFileSize(uploadId, 0) 180 await checkFileSize(uploadId, 0)
185 }) 181 })
186 }) 182 })
diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts
index 1058a1e9c..29dac6ec1 100644
--- a/server/tests/api/videos/single-server.ts
+++ b/server/tests/api/videos/single-server.ts
@@ -2,43 +2,26 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { keyBy } from 'lodash'
6
7import { 5import {
8 checkVideoFilesWereRemoved, 6 checkVideoFilesWereRemoved,
9 cleanupTests, 7 cleanupTests,
10 completeVideoCheck, 8 completeVideoCheck,
11 flushAndRunServer, 9 createSingleServer,
12 getVideo, 10 PeerTubeServer,
13 getVideoCategories,
14 getVideoLanguages,
15 getVideoLicences,
16 getVideoPrivacies,
17 getVideosList,
18 getVideosListPagination,
19 getVideosListSort,
20 getVideosWithFilters,
21 rateVideo,
22 removeVideo,
23 ServerInfo,
24 setAccessTokensToServers, 11 setAccessTokensToServers,
25 testImage, 12 testImage,
26 updateVideo,
27 uploadVideo,
28 viewVideo,
29 wait 13 wait
30} from '../../../../shared/extra-utils' 14} from '@shared/extra-utils'
31import { VideoPrivacy } from '../../../../shared/models/videos' 15import { Video, VideoPrivacy } from '@shared/models'
32import { HttpStatusCode } from '@shared/core-utils'
33 16
34const expect = chai.expect 17const expect = chai.expect
35 18
36describe('Test a single server', function () { 19describe('Test a single server', function () {
37 20
38 function runSuite (mode: 'legacy' | 'resumable') { 21 function runSuite (mode: 'legacy' | 'resumable') {
39 let server: ServerInfo = null 22 let server: PeerTubeServer = null
40 let videoId = -1 23 let videoId: number | string
41 let videoId2 = -1 24 let videoId2: string
42 let videoUUID = '' 25 let videoUUID = ''
43 let videosListBase: any[] = null 26 let videosListBase: any[] = null
44 27
@@ -111,134 +94,123 @@ describe('Test a single server', function () {
111 before(async function () { 94 before(async function () {
112 this.timeout(30000) 95 this.timeout(30000)
113 96
114 server = await flushAndRunServer(1) 97 server = await createSingleServer(1)
115 98
116 await setAccessTokensToServers([ server ]) 99 await setAccessTokensToServers([ server ])
117 }) 100 })
118 101
119 it('Should list video categories', async function () { 102 it('Should list video categories', async function () {
120 const res = await getVideoCategories(server.url) 103 const categories = await server.videos.getCategories()
121
122 const categories = res.body
123 expect(Object.keys(categories)).to.have.length.above(10) 104 expect(Object.keys(categories)).to.have.length.above(10)
124 105
125 expect(categories[11]).to.equal('News & Politics') 106 expect(categories[11]).to.equal('News & Politics')
126 }) 107 })
127 108
128 it('Should list video licences', async function () { 109 it('Should list video licences', async function () {
129 const res = await getVideoLicences(server.url) 110 const licences = await server.videos.getLicences()
130
131 const licences = res.body
132 expect(Object.keys(licences)).to.have.length.above(5) 111 expect(Object.keys(licences)).to.have.length.above(5)
133 112
134 expect(licences[3]).to.equal('Attribution - No Derivatives') 113 expect(licences[3]).to.equal('Attribution - No Derivatives')
135 }) 114 })
136 115
137 it('Should list video languages', async function () { 116 it('Should list video languages', async function () {
138 const res = await getVideoLanguages(server.url) 117 const languages = await server.videos.getLanguages()
139
140 const languages = res.body
141 expect(Object.keys(languages)).to.have.length.above(5) 118 expect(Object.keys(languages)).to.have.length.above(5)
142 119
143 expect(languages['ru']).to.equal('Russian') 120 expect(languages['ru']).to.equal('Russian')
144 }) 121 })
145 122
146 it('Should list video privacies', async function () { 123 it('Should list video privacies', async function () {
147 const res = await getVideoPrivacies(server.url) 124 const privacies = await server.videos.getPrivacies()
148
149 const privacies = res.body
150 expect(Object.keys(privacies)).to.have.length.at.least(3) 125 expect(Object.keys(privacies)).to.have.length.at.least(3)
151 126
152 expect(privacies[3]).to.equal('Private') 127 expect(privacies[3]).to.equal('Private')
153 }) 128 })
154 129
155 it('Should not have videos', async function () { 130 it('Should not have videos', async function () {
156 const res = await getVideosList(server.url) 131 const { data, total } = await server.videos.list()
157 132
158 expect(res.body.total).to.equal(0) 133 expect(total).to.equal(0)
159 expect(res.body.data).to.be.an('array') 134 expect(data).to.be.an('array')
160 expect(res.body.data.length).to.equal(0) 135 expect(data.length).to.equal(0)
161 }) 136 })
162 137
163 it('Should upload the video', async function () { 138 it('Should upload the video', async function () {
164 this.timeout(10000) 139 this.timeout(10000)
165 140
166 const videoAttributes = { 141 const attributes = {
167 name: 'my super name', 142 name: 'my super name',
168 category: 2, 143 category: 2,
169 nsfw: true, 144 nsfw: true,
170 licence: 6, 145 licence: 6,
171 tags: [ 'tag1', 'tag2', 'tag3' ] 146 tags: [ 'tag1', 'tag2', 'tag3' ]
172 } 147 }
173 const res = await uploadVideo(server.url, server.accessToken, videoAttributes, HttpStatusCode.OK_200, mode) 148 const video = await server.videos.upload({ attributes, mode })
174 expect(res.body.video).to.not.be.undefined 149 expect(video).to.not.be.undefined
175 expect(res.body.video.id).to.equal(1) 150 expect(video.id).to.equal(1)
176 expect(res.body.video.uuid).to.have.length.above(5) 151 expect(video.uuid).to.have.length.above(5)
177 152
178 videoId = res.body.video.id 153 videoId = video.id
179 videoUUID = res.body.video.uuid 154 videoUUID = video.uuid
180 }) 155 })
181 156
182 it('Should get and seed the uploaded video', async function () { 157 it('Should get and seed the uploaded video', async function () {
183 this.timeout(5000) 158 this.timeout(5000)
184 159
185 const res = await getVideosList(server.url) 160 const { data, total } = await server.videos.list()
186 161
187 expect(res.body.total).to.equal(1) 162 expect(total).to.equal(1)
188 expect(res.body.data).to.be.an('array') 163 expect(data).to.be.an('array')
189 expect(res.body.data.length).to.equal(1) 164 expect(data.length).to.equal(1)
190 165
191 const video = res.body.data[0] 166 const video = data[0]
192 await completeVideoCheck(server.url, video, getCheckAttributes()) 167 await completeVideoCheck(server, video, getCheckAttributes())
193 }) 168 })
194 169
195 it('Should get the video by UUID', async function () { 170 it('Should get the video by UUID', async function () {
196 this.timeout(5000) 171 this.timeout(5000)
197 172
198 const res = await getVideo(server.url, videoUUID) 173 const video = await server.videos.get({ id: videoUUID })
199 174 await completeVideoCheck(server, video, getCheckAttributes())
200 const video = res.body
201 await completeVideoCheck(server.url, video, getCheckAttributes())
202 }) 175 })
203 176
204 it('Should have the views updated', async function () { 177 it('Should have the views updated', async function () {
205 this.timeout(20000) 178 this.timeout(20000)
206 179
207 await viewVideo(server.url, videoId) 180 await server.videos.view({ id: videoId })
208 await viewVideo(server.url, videoId) 181 await server.videos.view({ id: videoId })
209 await viewVideo(server.url, videoId) 182 await server.videos.view({ id: videoId })
210 183
211 await wait(1500) 184 await wait(1500)
212 185
213 await viewVideo(server.url, videoId) 186 await server.videos.view({ id: videoId })
214 await viewVideo(server.url, videoId) 187 await server.videos.view({ id: videoId })
215 188
216 await wait(1500) 189 await wait(1500)
217 190
218 await viewVideo(server.url, videoId) 191 await server.videos.view({ id: videoId })
219 await viewVideo(server.url, videoId) 192 await server.videos.view({ id: videoId })
220 193
221 // Wait the repeatable job 194 // Wait the repeatable job
222 await wait(8000) 195 await wait(8000)
223 196
224 const res = await getVideo(server.url, videoId) 197 const video = await server.videos.get({ id: videoId })
225
226 const video = res.body
227 expect(video.views).to.equal(3) 198 expect(video.views).to.equal(3)
228 }) 199 })
229 200
230 it('Should remove the video', async function () { 201 it('Should remove the video', async function () {
231 await removeVideo(server.url, server.accessToken, videoId) 202 const video = await server.videos.get({ id: videoId })
203 await server.videos.remove({ id: videoId })
232 204
233 await checkVideoFilesWereRemoved(videoUUID, 1) 205 await checkVideoFilesWereRemoved({ video, server })
234 }) 206 })
235 207
236 it('Should not have videos', async function () { 208 it('Should not have videos', async function () {
237 const res = await getVideosList(server.url) 209 const { total, data } = await server.videos.list()
238 210
239 expect(res.body.total).to.equal(0) 211 expect(total).to.equal(0)
240 expect(res.body.data).to.be.an('array') 212 expect(data).to.be.an('array')
241 expect(res.body.data).to.have.lengthOf(0) 213 expect(data).to.have.lengthOf(0)
242 }) 214 })
243 215
244 it('Should upload 6 videos', async function () { 216 it('Should upload 6 videos', async function () {
@@ -250,7 +222,7 @@ describe('Test a single server', function () {
250 ]) 222 ])
251 223
252 for (const video of videos) { 224 for (const video of videos) {
253 const videoAttributes = { 225 const attributes = {
254 name: video + ' name', 226 name: video + ' name',
255 description: video + ' description', 227 description: video + ' description',
256 category: 2, 228 category: 2,
@@ -261,19 +233,20 @@ describe('Test a single server', function () {
261 fixture: video 233 fixture: video
262 } 234 }
263 235
264 await uploadVideo(server.url, server.accessToken, videoAttributes, HttpStatusCode.OK_200, mode) 236 await server.videos.upload({ attributes, mode })
265 } 237 }
266 }) 238 })
267 239
268 it('Should have the correct durations', async function () { 240 it('Should have the correct durations', async function () {
269 const res = await getVideosList(server.url) 241 const { total, data } = await server.videos.list()
242
243 expect(total).to.equal(6)
244 expect(data).to.be.an('array')
245 expect(data).to.have.lengthOf(6)
270 246
271 expect(res.body.total).to.equal(6) 247 const videosByName: { [ name: string ]: Video } = {}
272 const videos = res.body.data 248 data.forEach(v => { videosByName[v.name] = v })
273 expect(videos).to.be.an('array')
274 expect(videos).to.have.lengthOf(6)
275 249
276 const videosByName = keyBy<{ duration: number }>(videos, 'name')
277 expect(videosByName['video_short.mp4 name'].duration).to.equal(5) 250 expect(videosByName['video_short.mp4 name'].duration).to.equal(5)
278 expect(videosByName['video_short.ogv name'].duration).to.equal(5) 251 expect(videosByName['video_short.ogv name'].duration).to.equal(5)
279 expect(videosByName['video_short.webm name'].duration).to.equal(5) 252 expect(videosByName['video_short.webm name'].duration).to.equal(5)
@@ -283,96 +256,87 @@ describe('Test a single server', function () {
283 }) 256 })
284 257
285 it('Should have the correct thumbnails', async function () { 258 it('Should have the correct thumbnails', async function () {
286 const res = await getVideosList(server.url) 259 const { data } = await server.videos.list()
287 260
288 const videos = res.body.data
289 // For the next test 261 // For the next test
290 videosListBase = videos 262 videosListBase = data
291 263
292 for (const video of videos) { 264 for (const video of data) {
293 const videoName = video.name.replace(' name', '') 265 const videoName = video.name.replace(' name', '')
294 await testImage(server.url, videoName, video.thumbnailPath) 266 await testImage(server.url, videoName, video.thumbnailPath)
295 } 267 }
296 }) 268 })
297 269
298 it('Should list only the two first videos', async function () { 270 it('Should list only the two first videos', async function () {
299 const res = await getVideosListPagination(server.url, 0, 2, 'name') 271 const { total, data } = await server.videos.list({ start: 0, count: 2, sort: 'name' })
300 272
301 const videos = res.body.data 273 expect(total).to.equal(6)
302 expect(res.body.total).to.equal(6) 274 expect(data.length).to.equal(2)
303 expect(videos.length).to.equal(2) 275 expect(data[0].name).to.equal(videosListBase[0].name)
304 expect(videos[0].name).to.equal(videosListBase[0].name) 276 expect(data[1].name).to.equal(videosListBase[1].name)
305 expect(videos[1].name).to.equal(videosListBase[1].name)
306 }) 277 })
307 278
308 it('Should list only the next three videos', async function () { 279 it('Should list only the next three videos', async function () {
309 const res = await getVideosListPagination(server.url, 2, 3, 'name') 280 const { total, data } = await server.videos.list({ start: 2, count: 3, sort: 'name' })
310 281
311 const videos = res.body.data 282 expect(total).to.equal(6)
312 expect(res.body.total).to.equal(6) 283 expect(data.length).to.equal(3)
313 expect(videos.length).to.equal(3) 284 expect(data[0].name).to.equal(videosListBase[2].name)
314 expect(videos[0].name).to.equal(videosListBase[2].name) 285 expect(data[1].name).to.equal(videosListBase[3].name)
315 expect(videos[1].name).to.equal(videosListBase[3].name) 286 expect(data[2].name).to.equal(videosListBase[4].name)
316 expect(videos[2].name).to.equal(videosListBase[4].name)
317 }) 287 })
318 288
319 it('Should list the last video', async function () { 289 it('Should list the last video', async function () {
320 const res = await getVideosListPagination(server.url, 5, 6, 'name') 290 const { total, data } = await server.videos.list({ start: 5, count: 6, sort: 'name' })
321 291
322 const videos = res.body.data 292 expect(total).to.equal(6)
323 expect(res.body.total).to.equal(6) 293 expect(data.length).to.equal(1)
324 expect(videos.length).to.equal(1) 294 expect(data[0].name).to.equal(videosListBase[5].name)
325 expect(videos[0].name).to.equal(videosListBase[5].name)
326 }) 295 })
327 296
328 it('Should not have the total field', async function () { 297 it('Should not have the total field', async function () {
329 const res = await getVideosListPagination(server.url, 5, 6, 'name', true) 298 const { total, data } = await server.videos.list({ start: 5, count: 6, sort: 'name', skipCount: true })
330 299
331 const videos = res.body.data 300 expect(total).to.not.exist
332 expect(res.body.total).to.not.exist 301 expect(data.length).to.equal(1)
333 expect(videos.length).to.equal(1) 302 expect(data[0].name).to.equal(videosListBase[5].name)
334 expect(videos[0].name).to.equal(videosListBase[5].name)
335 }) 303 })
336 304
337 it('Should list and sort by name in descending order', async function () { 305 it('Should list and sort by name in descending order', async function () {
338 const res = await getVideosListSort(server.url, '-name') 306 const { total, data } = await server.videos.list({ sort: '-name' })
339 307
340 const videos = res.body.data 308 expect(total).to.equal(6)
341 expect(res.body.total).to.equal(6) 309 expect(data.length).to.equal(6)
342 expect(videos.length).to.equal(6) 310 expect(data[0].name).to.equal('video_short.webm name')
343 expect(videos[0].name).to.equal('video_short.webm name') 311 expect(data[1].name).to.equal('video_short.ogv name')
344 expect(videos[1].name).to.equal('video_short.ogv name') 312 expect(data[2].name).to.equal('video_short.mp4 name')
345 expect(videos[2].name).to.equal('video_short.mp4 name') 313 expect(data[3].name).to.equal('video_short3.webm name')
346 expect(videos[3].name).to.equal('video_short3.webm name') 314 expect(data[4].name).to.equal('video_short2.webm name')
347 expect(videos[4].name).to.equal('video_short2.webm name') 315 expect(data[5].name).to.equal('video_short1.webm name')
348 expect(videos[5].name).to.equal('video_short1.webm name')
349 316
350 videoId = videos[3].uuid 317 videoId = data[3].uuid
351 videoId2 = videos[5].uuid 318 videoId2 = data[5].uuid
352 }) 319 })
353 320
354 it('Should list and sort by trending in descending order', async function () { 321 it('Should list and sort by trending in descending order', async function () {
355 const res = await getVideosListPagination(server.url, 0, 2, '-trending') 322 const { total, data } = await server.videos.list({ start: 0, count: 2, sort: '-trending' })
356 323
357 const videos = res.body.data 324 expect(total).to.equal(6)
358 expect(res.body.total).to.equal(6) 325 expect(data.length).to.equal(2)
359 expect(videos.length).to.equal(2)
360 }) 326 })
361 327
362 it('Should list and sort by hotness in descending order', async function () { 328 it('Should list and sort by hotness in descending order', async function () {
363 const res = await getVideosListPagination(server.url, 0, 2, '-hot') 329 const { total, data } = await server.videos.list({ start: 0, count: 2, sort: '-hot' })
364 330
365 const videos = res.body.data 331 expect(total).to.equal(6)
366 expect(res.body.total).to.equal(6) 332 expect(data.length).to.equal(2)
367 expect(videos.length).to.equal(2)
368 }) 333 })
369 334
370 it('Should list and sort by best in descending order', async function () { 335 it('Should list and sort by best in descending order', async function () {
371 const res = await getVideosListPagination(server.url, 0, 2, '-best') 336 const { total, data } = await server.videos.list({ start: 0, count: 2, sort: '-best' })
372 337
373 const videos = res.body.data 338 expect(total).to.equal(6)
374 expect(res.body.total).to.equal(6) 339 expect(data.length).to.equal(2)
375 expect(videos.length).to.equal(2)
376 }) 340 })
377 341
378 it('Should update a video', async function () { 342 it('Should update a video', async function () {
@@ -387,67 +351,66 @@ describe('Test a single server', function () {
387 downloadEnabled: false, 351 downloadEnabled: false,
388 tags: [ 'tagup1', 'tagup2' ] 352 tags: [ 'tagup1', 'tagup2' ]
389 } 353 }
390 await updateVideo(server.url, server.accessToken, videoId, attributes) 354 await server.videos.update({ id: videoId, attributes })
391 }) 355 })
392 356
393 it('Should filter by tags and category', async function () { 357 it('Should filter by tags and category', async function () {
394 const res1 = await getVideosWithFilters(server.url, { tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 4 ] }) 358 {
395 expect(res1.body.total).to.equal(1) 359 const { data, total } = await server.videos.list({ tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 4 ] })
396 expect(res1.body.data[0].name).to.equal('my super video updated') 360 expect(total).to.equal(1)
361 expect(data[0].name).to.equal('my super video updated')
362 }
397 363
398 const res2 = await getVideosWithFilters(server.url, { tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 3 ] }) 364 {
399 expect(res2.body.total).to.equal(0) 365 const { total } = await server.videos.list({ tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 3 ] })
366 expect(total).to.equal(0)
367 }
400 }) 368 })
401 369
402 it('Should have the video updated', async function () { 370 it('Should have the video updated', async function () {
403 this.timeout(60000) 371 this.timeout(60000)
404 372
405 const res = await getVideo(server.url, videoId) 373 const video = await server.videos.get({ id: videoId })
406 const video = res.body
407 374
408 await completeVideoCheck(server.url, video, updateCheckAttributes()) 375 await completeVideoCheck(server, video, updateCheckAttributes())
409 }) 376 })
410 377
411 it('Should update only the tags of a video', async function () { 378 it('Should update only the tags of a video', async function () {
412 const attributes = { 379 const attributes = {
413 tags: [ 'supertag', 'tag1', 'tag2' ] 380 tags: [ 'supertag', 'tag1', 'tag2' ]
414 } 381 }
415 await updateVideo(server.url, server.accessToken, videoId, attributes) 382 await server.videos.update({ id: videoId, attributes })
416 383
417 const res = await getVideo(server.url, videoId) 384 const video = await server.videos.get({ id: videoId })
418 const video = res.body
419 385
420 await completeVideoCheck(server.url, video, Object.assign(updateCheckAttributes(), attributes)) 386 await completeVideoCheck(server, video, Object.assign(updateCheckAttributes(), attributes))
421 }) 387 })
422 388
423 it('Should update only the description of a video', async function () { 389 it('Should update only the description of a video', async function () {
424 const attributes = { 390 const attributes = {
425 description: 'hello everybody' 391 description: 'hello everybody'
426 } 392 }
427 await updateVideo(server.url, server.accessToken, videoId, attributes) 393 await server.videos.update({ id: videoId, attributes })
428 394
429 const res = await getVideo(server.url, videoId) 395 const video = await server.videos.get({ id: videoId })
430 const video = res.body
431 396
432 const expectedAttributes = Object.assign(updateCheckAttributes(), { tags: [ 'supertag', 'tag1', 'tag2' ] }, attributes) 397 const expectedAttributes = Object.assign(updateCheckAttributes(), { tags: [ 'supertag', 'tag1', 'tag2' ] }, attributes)
433 await completeVideoCheck(server.url, video, expectedAttributes) 398 await completeVideoCheck(server, video, expectedAttributes)
434 }) 399 })
435 400
436 it('Should like a video', async function () { 401 it('Should like a video', async function () {
437 await rateVideo(server.url, server.accessToken, videoId, 'like') 402 await server.videos.rate({ id: videoId, rating: 'like' })
438 403
439 const res = await getVideo(server.url, videoId) 404 const video = await server.videos.get({ id: videoId })
440 const video = res.body
441 405
442 expect(video.likes).to.equal(1) 406 expect(video.likes).to.equal(1)
443 expect(video.dislikes).to.equal(0) 407 expect(video.dislikes).to.equal(0)
444 }) 408 })
445 409
446 it('Should dislike the same video', async function () { 410 it('Should dislike the same video', async function () {
447 await rateVideo(server.url, server.accessToken, videoId, 'dislike') 411 await server.videos.rate({ id: videoId, rating: 'dislike' })
448 412
449 const res = await getVideo(server.url, videoId) 413 const video = await server.videos.get({ id: videoId })
450 const video = res.body
451 414
452 expect(video.likes).to.equal(0) 415 expect(video.likes).to.equal(0)
453 expect(video.dislikes).to.equal(1) 416 expect(video.dislikes).to.equal(1)
@@ -457,10 +420,10 @@ describe('Test a single server', function () {
457 { 420 {
458 const now = new Date() 421 const now = new Date()
459 const attributes = { originallyPublishedAt: now.toISOString() } 422 const attributes = { originallyPublishedAt: now.toISOString() }
460 await updateVideo(server.url, server.accessToken, videoId, attributes) 423 await server.videos.update({ id: videoId, attributes })
461 424
462 const res = await getVideosListSort(server.url, '-originallyPublishedAt') 425 const { data } = await server.videos.list({ sort: '-originallyPublishedAt' })
463 const names = res.body.data.map(v => v.name) 426 const names = data.map(v => v.name)
464 427
465 expect(names[0]).to.equal('my super video updated') 428 expect(names[0]).to.equal('my super video updated')
466 expect(names[1]).to.equal('video_short2.webm name') 429 expect(names[1]).to.equal('video_short2.webm name')
@@ -473,10 +436,10 @@ describe('Test a single server', function () {
473 { 436 {
474 const now = new Date() 437 const now = new Date()
475 const attributes = { originallyPublishedAt: now.toISOString() } 438 const attributes = { originallyPublishedAt: now.toISOString() }
476 await updateVideo(server.url, server.accessToken, videoId2, attributes) 439 await server.videos.update({ id: videoId2, attributes })
477 440
478 const res = await getVideosListSort(server.url, '-originallyPublishedAt') 441 const { data } = await server.videos.list({ sort: '-originallyPublishedAt' })
479 const names = res.body.data.map(v => v.name) 442 const names = data.map(v => v.name)
480 443
481 expect(names[0]).to.equal('video_short1.webm name') 444 expect(names[0]).to.equal('video_short1.webm name')
482 expect(names[1]).to.equal('my super video updated') 445 expect(names[1]).to.equal('my super video updated')
diff --git a/server/tests/api/videos/video-captions.ts b/server/tests/api/videos/video-captions.ts
index 14ecedfa6..3bb0d131c 100644
--- a/server/tests/api/videos/video-captions.ts
+++ b/server/tests/api/videos/video-captions.ts
@@ -1,72 +1,61 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 checkVideoFilesWereRemoved, 6 checkVideoFilesWereRemoved,
7 cleanupTests, 7 cleanupTests,
8 createMultipleServers,
8 doubleFollow, 9 doubleFollow,
9 flushAndRunMultipleServers, 10 PeerTubeServer,
10 removeVideo, 11 setAccessTokensToServers,
11 uploadVideo, 12 testCaptionFile,
12 wait 13 wait,
13} from '../../../../shared/extra-utils' 14 waitJobs
14import { ServerInfo, setAccessTokensToServers } from '../../../../shared/extra-utils/index' 15} from '@shared/extra-utils'
15import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
16import {
17 createVideoCaption,
18 deleteVideoCaption,
19 listVideoCaptions,
20 testCaptionFile
21} from '../../../../shared/extra-utils/videos/video-captions'
22import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
23 16
24const expect = chai.expect 17const expect = chai.expect
25 18
26describe('Test video captions', function () { 19describe('Test video captions', function () {
27 const uuidRegex = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' 20 const uuidRegex = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
28 21
29 let servers: ServerInfo[] 22 let servers: PeerTubeServer[]
30 let videoUUID: string 23 let videoUUID: string
31 24
32 before(async function () { 25 before(async function () {
33 this.timeout(60000) 26 this.timeout(60000)
34 27
35 servers = await flushAndRunMultipleServers(2) 28 servers = await createMultipleServers(2)
36 29
37 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
38 await doubleFollow(servers[0], servers[1]) 31 await doubleFollow(servers[0], servers[1])
39 32
40 await waitJobs(servers) 33 await waitJobs(servers)
41 34
42 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'my video name' }) 35 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'my video name' } })
43 videoUUID = res.body.video.uuid 36 videoUUID = uuid
44 37
45 await waitJobs(servers) 38 await waitJobs(servers)
46 }) 39 })
47 40
48 it('Should list the captions and return an empty list', async function () { 41 it('Should list the captions and return an empty list', async function () {
49 for (const server of servers) { 42 for (const server of servers) {
50 const res = await listVideoCaptions(server.url, videoUUID) 43 const body = await server.captions.list({ videoId: videoUUID })
51 expect(res.body.total).to.equal(0) 44 expect(body.total).to.equal(0)
52 expect(res.body.data).to.have.lengthOf(0) 45 expect(body.data).to.have.lengthOf(0)
53 } 46 }
54 }) 47 })
55 48
56 it('Should create two new captions', async function () { 49 it('Should create two new captions', async function () {
57 this.timeout(30000) 50 this.timeout(30000)
58 51
59 await createVideoCaption({ 52 await servers[0].captions.add({
60 url: servers[0].url,
61 accessToken: servers[0].accessToken,
62 language: 'ar', 53 language: 'ar',
63 videoId: videoUUID, 54 videoId: videoUUID,
64 fixture: 'subtitle-good1.vtt' 55 fixture: 'subtitle-good1.vtt'
65 }) 56 })
66 57
67 await createVideoCaption({ 58 await servers[0].captions.add({
68 url: servers[0].url,
69 accessToken: servers[0].accessToken,
70 language: 'zh', 59 language: 'zh',
71 videoId: videoUUID, 60 videoId: videoUUID,
72 fixture: 'subtitle-good2.vtt', 61 fixture: 'subtitle-good2.vtt',
@@ -78,17 +67,17 @@ describe('Test video captions', function () {
78 67
79 it('Should list these uploaded captions', async function () { 68 it('Should list these uploaded captions', async function () {
80 for (const server of servers) { 69 for (const server of servers) {
81 const res = await listVideoCaptions(server.url, videoUUID) 70 const body = await server.captions.list({ videoId: videoUUID })
82 expect(res.body.total).to.equal(2) 71 expect(body.total).to.equal(2)
83 expect(res.body.data).to.have.lengthOf(2) 72 expect(body.data).to.have.lengthOf(2)
84 73
85 const caption1: VideoCaption = res.body.data[0] 74 const caption1 = body.data[0]
86 expect(caption1.language.id).to.equal('ar') 75 expect(caption1.language.id).to.equal('ar')
87 expect(caption1.language.label).to.equal('Arabic') 76 expect(caption1.language.label).to.equal('Arabic')
88 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-ar.vtt$')) 77 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-ar.vtt$'))
89 await testCaptionFile(server.url, caption1.captionPath, 'Subtitle good 1.') 78 await testCaptionFile(server.url, caption1.captionPath, 'Subtitle good 1.')
90 79
91 const caption2: VideoCaption = res.body.data[1] 80 const caption2 = body.data[1]
92 expect(caption2.language.id).to.equal('zh') 81 expect(caption2.language.id).to.equal('zh')
93 expect(caption2.language.label).to.equal('Chinese') 82 expect(caption2.language.label).to.equal('Chinese')
94 expect(caption2.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-zh.vtt$')) 83 expect(caption2.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-zh.vtt$'))
@@ -99,9 +88,7 @@ describe('Test video captions', function () {
99 it('Should replace an existing caption', async function () { 88 it('Should replace an existing caption', async function () {
100 this.timeout(30000) 89 this.timeout(30000)
101 90
102 await createVideoCaption({ 91 await servers[0].captions.add({
103 url: servers[0].url,
104 accessToken: servers[0].accessToken,
105 language: 'ar', 92 language: 'ar',
106 videoId: videoUUID, 93 videoId: videoUUID,
107 fixture: 'subtitle-good2.vtt' 94 fixture: 'subtitle-good2.vtt'
@@ -112,11 +99,11 @@ describe('Test video captions', function () {
112 99
113 it('Should have this caption updated', async function () { 100 it('Should have this caption updated', async function () {
114 for (const server of servers) { 101 for (const server of servers) {
115 const res = await listVideoCaptions(server.url, videoUUID) 102 const body = await server.captions.list({ videoId: videoUUID })
116 expect(res.body.total).to.equal(2) 103 expect(body.total).to.equal(2)
117 expect(res.body.data).to.have.lengthOf(2) 104 expect(body.data).to.have.lengthOf(2)
118 105
119 const caption1: VideoCaption = res.body.data[0] 106 const caption1 = body.data[0]
120 expect(caption1.language.id).to.equal('ar') 107 expect(caption1.language.id).to.equal('ar')
121 expect(caption1.language.label).to.equal('Arabic') 108 expect(caption1.language.label).to.equal('Arabic')
122 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-ar.vtt$')) 109 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-ar.vtt$'))
@@ -127,9 +114,7 @@ describe('Test video captions', function () {
127 it('Should replace an existing caption with a srt file and convert it', async function () { 114 it('Should replace an existing caption with a srt file and convert it', async function () {
128 this.timeout(30000) 115 this.timeout(30000)
129 116
130 await createVideoCaption({ 117 await servers[0].captions.add({
131 url: servers[0].url,
132 accessToken: servers[0].accessToken,
133 language: 'ar', 118 language: 'ar',
134 videoId: videoUUID, 119 videoId: videoUUID,
135 fixture: 'subtitle-good.srt' 120 fixture: 'subtitle-good.srt'
@@ -143,11 +128,11 @@ describe('Test video captions', function () {
143 128
144 it('Should have this caption updated and converted', async function () { 129 it('Should have this caption updated and converted', async function () {
145 for (const server of servers) { 130 for (const server of servers) {
146 const res = await listVideoCaptions(server.url, videoUUID) 131 const body = await server.captions.list({ videoId: videoUUID })
147 expect(res.body.total).to.equal(2) 132 expect(body.total).to.equal(2)
148 expect(res.body.data).to.have.lengthOf(2) 133 expect(body.data).to.have.lengthOf(2)
149 134
150 const caption1: VideoCaption = res.body.data[0] 135 const caption1 = body.data[0]
151 expect(caption1.language.id).to.equal('ar') 136 expect(caption1.language.id).to.equal('ar')
152 expect(caption1.language.label).to.equal('Arabic') 137 expect(caption1.language.label).to.equal('Arabic')
153 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-ar.vtt$')) 138 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-ar.vtt$'))
@@ -172,18 +157,18 @@ describe('Test video captions', function () {
172 it('Should remove one caption', async function () { 157 it('Should remove one caption', async function () {
173 this.timeout(30000) 158 this.timeout(30000)
174 159
175 await deleteVideoCaption(servers[0].url, servers[0].accessToken, videoUUID, 'ar') 160 await servers[0].captions.delete({ videoId: videoUUID, language: 'ar' })
176 161
177 await waitJobs(servers) 162 await waitJobs(servers)
178 }) 163 })
179 164
180 it('Should only list the caption that was not deleted', async function () { 165 it('Should only list the caption that was not deleted', async function () {
181 for (const server of servers) { 166 for (const server of servers) {
182 const res = await listVideoCaptions(server.url, videoUUID) 167 const body = await server.captions.list({ videoId: videoUUID })
183 expect(res.body.total).to.equal(1) 168 expect(body.total).to.equal(1)
184 expect(res.body.data).to.have.lengthOf(1) 169 expect(body.data).to.have.lengthOf(1)
185 170
186 const caption: VideoCaption = res.body.data[0] 171 const caption = body.data[0]
187 172
188 expect(caption.language.id).to.equal('zh') 173 expect(caption.language.id).to.equal('zh')
189 expect(caption.language.label).to.equal('Chinese') 174 expect(caption.language.label).to.equal('Chinese')
@@ -193,9 +178,12 @@ describe('Test video captions', function () {
193 }) 178 })
194 179
195 it('Should remove the video, and thus all video captions', async function () { 180 it('Should remove the video, and thus all video captions', async function () {
196 await removeVideo(servers[0].url, servers[0].accessToken, videoUUID) 181 const video = await servers[0].videos.get({ id: videoUUID })
182 const { data: captions } = await servers[0].captions.list({ videoId: videoUUID })
183
184 await servers[0].videos.remove({ id: videoUUID })
197 185
198 await checkVideoFilesWereRemoved(videoUUID, 1) 186 await checkVideoFilesWereRemoved({ server: servers[0], video, captions })
199 }) 187 })
200 188
201 after(async function () { 189 after(async function () {
diff --git a/server/tests/api/videos/video-change-ownership.ts b/server/tests/api/videos/video-change-ownership.ts
index a3384851b..d6665fe4e 100644
--- a/server/tests/api/videos/video-change-ownership.ts
+++ b/server/tests/api/videos/video-change-ownership.ts
@@ -2,234 +2,212 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6import { 5import {
7 acceptChangeOwnership, 6 ChangeOwnershipCommand,
8 changeVideoOwnership,
9 cleanupTests, 7 cleanupTests,
10 createLive, 8 createMultipleServers,
11 createUser, 9 createSingleServer,
12 doubleFollow, 10 doubleFollow,
13 flushAndRunMultipleServers, 11 PeerTubeServer,
14 flushAndRunServer,
15 getMyUserInformation,
16 getVideo,
17 getVideoChangeOwnershipList,
18 getVideosList,
19 refuseChangeOwnership,
20 ServerInfo,
21 setAccessTokensToServers, 12 setAccessTokensToServers,
22 setDefaultVideoChannel, 13 setDefaultVideoChannel,
23 updateCustomSubConfig, 14 waitJobs
24 uploadVideo, 15} from '@shared/extra-utils'
25 userLogin 16import { HttpStatusCode, VideoPrivacy } from '@shared/models'
26} from '../../../../shared/extra-utils'
27import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
28import { User } from '../../../../shared/models/users'
29import { VideoDetails, VideoPrivacy } from '../../../../shared/models/videos'
30 17
31const expect = chai.expect 18const expect = chai.expect
32 19
33describe('Test video change ownership - nominal', function () { 20describe('Test video change ownership - nominal', function () {
34 let servers: ServerInfo[] = [] 21 let servers: PeerTubeServer[] = []
35 const firstUser = { 22
36 username: 'first', 23 const firstUser = 'first'
37 password: 'My great password' 24 const secondUser = 'second'
38 } 25
39 const secondUser = { 26 let firstUserToken = ''
40 username: 'second',
41 password: 'My other password'
42 }
43
44 let firstUserAccessToken = ''
45 let firstUserChannelId: number 27 let firstUserChannelId: number
46 28
47 let secondUserAccessToken = '' 29 let secondUserToken = ''
48 let secondUserChannelId: number 30 let secondUserChannelId: number
49 31
50 let lastRequestChangeOwnershipId = '' 32 let lastRequestId: number
51 33
52 let liveId: number 34 let liveId: number
53 35
36 let command: ChangeOwnershipCommand
37
54 before(async function () { 38 before(async function () {
55 this.timeout(50000) 39 this.timeout(50000)
56 40
57 servers = await flushAndRunMultipleServers(2) 41 servers = await createMultipleServers(2)
58 await setAccessTokensToServers(servers) 42 await setAccessTokensToServers(servers)
59 await setDefaultVideoChannel(servers) 43 await setDefaultVideoChannel(servers)
60 44
61 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 45 await servers[0].config.updateCustomSubConfig({
62 transcoding: { 46 newConfig: {
63 enabled: false 47 transcoding: {
64 }, 48 enabled: false
65 live: { 49 },
66 enabled: true 50 live: {
51 enabled: true
52 }
67 } 53 }
68 }) 54 })
69 55
70 const videoQuota = 42000000 56 firstUserToken = await servers[0].users.generateUserAndToken(firstUser)
71 await createUser({ 57 secondUserToken = await servers[0].users.generateUserAndToken(secondUser)
72 url: servers[0].url,
73 accessToken: servers[0].accessToken,
74 username: firstUser.username,
75 password: firstUser.password,
76 videoQuota: videoQuota
77 })
78 await createUser({
79 url: servers[0].url,
80 accessToken: servers[0].accessToken,
81 username: secondUser.username,
82 password: secondUser.password,
83 videoQuota: videoQuota
84 })
85
86 firstUserAccessToken = await userLogin(servers[0], firstUser)
87 secondUserAccessToken = await userLogin(servers[0], secondUser)
88 58
89 { 59 {
90 const res = await getMyUserInformation(servers[0].url, firstUserAccessToken) 60 const { videoChannels } = await servers[0].users.getMyInfo({ token: firstUserToken })
91 const firstUserInformation: User = res.body 61 firstUserChannelId = videoChannels[0].id
92 firstUserChannelId = firstUserInformation.videoChannels[0].id
93 } 62 }
94 63
95 { 64 {
96 const res = await getMyUserInformation(servers[0].url, secondUserAccessToken) 65 const { videoChannels } = await servers[0].users.getMyInfo({ token: secondUserToken })
97 const secondUserInformation: User = res.body 66 secondUserChannelId = videoChannels[0].id
98 secondUserChannelId = secondUserInformation.videoChannels[0].id
99 } 67 }
100 68
101 { 69 {
102 const videoAttributes = { 70 const attributes = {
103 name: 'my super name', 71 name: 'my super name',
104 description: 'my super description' 72 description: 'my super description'
105 } 73 }
106 const res = await uploadVideo(servers[0].url, firstUserAccessToken, videoAttributes) 74 const { id } = await servers[0].videos.upload({ token: firstUserToken, attributes })
107 75
108 const resVideo = await getVideo(servers[0].url, res.body.video.id) 76 servers[0].store.videoCreated = await servers[0].videos.get({ id })
109 servers[0].video = resVideo.body
110 } 77 }
111 78
112 { 79 {
113 const attributes = { name: 'live', channelId: firstUserChannelId, privacy: VideoPrivacy.PUBLIC } 80 const attributes = { name: 'live', channelId: firstUserChannelId, privacy: VideoPrivacy.PUBLIC }
114 const res = await createLive(servers[0].url, firstUserAccessToken, attributes) 81 const video = await servers[0].live.create({ token: firstUserToken, fields: attributes })
115 82
116 liveId = res.body.video.id 83 liveId = video.id
117 } 84 }
118 85
86 command = servers[0].changeOwnership
87
119 await doubleFollow(servers[0], servers[1]) 88 await doubleFollow(servers[0], servers[1])
120 }) 89 })
121 90
122 it('Should not have video change ownership', async function () { 91 it('Should not have video change ownership', async function () {
123 const resFirstUser = await getVideoChangeOwnershipList(servers[0].url, firstUserAccessToken) 92 {
93 const body = await command.list({ token: firstUserToken })
124 94
125 expect(resFirstUser.body.total).to.equal(0) 95 expect(body.total).to.equal(0)
126 expect(resFirstUser.body.data).to.be.an('array') 96 expect(body.data).to.be.an('array')
127 expect(resFirstUser.body.data.length).to.equal(0) 97 expect(body.data.length).to.equal(0)
98 }
128 99
129 const resSecondUser = await getVideoChangeOwnershipList(servers[0].url, secondUserAccessToken) 100 {
101 const body = await command.list({ token: secondUserToken })
130 102
131 expect(resSecondUser.body.total).to.equal(0) 103 expect(body.total).to.equal(0)
132 expect(resSecondUser.body.data).to.be.an('array') 104 expect(body.data).to.be.an('array')
133 expect(resSecondUser.body.data.length).to.equal(0) 105 expect(body.data.length).to.equal(0)
106 }
134 }) 107 })
135 108
136 it('Should send a request to change ownership of a video', async function () { 109 it('Should send a request to change ownership of a video', async function () {
137 this.timeout(15000) 110 this.timeout(15000)
138 111
139 await changeVideoOwnership(servers[0].url, firstUserAccessToken, servers[0].video.id, secondUser.username) 112 await command.create({ token: firstUserToken, videoId: servers[0].store.videoCreated.id, username: secondUser })
140 }) 113 })
141 114
142 it('Should only return a request to change ownership for the second user', async function () { 115 it('Should only return a request to change ownership for the second user', async function () {
143 const resFirstUser = await getVideoChangeOwnershipList(servers[0].url, firstUserAccessToken) 116 {
117 const body = await command.list({ token: firstUserToken })
144 118
145 expect(resFirstUser.body.total).to.equal(0) 119 expect(body.total).to.equal(0)
146 expect(resFirstUser.body.data).to.be.an('array') 120 expect(body.data).to.be.an('array')
147 expect(resFirstUser.body.data.length).to.equal(0) 121 expect(body.data.length).to.equal(0)
122 }
148 123
149 const resSecondUser = await getVideoChangeOwnershipList(servers[0].url, secondUserAccessToken) 124 {
125 const body = await command.list({ token: secondUserToken })
150 126
151 expect(resSecondUser.body.total).to.equal(1) 127 expect(body.total).to.equal(1)
152 expect(resSecondUser.body.data).to.be.an('array') 128 expect(body.data).to.be.an('array')
153 expect(resSecondUser.body.data.length).to.equal(1) 129 expect(body.data.length).to.equal(1)
154 130
155 lastRequestChangeOwnershipId = resSecondUser.body.data[0].id 131 lastRequestId = body.data[0].id
132 }
156 }) 133 })
157 134
158 it('Should accept the same change ownership request without crashing', async function () { 135 it('Should accept the same change ownership request without crashing', async function () {
159 this.timeout(10000) 136 this.timeout(10000)
160 137
161 await changeVideoOwnership(servers[0].url, firstUserAccessToken, servers[0].video.id, secondUser.username) 138 await command.create({ token: firstUserToken, videoId: servers[0].store.videoCreated.id, username: secondUser })
162 }) 139 })
163 140
164 it('Should not create multiple change ownership requests while one is waiting', async function () { 141 it('Should not create multiple change ownership requests while one is waiting', async function () {
165 this.timeout(10000) 142 this.timeout(10000)
166 143
167 const resSecondUser = await getVideoChangeOwnershipList(servers[0].url, secondUserAccessToken) 144 const body = await command.list({ token: secondUserToken })
168 145
169 expect(resSecondUser.body.total).to.equal(1) 146 expect(body.total).to.equal(1)
170 expect(resSecondUser.body.data).to.be.an('array') 147 expect(body.data).to.be.an('array')
171 expect(resSecondUser.body.data.length).to.equal(1) 148 expect(body.data.length).to.equal(1)
172 }) 149 })
173 150
174 it('Should not be possible to refuse the change of ownership from first user', async function () { 151 it('Should not be possible to refuse the change of ownership from first user', async function () {
175 this.timeout(10000) 152 this.timeout(10000)
176 153
177 await refuseChangeOwnership(servers[0].url, firstUserAccessToken, lastRequestChangeOwnershipId, HttpStatusCode.FORBIDDEN_403) 154 await command.refuse({ token: firstUserToken, ownershipId: lastRequestId, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
178 }) 155 })
179 156
180 it('Should be possible to refuse the change of ownership from second user', async function () { 157 it('Should be possible to refuse the change of ownership from second user', async function () {
181 this.timeout(10000) 158 this.timeout(10000)
182 159
183 await refuseChangeOwnership(servers[0].url, secondUserAccessToken, lastRequestChangeOwnershipId) 160 await command.refuse({ token: secondUserToken, ownershipId: lastRequestId })
184 }) 161 })
185 162
186 it('Should send a new request to change ownership of a video', async function () { 163 it('Should send a new request to change ownership of a video', async function () {
187 this.timeout(15000) 164 this.timeout(15000)
188 165
189 await changeVideoOwnership(servers[0].url, firstUserAccessToken, servers[0].video.id, secondUser.username) 166 await command.create({ token: firstUserToken, videoId: servers[0].store.videoCreated.id, username: secondUser })
190 }) 167 })
191 168
192 it('Should return two requests to change ownership for the second user', async function () { 169 it('Should return two requests to change ownership for the second user', async function () {
193 const resFirstUser = await getVideoChangeOwnershipList(servers[0].url, firstUserAccessToken) 170 {
171 const body = await command.list({ token: firstUserToken })
194 172
195 expect(resFirstUser.body.total).to.equal(0) 173 expect(body.total).to.equal(0)
196 expect(resFirstUser.body.data).to.be.an('array') 174 expect(body.data).to.be.an('array')
197 expect(resFirstUser.body.data.length).to.equal(0) 175 expect(body.data.length).to.equal(0)
176 }
198 177
199 const resSecondUser = await getVideoChangeOwnershipList(servers[0].url, secondUserAccessToken) 178 {
179 const body = await command.list({ token: secondUserToken })
200 180
201 expect(resSecondUser.body.total).to.equal(2) 181 expect(body.total).to.equal(2)
202 expect(resSecondUser.body.data).to.be.an('array') 182 expect(body.data).to.be.an('array')
203 expect(resSecondUser.body.data.length).to.equal(2) 183 expect(body.data.length).to.equal(2)
204 184
205 lastRequestChangeOwnershipId = resSecondUser.body.data[0].id 185 lastRequestId = body.data[0].id
186 }
206 }) 187 })
207 188
208 it('Should not be possible to accept the change of ownership from first user', async function () { 189 it('Should not be possible to accept the change of ownership from first user', async function () {
209 this.timeout(10000) 190 this.timeout(10000)
210 191
211 await acceptChangeOwnership( 192 await command.accept({
212 servers[0].url, 193 token: firstUserToken,
213 firstUserAccessToken, 194 ownershipId: lastRequestId,
214 lastRequestChangeOwnershipId, 195 channelId: secondUserChannelId,
215 secondUserChannelId, 196 expectedStatus: HttpStatusCode.FORBIDDEN_403
216 HttpStatusCode.FORBIDDEN_403 197 })
217 )
218 }) 198 })
219 199
220 it('Should be possible to accept the change of ownership from second user', async function () { 200 it('Should be possible to accept the change of ownership from second user', async function () {
221 this.timeout(10000) 201 this.timeout(10000)
222 202
223 await acceptChangeOwnership(servers[0].url, secondUserAccessToken, lastRequestChangeOwnershipId, secondUserChannelId) 203 await command.accept({ token: secondUserToken, ownershipId: lastRequestId, channelId: secondUserChannelId })
224 204
225 await waitJobs(servers) 205 await waitJobs(servers)
226 }) 206 })
227 207
228 it('Should have the channel of the video updated', async function () { 208 it('Should have the channel of the video updated', async function () {
229 for (const server of servers) { 209 for (const server of servers) {
230 const res = await getVideo(server.url, servers[0].video.uuid) 210 const video = await server.videos.get({ id: servers[0].store.videoCreated.uuid })
231
232 const video: VideoDetails = res.body
233 211
234 expect(video.name).to.equal('my super name') 212 expect(video.name).to.equal('my super name')
235 expect(video.channel.displayName).to.equal('Main second channel') 213 expect(video.channel.displayName).to.equal('Main second channel')
@@ -240,27 +218,25 @@ describe('Test video change ownership - nominal', function () {
240 it('Should send a request to change ownership of a live', async function () { 218 it('Should send a request to change ownership of a live', async function () {
241 this.timeout(15000) 219 this.timeout(15000)
242 220
243 await changeVideoOwnership(servers[0].url, firstUserAccessToken, liveId, secondUser.username) 221 await command.create({ token: firstUserToken, videoId: liveId, username: secondUser })
244 222
245 const resSecondUser = await getVideoChangeOwnershipList(servers[0].url, secondUserAccessToken) 223 const body = await command.list({ token: secondUserToken })
246 224
247 expect(resSecondUser.body.total).to.equal(3) 225 expect(body.total).to.equal(3)
248 expect(resSecondUser.body.data.length).to.equal(3) 226 expect(body.data.length).to.equal(3)
249 227
250 lastRequestChangeOwnershipId = resSecondUser.body.data[0].id 228 lastRequestId = body.data[0].id
251 }) 229 })
252 230
253 it('Should accept a live ownership change', async function () { 231 it('Should accept a live ownership change', async function () {
254 this.timeout(20000) 232 this.timeout(20000)
255 233
256 await acceptChangeOwnership(servers[0].url, secondUserAccessToken, lastRequestChangeOwnershipId, secondUserChannelId) 234 await command.accept({ token: secondUserToken, ownershipId: lastRequestId, channelId: secondUserChannelId })
257 235
258 await waitJobs(servers) 236 await waitJobs(servers)
259 237
260 for (const server of servers) { 238 for (const server of servers) {
261 const res = await getVideo(server.url, servers[0].video.uuid) 239 const video = await server.videos.get({ id: servers[0].store.videoCreated.uuid })
262
263 const video: VideoDetails = res.body
264 240
265 expect(video.name).to.equal('my super name') 241 expect(video.name).to.equal('my super name')
266 expect(video.channel.displayName).to.equal('Main second channel') 242 expect(video.channel.displayName).to.equal('Main second channel')
@@ -274,99 +250,79 @@ describe('Test video change ownership - nominal', function () {
274}) 250})
275 251
276describe('Test video change ownership - quota too small', function () { 252describe('Test video change ownership - quota too small', function () {
277 let server: ServerInfo 253 let server: PeerTubeServer
278 const firstUser = { 254 const firstUser = 'first'
279 username: 'first', 255 const secondUser = 'second'
280 password: 'My great password' 256
281 } 257 let firstUserToken = ''
282 const secondUser = { 258 let secondUserToken = ''
283 username: 'second', 259 let lastRequestId: number
284 password: 'My other password'
285 }
286 let firstUserAccessToken = ''
287 let secondUserAccessToken = ''
288 let lastRequestChangeOwnershipId = ''
289 260
290 before(async function () { 261 before(async function () {
291 this.timeout(50000) 262 this.timeout(50000)
292 263
293 // Run one server 264 // Run one server
294 server = await flushAndRunServer(1) 265 server = await createSingleServer(1)
295 await setAccessTokensToServers([ server ]) 266 await setAccessTokensToServers([ server ])
296 267
297 const videoQuota = 42000000 268 await server.users.create({ username: secondUser, videoQuota: 10 })
298 const limitedVideoQuota = 10
299 await createUser({
300 url: server.url,
301 accessToken: server.accessToken,
302 username: firstUser.username,
303 password: firstUser.password,
304 videoQuota: videoQuota
305 })
306 await createUser({
307 url: server.url,
308 accessToken: server.accessToken,
309 username: secondUser.username,
310 password: secondUser.password,
311 videoQuota: limitedVideoQuota
312 })
313 269
314 firstUserAccessToken = await userLogin(server, firstUser) 270 firstUserToken = await server.users.generateUserAndToken(firstUser)
315 secondUserAccessToken = await userLogin(server, secondUser) 271 secondUserToken = await server.login.getAccessToken(secondUser)
316 272
317 // Upload some videos on the server 273 // Upload some videos on the server
318 const video1Attributes = { 274 const attributes = {
319 name: 'my super name', 275 name: 'my super name',
320 description: 'my super description' 276 description: 'my super description'
321 } 277 }
322 await uploadVideo(server.url, firstUserAccessToken, video1Attributes) 278 await server.videos.upload({ token: firstUserToken, attributes })
323 279
324 await waitJobs(server) 280 await waitJobs(server)
325 281
326 const res = await getVideosList(server.url) 282 const { data } = await server.videos.list()
327 const videos = res.body.data 283 expect(data.length).to.equal(1)
328
329 expect(videos.length).to.equal(1)
330 284
331 server.video = videos.find(video => video.name === 'my super name') 285 server.store.videoCreated = data.find(video => video.name === 'my super name')
332 }) 286 })
333 287
334 it('Should send a request to change ownership of a video', async function () { 288 it('Should send a request to change ownership of a video', async function () {
335 this.timeout(15000) 289 this.timeout(15000)
336 290
337 await changeVideoOwnership(server.url, firstUserAccessToken, server.video.id, secondUser.username) 291 await server.changeOwnership.create({ token: firstUserToken, videoId: server.store.videoCreated.id, username: secondUser })
338 }) 292 })
339 293
340 it('Should only return a request to change ownership for the second user', async function () { 294 it('Should only return a request to change ownership for the second user', async function () {
341 const resFirstUser = await getVideoChangeOwnershipList(server.url, firstUserAccessToken) 295 {
296 const body = await server.changeOwnership.list({ token: firstUserToken })
342 297
343 expect(resFirstUser.body.total).to.equal(0) 298 expect(body.total).to.equal(0)
344 expect(resFirstUser.body.data).to.be.an('array') 299 expect(body.data).to.be.an('array')
345 expect(resFirstUser.body.data.length).to.equal(0) 300 expect(body.data.length).to.equal(0)
301 }
346 302
347 const resSecondUser = await getVideoChangeOwnershipList(server.url, secondUserAccessToken) 303 {
304 const body = await server.changeOwnership.list({ token: secondUserToken })
348 305
349 expect(resSecondUser.body.total).to.equal(1) 306 expect(body.total).to.equal(1)
350 expect(resSecondUser.body.data).to.be.an('array') 307 expect(body.data).to.be.an('array')
351 expect(resSecondUser.body.data.length).to.equal(1) 308 expect(body.data.length).to.equal(1)
352 309
353 lastRequestChangeOwnershipId = resSecondUser.body.data[0].id 310 lastRequestId = body.data[0].id
311 }
354 }) 312 })
355 313
356 it('Should not be possible to accept the change of ownership from second user because of exceeded quota', async function () { 314 it('Should not be possible to accept the change of ownership from second user because of exceeded quota', async function () {
357 this.timeout(10000) 315 this.timeout(10000)
358 316
359 const secondUserInformationResponse = await getMyUserInformation(server.url, secondUserAccessToken) 317 const { videoChannels } = await server.users.getMyInfo({ token: secondUserToken })
360 const secondUserInformation: User = secondUserInformationResponse.body 318 const channelId = videoChannels[0].id
361 const channelId = secondUserInformation.videoChannels[0].id
362 319
363 await acceptChangeOwnership( 320 await server.changeOwnership.accept({
364 server.url, 321 token: secondUserToken,
365 secondUserAccessToken, 322 ownershipId: lastRequestId,
366 lastRequestChangeOwnershipId,
367 channelId, 323 channelId,
368 HttpStatusCode.PAYLOAD_TOO_LARGE_413 324 expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413
369 ) 325 })
370 }) 326 })
371 327
372 after(async function () { 328 after(async function () {
diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts
index 865098777..c25754eb6 100644
--- a/server/tests/api/videos/video-channels.ts
+++ b/server/tests/api/videos/video-channels.ts
@@ -6,48 +6,28 @@ import { basename } from 'path'
6import { ACTOR_IMAGES_SIZE } from '@server/initializers/constants' 6import { ACTOR_IMAGES_SIZE } from '@server/initializers/constants'
7import { 7import {
8 cleanupTests, 8 cleanupTests,
9 createUser, 9 createMultipleServers,
10 deleteVideoChannelImage,
11 doubleFollow, 10 doubleFollow,
12 flushAndRunMultipleServers, 11 PeerTubeServer,
13 getActorImage, 12 setAccessTokensToServers,
14 getVideo,
15 getVideoChannel,
16 getVideoChannelVideos,
17 setDefaultVideoChannel, 13 setDefaultVideoChannel,
18 testFileExistsOrNot, 14 testFileExistsOrNot,
19 testImage, 15 testImage,
20 updateVideo, 16 wait,
21 updateVideoChannelImage, 17 waitJobs
22 uploadVideo, 18} from '@shared/extra-utils'
23 userLogin, 19import { User, VideoChannel } from '@shared/models'
24 wait
25} from '../../../../shared/extra-utils'
26import {
27 addVideoChannel,
28 deleteVideoChannel,
29 getAccountVideoChannelsList,
30 getMyUserInformation,
31 getVideoChannelsList,
32 ServerInfo,
33 setAccessTokensToServers,
34 updateVideoChannel,
35 viewVideo
36} from '../../../../shared/extra-utils/index'
37import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
38import { User, Video, VideoChannel, VideoDetails } from '../../../../shared/index'
39 20
40const expect = chai.expect 21const expect = chai.expect
41 22
42async function findChannel (server: ServerInfo, channelId: number) { 23async function findChannel (server: PeerTubeServer, channelId: number) {
43 const res = await getVideoChannelsList(server.url, 0, 5, '-name') 24 const body = await server.channels.list({ sort: '-name' })
44 const videoChannel = res.body.data.find(c => c.id === channelId)
45 25
46 return videoChannel as VideoChannel 26 return body.data.find(c => c.id === channelId)
47} 27}
48 28
49describe('Test video channels', function () { 29describe('Test video channels', function () {
50 let servers: ServerInfo[] 30 let servers: PeerTubeServer[]
51 let userInfo: User 31 let userInfo: User
52 let secondVideoChannelId: number 32 let secondVideoChannelId: number
53 let totoChannel: number 33 let totoChannel: number
@@ -60,7 +40,7 @@ describe('Test video channels', function () {
60 before(async function () { 40 before(async function () {
61 this.timeout(60000) 41 this.timeout(60000)
62 42
63 servers = await flushAndRunMultipleServers(2) 43 servers = await createMultipleServers(2)
64 44
65 await setAccessTokensToServers(servers) 45 await setAccessTokensToServers(servers)
66 await setDefaultVideoChannel(servers) 46 await setDefaultVideoChannel(servers)
@@ -69,11 +49,11 @@ describe('Test video channels', function () {
69 }) 49 })
70 50
71 it('Should have one video channel (created with root)', async () => { 51 it('Should have one video channel (created with root)', async () => {
72 const res = await getVideoChannelsList(servers[0].url, 0, 2) 52 const body = await servers[0].channels.list({ start: 0, count: 2 })
73 53
74 expect(res.body.total).to.equal(1) 54 expect(body.total).to.equal(1)
75 expect(res.body.data).to.be.an('array') 55 expect(body.data).to.be.an('array')
76 expect(res.body.data).to.have.lengthOf(1) 56 expect(body.data).to.have.lengthOf(1)
77 }) 57 })
78 58
79 it('Should create another video channel', async function () { 59 it('Should create another video channel', async function () {
@@ -86,23 +66,22 @@ describe('Test video channels', function () {
86 description: 'super video channel description', 66 description: 'super video channel description',
87 support: 'super video channel support text' 67 support: 'super video channel support text'
88 } 68 }
89 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel) 69 const created = await servers[0].channels.create({ attributes: videoChannel })
90 secondVideoChannelId = res.body.videoChannel.id 70 secondVideoChannelId = created.id
91 } 71 }
92 72
93 // The channel is 1 is propagated to servers 2 73 // The channel is 1 is propagated to servers 2
94 { 74 {
95 const videoAttributesArg = { name: 'my video name', channelId: secondVideoChannelId, support: 'video support field' } 75 const attributes = { name: 'my video name', channelId: secondVideoChannelId, support: 'video support field' }
96 const res = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributesArg) 76 const { uuid } = await servers[0].videos.upload({ attributes })
97 videoUUID = res.body.video.uuid 77 videoUUID = uuid
98 } 78 }
99 79
100 await waitJobs(servers) 80 await waitJobs(servers)
101 }) 81 })
102 82
103 it('Should have two video channels when getting my information', async () => { 83 it('Should have two video channels when getting my information', async () => {
104 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) 84 userInfo = await servers[0].users.getMyInfo()
105 userInfo = res.body
106 85
107 expect(userInfo.videoChannels).to.be.an('array') 86 expect(userInfo.videoChannels).to.be.an('array')
108 expect(userInfo.videoChannels).to.have.lengthOf(2) 87 expect(userInfo.videoChannels).to.have.lengthOf(2)
@@ -120,16 +99,14 @@ describe('Test video channels', function () {
120 }) 99 })
121 100
122 it('Should have two video channels when getting account channels on server 1', async function () { 101 it('Should have two video channels when getting account channels on server 1', async function () {
123 const res = await getAccountVideoChannelsList({ 102 const body = await servers[0].channels.listByAccount({ accountName })
124 url: servers[0].url, 103 expect(body.total).to.equal(2)
125 accountName 104
126 }) 105 const videoChannels = body.data
127 106
128 expect(res.body.total).to.equal(2) 107 expect(videoChannels).to.be.an('array')
129 expect(res.body.data).to.be.an('array') 108 expect(videoChannels).to.have.lengthOf(2)
130 expect(res.body.data).to.have.lengthOf(2)
131 109
132 const videoChannels = res.body.data
133 expect(videoChannels[0].name).to.equal('root_channel') 110 expect(videoChannels[0].name).to.equal('root_channel')
134 expect(videoChannels[0].displayName).to.equal('Main root channel') 111 expect(videoChannels[0].displayName).to.equal('Main root channel')
135 112
@@ -141,79 +118,69 @@ describe('Test video channels', function () {
141 118
142 it('Should paginate and sort account channels', async function () { 119 it('Should paginate and sort account channels', async function () {
143 { 120 {
144 const res = await getAccountVideoChannelsList({ 121 const body = await servers[0].channels.listByAccount({
145 url: servers[0].url,
146 accountName, 122 accountName,
147 start: 0, 123 start: 0,
148 count: 1, 124 count: 1,
149 sort: 'createdAt' 125 sort: 'createdAt'
150 }) 126 })
151 127
152 expect(res.body.total).to.equal(2) 128 expect(body.total).to.equal(2)
153 expect(res.body.data).to.have.lengthOf(1) 129 expect(body.data).to.have.lengthOf(1)
154 130
155 const videoChannel: VideoChannel = res.body.data[0] 131 const videoChannel: VideoChannel = body.data[0]
156 expect(videoChannel.name).to.equal('root_channel') 132 expect(videoChannel.name).to.equal('root_channel')
157 } 133 }
158 134
159 { 135 {
160 const res = await getAccountVideoChannelsList({ 136 const body = await servers[0].channels.listByAccount({
161 url: servers[0].url,
162 accountName, 137 accountName,
163 start: 0, 138 start: 0,
164 count: 1, 139 count: 1,
165 sort: '-createdAt' 140 sort: '-createdAt'
166 }) 141 })
167 142
168 expect(res.body.total).to.equal(2) 143 expect(body.total).to.equal(2)
169 expect(res.body.data).to.have.lengthOf(1) 144 expect(body.data).to.have.lengthOf(1)
170 145 expect(body.data[0].name).to.equal('second_video_channel')
171 const videoChannel: VideoChannel = res.body.data[0]
172 expect(videoChannel.name).to.equal('second_video_channel')
173 } 146 }
174 147
175 { 148 {
176 const res = await getAccountVideoChannelsList({ 149 const body = await servers[0].channels.listByAccount({
177 url: servers[0].url,
178 accountName, 150 accountName,
179 start: 1, 151 start: 1,
180 count: 1, 152 count: 1,
181 sort: '-createdAt' 153 sort: '-createdAt'
182 }) 154 })
183 155
184 expect(res.body.total).to.equal(2) 156 expect(body.total).to.equal(2)
185 expect(res.body.data).to.have.lengthOf(1) 157 expect(body.data).to.have.lengthOf(1)
186 158 expect(body.data[0].name).to.equal('root_channel')
187 const videoChannel: VideoChannel = res.body.data[0]
188 expect(videoChannel.name).to.equal('root_channel')
189 } 159 }
190 }) 160 })
191 161
192 it('Should have one video channel when getting account channels on server 2', async function () { 162 it('Should have one video channel when getting account channels on server 2', async function () {
193 const res = await getAccountVideoChannelsList({ 163 const body = await servers[1].channels.listByAccount({ accountName })
194 url: servers[1].url,
195 accountName
196 })
197 164
198 expect(res.body.total).to.equal(1) 165 expect(body.total).to.equal(1)
199 expect(res.body.data).to.be.an('array') 166 expect(body.data).to.be.an('array')
200 expect(res.body.data).to.have.lengthOf(1) 167 expect(body.data).to.have.lengthOf(1)
201 168
202 const videoChannels = res.body.data 169 const videoChannel = body.data[0]
203 expect(videoChannels[0].name).to.equal('second_video_channel') 170 expect(videoChannel.name).to.equal('second_video_channel')
204 expect(videoChannels[0].displayName).to.equal('second video channel') 171 expect(videoChannel.displayName).to.equal('second video channel')
205 expect(videoChannels[0].description).to.equal('super video channel description') 172 expect(videoChannel.description).to.equal('super video channel description')
206 expect(videoChannels[0].support).to.equal('super video channel support text') 173 expect(videoChannel.support).to.equal('super video channel support text')
207 }) 174 })
208 175
209 it('Should list video channels', async function () { 176 it('Should list video channels', async function () {
210 const res = await getVideoChannelsList(servers[0].url, 1, 1, '-name') 177 const body = await servers[0].channels.list({ start: 1, count: 1, sort: '-name' })
211 178
212 expect(res.body.total).to.equal(2) 179 expect(body.total).to.equal(2)
213 expect(res.body.data).to.be.an('array') 180 expect(body.data).to.be.an('array')
214 expect(res.body.data).to.have.lengthOf(1) 181 expect(body.data).to.have.lengthOf(1)
215 expect(res.body.data[0].name).to.equal('root_channel') 182 expect(body.data[0].name).to.equal('root_channel')
216 expect(res.body.data[0].displayName).to.equal('Main root channel') 183 expect(body.data[0].displayName).to.equal('Main root channel')
217 }) 184 })
218 185
219 it('Should update video channel', async function () { 186 it('Should update video channel', async function () {
@@ -225,30 +192,29 @@ describe('Test video channels', function () {
225 support: 'support updated' 192 support: 'support updated'
226 } 193 }
227 194
228 await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes) 195 await servers[0].channels.update({ channelName: 'second_video_channel', attributes: videoChannelAttributes })
229 196
230 await waitJobs(servers) 197 await waitJobs(servers)
231 }) 198 })
232 199
233 it('Should have video channel updated', async function () { 200 it('Should have video channel updated', async function () {
234 for (const server of servers) { 201 for (const server of servers) {
235 const res = await getVideoChannelsList(server.url, 0, 1, '-name') 202 const body = await server.channels.list({ start: 0, count: 1, sort: '-name' })
236 203
237 expect(res.body.total).to.equal(2) 204 expect(body.total).to.equal(2)
238 expect(res.body.data).to.be.an('array') 205 expect(body.data).to.be.an('array')
239 expect(res.body.data).to.have.lengthOf(1) 206 expect(body.data).to.have.lengthOf(1)
240 expect(res.body.data[0].name).to.equal('second_video_channel') 207
241 expect(res.body.data[0].displayName).to.equal('video channel updated') 208 expect(body.data[0].name).to.equal('second_video_channel')
242 expect(res.body.data[0].description).to.equal('video channel description updated') 209 expect(body.data[0].displayName).to.equal('video channel updated')
243 expect(res.body.data[0].support).to.equal('support updated') 210 expect(body.data[0].description).to.equal('video channel description updated')
211 expect(body.data[0].support).to.equal('support updated')
244 } 212 }
245 }) 213 })
246 214
247 it('Should not have updated the video support field', async function () { 215 it('Should not have updated the video support field', async function () {
248 for (const server of servers) { 216 for (const server of servers) {
249 const res = await getVideo(server.url, videoUUID) 217 const video = await server.videos.get({ id: videoUUID })
250 const video: VideoDetails = res.body
251
252 expect(video.support).to.equal('video support field') 218 expect(video.support).to.equal('video support field')
253 } 219 }
254 }) 220 })
@@ -261,14 +227,12 @@ describe('Test video channels', function () {
261 bulkVideosSupportUpdate: true 227 bulkVideosSupportUpdate: true
262 } 228 }
263 229
264 await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes) 230 await servers[0].channels.update({ channelName: 'second_video_channel', attributes: videoChannelAttributes })
265 231
266 await waitJobs(servers) 232 await waitJobs(servers)
267 233
268 for (const server of servers) { 234 for (const server of servers) {
269 const res = await getVideo(server.url, videoUUID) 235 const video = await server.videos.get({ id: videoUUID })
270 const video: VideoDetails = res.body
271
272 expect(video.support).to.equal(videoChannelAttributes.support) 236 expect(video.support).to.equal(videoChannelAttributes.support)
273 } 237 }
274 }) 238 })
@@ -278,10 +242,8 @@ describe('Test video channels', function () {
278 242
279 const fixture = 'avatar.png' 243 const fixture = 'avatar.png'
280 244
281 await updateVideoChannelImage({ 245 await servers[0].channels.updateImage({
282 url: servers[0].url, 246 channelName: 'second_video_channel',
283 accessToken: servers[0].accessToken,
284 videoChannelName: 'second_video_channel',
285 fixture, 247 fixture,
286 type: 'avatar' 248 type: 'avatar'
287 }) 249 })
@@ -295,7 +257,7 @@ describe('Test video channels', function () {
295 await testImage(server.url, 'avatar-resized', avatarPaths[server.port], '.png') 257 await testImage(server.url, 'avatar-resized', avatarPaths[server.port], '.png')
296 await testFileExistsOrNot(server, 'avatars', basename(avatarPaths[server.port]), true) 258 await testFileExistsOrNot(server, 'avatars', basename(avatarPaths[server.port]), true)
297 259
298 const row = await getActorImage(server.internalServerNumber, basename(avatarPaths[server.port])) 260 const row = await server.sql.getActorImage(basename(avatarPaths[server.port]))
299 expect(row.height).to.equal(ACTOR_IMAGES_SIZE.AVATARS.height) 261 expect(row.height).to.equal(ACTOR_IMAGES_SIZE.AVATARS.height)
300 expect(row.width).to.equal(ACTOR_IMAGES_SIZE.AVATARS.width) 262 expect(row.width).to.equal(ACTOR_IMAGES_SIZE.AVATARS.width)
301 } 263 }
@@ -306,10 +268,8 @@ describe('Test video channels', function () {
306 268
307 const fixture = 'banner.jpg' 269 const fixture = 'banner.jpg'
308 270
309 await updateVideoChannelImage({ 271 await servers[0].channels.updateImage({
310 url: servers[0].url, 272 channelName: 'second_video_channel',
311 accessToken: servers[0].accessToken,
312 videoChannelName: 'second_video_channel',
313 fixture, 273 fixture,
314 type: 'banner' 274 type: 'banner'
315 }) 275 })
@@ -317,14 +277,13 @@ describe('Test video channels', function () {
317 await waitJobs(servers) 277 await waitJobs(servers)
318 278
319 for (const server of servers) { 279 for (const server of servers) {
320 const res = await getVideoChannel(server.url, 'second_video_channel@' + servers[0].host) 280 const videoChannel = await server.channels.get({ channelName: 'second_video_channel@' + servers[0].host })
321 const videoChannel = res.body
322 281
323 bannerPaths[server.port] = videoChannel.banner.path 282 bannerPaths[server.port] = videoChannel.banner.path
324 await testImage(server.url, 'banner-resized', bannerPaths[server.port]) 283 await testImage(server.url, 'banner-resized', bannerPaths[server.port])
325 await testFileExistsOrNot(server, 'avatars', basename(bannerPaths[server.port]), true) 284 await testFileExistsOrNot(server, 'avatars', basename(bannerPaths[server.port]), true)
326 285
327 const row = await getActorImage(server.internalServerNumber, basename(bannerPaths[server.port])) 286 const row = await server.sql.getActorImage(basename(bannerPaths[server.port]))
328 expect(row.height).to.equal(ACTOR_IMAGES_SIZE.BANNERS.height) 287 expect(row.height).to.equal(ACTOR_IMAGES_SIZE.BANNERS.height)
329 expect(row.width).to.equal(ACTOR_IMAGES_SIZE.BANNERS.width) 288 expect(row.width).to.equal(ACTOR_IMAGES_SIZE.BANNERS.width)
330 } 289 }
@@ -333,12 +292,7 @@ describe('Test video channels', function () {
333 it('Should delete the video channel avatar', async function () { 292 it('Should delete the video channel avatar', async function () {
334 this.timeout(15000) 293 this.timeout(15000)
335 294
336 await deleteVideoChannelImage({ 295 await servers[0].channels.deleteImage({ channelName: 'second_video_channel', type: 'avatar' })
337 url: servers[0].url,
338 accessToken: servers[0].accessToken,
339 videoChannelName: 'second_video_channel',
340 type: 'avatar'
341 })
342 296
343 await waitJobs(servers) 297 await waitJobs(servers)
344 298
@@ -353,12 +307,7 @@ describe('Test video channels', function () {
353 it('Should delete the video channel banner', async function () { 307 it('Should delete the video channel banner', async function () {
354 this.timeout(15000) 308 this.timeout(15000)
355 309
356 await deleteVideoChannelImage({ 310 await servers[0].channels.deleteImage({ channelName: 'second_video_channel', type: 'banner' })
357 url: servers[0].url,
358 accessToken: servers[0].accessToken,
359 videoChannelName: 'second_video_channel',
360 type: 'banner'
361 })
362 311
363 await waitJobs(servers) 312 await waitJobs(servers)
364 313
@@ -375,18 +324,19 @@ describe('Test video channels', function () {
375 324
376 for (const server of servers) { 325 for (const server of servers) {
377 const channelURI = 'second_video_channel@localhost:' + servers[0].port 326 const channelURI = 'second_video_channel@localhost:' + servers[0].port
378 const res1 = await getVideoChannelVideos(server.url, server.accessToken, channelURI, 0, 5) 327 const { total, data } = await server.videos.listByChannel({ handle: channelURI })
379 expect(res1.body.total).to.equal(1) 328
380 expect(res1.body.data).to.be.an('array') 329 expect(total).to.equal(1)
381 expect(res1.body.data).to.have.lengthOf(1) 330 expect(data).to.be.an('array')
382 expect(res1.body.data[0].name).to.equal('my video name') 331 expect(data).to.have.lengthOf(1)
332 expect(data[0].name).to.equal('my video name')
383 } 333 }
384 }) 334 })
385 335
386 it('Should change the video channel of a video', async function () { 336 it('Should change the video channel of a video', async function () {
387 this.timeout(10000) 337 this.timeout(10000)
388 338
389 await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, { channelId: servers[0].videoChannel.id }) 339 await servers[0].videos.update({ id: videoUUID, attributes: { channelId: servers[0].store.channel.id } })
390 340
391 await waitJobs(servers) 341 await waitJobs(servers)
392 }) 342 })
@@ -395,47 +345,50 @@ describe('Test video channels', function () {
395 this.timeout(10000) 345 this.timeout(10000)
396 346
397 for (const server of servers) { 347 for (const server of servers) {
398 const secondChannelURI = 'second_video_channel@localhost:' + servers[0].port 348 {
399 const res1 = await getVideoChannelVideos(server.url, server.accessToken, secondChannelURI, 0, 5) 349 const secondChannelURI = 'second_video_channel@localhost:' + servers[0].port
400 expect(res1.body.total).to.equal(0) 350 const { total } = await server.videos.listByChannel({ handle: secondChannelURI })
401 351 expect(total).to.equal(0)
402 const channelURI = 'root_channel@localhost:' + servers[0].port 352 }
403 const res2 = await getVideoChannelVideos(server.url, server.accessToken, channelURI, 0, 5) 353
404 expect(res2.body.total).to.equal(1) 354 {
405 355 const channelURI = 'root_channel@localhost:' + servers[0].port
406 const videos: Video[] = res2.body.data 356 const { total, data } = await server.videos.listByChannel({ handle: channelURI })
407 expect(videos).to.be.an('array') 357 expect(total).to.equal(1)
408 expect(videos).to.have.lengthOf(1) 358
409 expect(videos[0].name).to.equal('my video name') 359 expect(data).to.be.an('array')
360 expect(data).to.have.lengthOf(1)
361 expect(data[0].name).to.equal('my video name')
362 }
410 } 363 }
411 }) 364 })
412 365
413 it('Should delete video channel', async function () { 366 it('Should delete video channel', async function () {
414 await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel') 367 await servers[0].channels.delete({ channelName: 'second_video_channel' })
415 }) 368 })
416 369
417 it('Should have video channel deleted', async function () { 370 it('Should have video channel deleted', async function () {
418 const res = await getVideoChannelsList(servers[0].url, 0, 10) 371 const body = await servers[0].channels.list({ start: 0, count: 10 })
419 372
420 expect(res.body.total).to.equal(1) 373 expect(body.total).to.equal(1)
421 expect(res.body.data).to.be.an('array') 374 expect(body.data).to.be.an('array')
422 expect(res.body.data).to.have.lengthOf(1) 375 expect(body.data).to.have.lengthOf(1)
423 expect(res.body.data[0].displayName).to.equal('Main root channel') 376 expect(body.data[0].displayName).to.equal('Main root channel')
424 }) 377 })
425 378
426 it('Should create the main channel with an uuid if there is a conflict', async function () { 379 it('Should create the main channel with an uuid if there is a conflict', async function () {
427 { 380 {
428 const videoChannel = { name: 'toto_channel', displayName: 'My toto channel' } 381 const videoChannel = { name: 'toto_channel', displayName: 'My toto channel' }
429 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel) 382 const created = await servers[0].channels.create({ attributes: videoChannel })
430 totoChannel = res.body.videoChannel.id 383 totoChannel = created.id
431 } 384 }
432 385
433 { 386 {
434 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: 'toto', password: 'password' }) 387 await servers[0].users.create({ username: 'toto', password: 'password' })
435 const accessToken = await userLogin(servers[0], { username: 'toto', password: 'password' }) 388 const accessToken = await servers[0].login.getAccessToken({ username: 'toto', password: 'password' })
436 389
437 const res = await getMyUserInformation(servers[0].url, accessToken) 390 const { videoChannels } = await servers[0].users.getMyInfo({ token: accessToken })
438 const videoChannel = res.body.videoChannels[0] 391 const videoChannel = videoChannels[0]
439 expect(videoChannel.name).to.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/) 392 expect(videoChannel.name).to.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/)
440 } 393 }
441 }) 394 })
@@ -444,15 +397,9 @@ describe('Test video channels', function () {
444 this.timeout(10000) 397 this.timeout(10000)
445 398
446 { 399 {
447 const res = await getAccountVideoChannelsList({ 400 const { data } = await servers[0].channels.listByAccount({ accountName, withStats: true })
448 url: servers[0].url,
449 accountName,
450 withStats: true
451 })
452
453 const channels: VideoChannel[] = res.body.data
454 401
455 for (const channel of channels) { 402 for (const channel of data) {
456 expect(channel).to.haveOwnProperty('viewsPerDay') 403 expect(channel).to.haveOwnProperty('viewsPerDay')
457 expect(channel.viewsPerDay).to.have.length(30 + 1) // daysPrior + today 404 expect(channel.viewsPerDay).to.have.length(30 + 1) // daysPrior + today
458 405
@@ -464,33 +411,24 @@ describe('Test video channels', function () {
464 } 411 }
465 412
466 { 413 {
467 // video has been posted on channel servers[0].videoChannel.id since last update 414 // video has been posted on channel servers[0].store.videoChannel.id since last update
468 await viewVideo(servers[0].url, videoUUID, 204, '0.0.0.1,127.0.0.1') 415 await servers[0].videos.view({ id: videoUUID, xForwardedFor: '0.0.0.1,127.0.0.1' })
469 await viewVideo(servers[0].url, videoUUID, 204, '0.0.0.2,127.0.0.1') 416 await servers[0].videos.view({ id: videoUUID, xForwardedFor: '0.0.0.2,127.0.0.1' })
470 417
471 // Wait the repeatable job 418 // Wait the repeatable job
472 await wait(8000) 419 await wait(8000)
473 420
474 const res = await getAccountVideoChannelsList({ 421 const { data } = await servers[0].channels.listByAccount({ accountName, withStats: true })
475 url: servers[0].url, 422 const channelWithView = data.find(channel => channel.id === servers[0].store.channel.id)
476 accountName,
477 withStats: true
478 })
479 const channelWithView = res.body.data.find((channel: VideoChannel) => channel.id === servers[0].videoChannel.id)
480 expect(channelWithView.viewsPerDay.slice(-1)[0].views).to.equal(2) 423 expect(channelWithView.viewsPerDay.slice(-1)[0].views).to.equal(2)
481 } 424 }
482 }) 425 })
483 426
484 it('Should report correct videos count', async function () { 427 it('Should report correct videos count', async function () {
485 const res = await getAccountVideoChannelsList({ 428 const { data } = await servers[0].channels.listByAccount({ accountName, withStats: true })
486 url: servers[0].url,
487 accountName,
488 withStats: true
489 })
490 const channels: VideoChannel[] = res.body.data
491 429
492 const totoChannel = channels.find(c => c.name === 'toto_channel') 430 const totoChannel = data.find(c => c.name === 'toto_channel')
493 const rootChannel = channels.find(c => c.name === 'root_channel') 431 const rootChannel = data.find(c => c.name === 'root_channel')
494 432
495 expect(rootChannel.videosCount).to.equal(1) 433 expect(rootChannel.videosCount).to.equal(1)
496 expect(totoChannel.videosCount).to.equal(0) 434 expect(totoChannel.videosCount).to.equal(0)
@@ -498,26 +436,18 @@ describe('Test video channels', function () {
498 436
499 it('Should search among account video channels', async function () { 437 it('Should search among account video channels', async function () {
500 { 438 {
501 const res = await getAccountVideoChannelsList({ 439 const body = await servers[0].channels.listByAccount({ accountName, search: 'root' })
502 url: servers[0].url, 440 expect(body.total).to.equal(1)
503 accountName,
504 search: 'root'
505 })
506 expect(res.body.total).to.equal(1)
507 441
508 const channels = res.body.data 442 const channels = body.data
509 expect(channels).to.have.lengthOf(1) 443 expect(channels).to.have.lengthOf(1)
510 } 444 }
511 445
512 { 446 {
513 const res = await getAccountVideoChannelsList({ 447 const body = await servers[0].channels.listByAccount({ accountName, search: 'does not exist' })
514 url: servers[0].url, 448 expect(body.total).to.equal(0)
515 accountName,
516 search: 'does not exist'
517 })
518 expect(res.body.total).to.equal(0)
519 449
520 const channels = res.body.data 450 const channels = body.data
521 expect(channels).to.have.lengthOf(0) 451 expect(channels).to.have.lengthOf(0)
522 } 452 }
523 }) 453 })
@@ -525,34 +455,24 @@ describe('Test video channels', function () {
525 it('Should list channels by updatedAt desc if a video has been uploaded', async function () { 455 it('Should list channels by updatedAt desc if a video has been uploaded', async function () {
526 this.timeout(30000) 456 this.timeout(30000)
527 457
528 await uploadVideo(servers[0].url, servers[0].accessToken, { channelId: totoChannel }) 458 await servers[0].videos.upload({ attributes: { channelId: totoChannel } })
529 await waitJobs(servers) 459 await waitJobs(servers)
530 460
531 for (const server of servers) { 461 for (const server of servers) {
532 const res = await getAccountVideoChannelsList({ 462 const { data } = await server.channels.listByAccount({ accountName, sort: '-updatedAt' })
533 url: server.url,
534 accountName,
535 sort: '-updatedAt'
536 })
537 463
538 const channels: VideoChannel[] = res.body.data 464 expect(data[0].name).to.equal('toto_channel')
539 expect(channels[0].name).to.equal('toto_channel') 465 expect(data[1].name).to.equal('root_channel')
540 expect(channels[1].name).to.equal('root_channel')
541 } 466 }
542 467
543 await uploadVideo(servers[0].url, servers[0].accessToken, { channelId: servers[0].videoChannel.id }) 468 await servers[0].videos.upload({ attributes: { channelId: servers[0].store.channel.id } })
544 await waitJobs(servers) 469 await waitJobs(servers)
545 470
546 for (const server of servers) { 471 for (const server of servers) {
547 const res = await getAccountVideoChannelsList({ 472 const { data } = await server.channels.listByAccount({ accountName, sort: '-updatedAt' })
548 url: server.url,
549 accountName,
550 sort: '-updatedAt'
551 })
552 473
553 const channels: VideoChannel[] = res.body.data 474 expect(data[0].name).to.equal('root_channel')
554 expect(channels[0].name).to.equal('root_channel') 475 expect(data[1].name).to.equal('toto_channel')
555 expect(channels[1].name).to.equal('toto_channel')
556 } 476 }
557 }) 477 })
558 478
diff --git a/server/tests/api/videos/video-comments.ts b/server/tests/api/videos/video-comments.ts
index b6b002307..61ee54540 100644
--- a/server/tests/api/videos/video-comments.ts
+++ b/server/tests/api/videos/video-comments.ts
@@ -2,80 +2,62 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoComment, VideoCommentAdmin, VideoCommentThreadTree } from '@shared/models'
6import { cleanupTests, testImage } from '../../../../shared/extra-utils'
7import { 5import {
8 createUser, 6 cleanupTests,
7 CommentsCommand,
8 createSingleServer,
9 dateIsValid, 9 dateIsValid,
10 flushAndRunServer, 10 PeerTubeServer,
11 getAccessToken,
12 ServerInfo,
13 setAccessTokensToServers, 11 setAccessTokensToServers,
14 updateMyAvatar, 12 testImage
15 uploadVideo 13} from '@shared/extra-utils'
16} from '../../../../shared/extra-utils/index'
17import {
18 addVideoCommentReply,
19 addVideoCommentThread,
20 deleteVideoComment,
21 getAdminVideoComments,
22 getVideoCommentThreads,
23 getVideoThreadComments
24} from '../../../../shared/extra-utils/videos/video-comments'
25 14
26const expect = chai.expect 15const expect = chai.expect
27 16
28describe('Test video comments', function () { 17describe('Test video comments', function () {
29 let server: ServerInfo 18 let server: PeerTubeServer
30 let videoId 19 let videoId: number
31 let videoUUID 20 let videoUUID: string
32 let threadId 21 let threadId: number
33 let replyToDeleteId: number 22 let replyToDeleteId: number
34 23
35 let userAccessTokenServer1: string 24 let userAccessTokenServer1: string
36 25
26 let command: CommentsCommand
27
37 before(async function () { 28 before(async function () {
38 this.timeout(30000) 29 this.timeout(30000)
39 30
40 server = await flushAndRunServer(1) 31 server = await createSingleServer(1)
41 32
42 await setAccessTokensToServers([ server ]) 33 await setAccessTokensToServers([ server ])
43 34
44 const res = await uploadVideo(server.url, server.accessToken, {}) 35 const { id, uuid } = await server.videos.upload()
45 videoUUID = res.body.video.uuid 36 videoUUID = uuid
46 videoId = res.body.video.id 37 videoId = id
47 38
48 await updateMyAvatar({ 39 await server.users.updateMyAvatar({ fixture: 'avatar.png' })
49 url: server.url,
50 accessToken: server.accessToken,
51 fixture: 'avatar.png'
52 })
53 40
54 await createUser({ 41 userAccessTokenServer1 = await server.users.generateUserAndToken('user1')
55 url: server.url, 42
56 accessToken: server.accessToken, 43 command = server.comments
57 username: 'user1',
58 password: 'password'
59 })
60 userAccessTokenServer1 = await getAccessToken(server.url, 'user1', 'password')
61 }) 44 })
62 45
63 describe('User comments', function () { 46 describe('User comments', function () {
64 47
65 it('Should not have threads on this video', async function () { 48 it('Should not have threads on this video', async function () {
66 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 49 const body = await command.listThreads({ videoId: videoUUID })
67 50
68 expect(res.body.total).to.equal(0) 51 expect(body.total).to.equal(0)
69 expect(res.body.totalNotDeletedComments).to.equal(0) 52 expect(body.totalNotDeletedComments).to.equal(0)
70 expect(res.body.data).to.be.an('array') 53 expect(body.data).to.be.an('array')
71 expect(res.body.data).to.have.lengthOf(0) 54 expect(body.data).to.have.lengthOf(0)
72 }) 55 })
73 56
74 it('Should create a thread in this video', async function () { 57 it('Should create a thread in this video', async function () {
75 const text = 'my super first comment' 58 const text = 'my super first comment'
76 59
77 const res = await addVideoCommentThread(server.url, server.accessToken, videoUUID, text) 60 const comment = await command.createThread({ videoId: videoUUID, text })
78 const comment = res.body.comment
79 61
80 expect(comment.inReplyToCommentId).to.be.null 62 expect(comment.inReplyToCommentId).to.be.null
81 expect(comment.text).equal('my super first comment') 63 expect(comment.text).equal('my super first comment')
@@ -91,14 +73,14 @@ describe('Test video comments', function () {
91 }) 73 })
92 74
93 it('Should list threads of this video', async function () { 75 it('Should list threads of this video', async function () {
94 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 76 const body = await command.listThreads({ videoId: videoUUID })
95 77
96 expect(res.body.total).to.equal(1) 78 expect(body.total).to.equal(1)
97 expect(res.body.totalNotDeletedComments).to.equal(1) 79 expect(body.totalNotDeletedComments).to.equal(1)
98 expect(res.body.data).to.be.an('array') 80 expect(body.data).to.be.an('array')
99 expect(res.body.data).to.have.lengthOf(1) 81 expect(body.data).to.have.lengthOf(1)
100 82
101 const comment: VideoComment = res.body.data[0] 83 const comment = body.data[0]
102 expect(comment.inReplyToCommentId).to.be.null 84 expect(comment.inReplyToCommentId).to.be.null
103 expect(comment.text).equal('my super first comment') 85 expect(comment.text).equal('my super first comment')
104 expect(comment.videoId).to.equal(videoId) 86 expect(comment.videoId).to.equal(videoId)
@@ -117,9 +99,9 @@ describe('Test video comments', function () {
117 }) 99 })
118 100
119 it('Should get all the thread created', async function () { 101 it('Should get all the thread created', async function () {
120 const res = await getVideoThreadComments(server.url, videoUUID, threadId) 102 const body = await command.getThread({ videoId: videoUUID, threadId })
121 103
122 const rootComment = res.body.comment 104 const rootComment = body.comment
123 expect(rootComment.inReplyToCommentId).to.be.null 105 expect(rootComment.inReplyToCommentId).to.be.null
124 expect(rootComment.text).equal('my super first comment') 106 expect(rootComment.text).equal('my super first comment')
125 expect(rootComment.videoId).to.equal(videoId) 107 expect(rootComment.videoId).to.equal(videoId)
@@ -129,20 +111,19 @@ describe('Test video comments', function () {
129 111
130 it('Should create multiple replies in this thread', async function () { 112 it('Should create multiple replies in this thread', async function () {
131 const text1 = 'my super answer to thread 1' 113 const text1 = 'my super answer to thread 1'
132 const childCommentRes = await addVideoCommentReply(server.url, server.accessToken, videoId, threadId, text1) 114 const created = await command.addReply({ videoId, toCommentId: threadId, text: text1 })
133 const childCommentId = childCommentRes.body.comment.id 115 const childCommentId = created.id
134 116
135 const text2 = 'my super answer to answer of thread 1' 117 const text2 = 'my super answer to answer of thread 1'
136 await addVideoCommentReply(server.url, server.accessToken, videoId, childCommentId, text2) 118 await command.addReply({ videoId, toCommentId: childCommentId, text: text2 })
137 119
138 const text3 = 'my second answer to thread 1' 120 const text3 = 'my second answer to thread 1'
139 await addVideoCommentReply(server.url, server.accessToken, videoId, threadId, text3) 121 await command.addReply({ videoId, toCommentId: threadId, text: text3 })
140 }) 122 })
141 123
142 it('Should get correctly the replies', async function () { 124 it('Should get correctly the replies', async function () {
143 const res = await getVideoThreadComments(server.url, videoUUID, threadId) 125 const tree = await command.getThread({ videoId: videoUUID, threadId })
144 126
145 const tree: VideoCommentThreadTree = res.body
146 expect(tree.comment.text).equal('my super first comment') 127 expect(tree.comment.text).equal('my super first comment')
147 expect(tree.children).to.have.lengthOf(2) 128 expect(tree.children).to.have.lengthOf(2)
148 129
@@ -163,42 +144,41 @@ describe('Test video comments', function () {
163 144
164 it('Should create other threads', async function () { 145 it('Should create other threads', async function () {
165 const text1 = 'super thread 2' 146 const text1 = 'super thread 2'
166 await addVideoCommentThread(server.url, server.accessToken, videoUUID, text1) 147 await command.createThread({ videoId: videoUUID, text: text1 })
167 148
168 const text2 = 'super thread 3' 149 const text2 = 'super thread 3'
169 await addVideoCommentThread(server.url, server.accessToken, videoUUID, text2) 150 await command.createThread({ videoId: videoUUID, text: text2 })
170 }) 151 })
171 152
172 it('Should list the threads', async function () { 153 it('Should list the threads', async function () {
173 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5, 'createdAt') 154 const body = await command.listThreads({ videoId: videoUUID, sort: 'createdAt' })
174 155
175 expect(res.body.total).to.equal(3) 156 expect(body.total).to.equal(3)
176 expect(res.body.totalNotDeletedComments).to.equal(6) 157 expect(body.totalNotDeletedComments).to.equal(6)
177 expect(res.body.data).to.be.an('array') 158 expect(body.data).to.be.an('array')
178 expect(res.body.data).to.have.lengthOf(3) 159 expect(body.data).to.have.lengthOf(3)
179 160
180 expect(res.body.data[0].text).to.equal('my super first comment') 161 expect(body.data[0].text).to.equal('my super first comment')
181 expect(res.body.data[0].totalReplies).to.equal(3) 162 expect(body.data[0].totalReplies).to.equal(3)
182 expect(res.body.data[1].text).to.equal('super thread 2') 163 expect(body.data[1].text).to.equal('super thread 2')
183 expect(res.body.data[1].totalReplies).to.equal(0) 164 expect(body.data[1].totalReplies).to.equal(0)
184 expect(res.body.data[2].text).to.equal('super thread 3') 165 expect(body.data[2].text).to.equal('super thread 3')
185 expect(res.body.data[2].totalReplies).to.equal(0) 166 expect(body.data[2].totalReplies).to.equal(0)
186 }) 167 })
187 168
188 it('Should delete a reply', async function () { 169 it('Should delete a reply', async function () {
189 await deleteVideoComment(server.url, server.accessToken, videoId, replyToDeleteId) 170 await command.delete({ videoId, commentId: replyToDeleteId })
190 171
191 { 172 {
192 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5, 'createdAt') 173 const body = await command.listThreads({ videoId: videoUUID, sort: 'createdAt' })
193 174
194 expect(res.body.total).to.equal(3) 175 expect(body.total).to.equal(3)
195 expect(res.body.totalNotDeletedComments).to.equal(5) 176 expect(body.totalNotDeletedComments).to.equal(5)
196 } 177 }
197 178
198 { 179 {
199 const res = await getVideoThreadComments(server.url, videoUUID, threadId) 180 const tree = await command.getThread({ videoId: videoUUID, threadId })
200 181
201 const tree: VideoCommentThreadTree = res.body
202 expect(tree.comment.text).equal('my super first comment') 182 expect(tree.comment.text).equal('my super first comment')
203 expect(tree.children).to.have.lengthOf(2) 183 expect(tree.children).to.have.lengthOf(2)
204 184
@@ -220,99 +200,88 @@ describe('Test video comments', function () {
220 }) 200 })
221 201
222 it('Should delete a complete thread', async function () { 202 it('Should delete a complete thread', async function () {
223 await deleteVideoComment(server.url, server.accessToken, videoId, threadId) 203 await command.delete({ videoId, commentId: threadId })
224 204
225 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5, 'createdAt') 205 const body = await command.listThreads({ videoId: videoUUID, sort: 'createdAt' })
226 expect(res.body.total).to.equal(3) 206 expect(body.total).to.equal(3)
227 expect(res.body.data).to.be.an('array') 207 expect(body.data).to.be.an('array')
228 expect(res.body.data).to.have.lengthOf(3) 208 expect(body.data).to.have.lengthOf(3)
229 209
230 expect(res.body.data[0].text).to.equal('') 210 expect(body.data[0].text).to.equal('')
231 expect(res.body.data[0].isDeleted).to.be.true 211 expect(body.data[0].isDeleted).to.be.true
232 expect(res.body.data[0].deletedAt).to.not.be.null 212 expect(body.data[0].deletedAt).to.not.be.null
233 expect(res.body.data[0].account).to.be.null 213 expect(body.data[0].account).to.be.null
234 expect(res.body.data[0].totalReplies).to.equal(2) 214 expect(body.data[0].totalReplies).to.equal(2)
235 expect(res.body.data[1].text).to.equal('super thread 2') 215 expect(body.data[1].text).to.equal('super thread 2')
236 expect(res.body.data[1].totalReplies).to.equal(0) 216 expect(body.data[1].totalReplies).to.equal(0)
237 expect(res.body.data[2].text).to.equal('super thread 3') 217 expect(body.data[2].text).to.equal('super thread 3')
238 expect(res.body.data[2].totalReplies).to.equal(0) 218 expect(body.data[2].totalReplies).to.equal(0)
239 }) 219 })
240 220
241 it('Should count replies from the video author correctly', async function () { 221 it('Should count replies from the video author correctly', async function () {
242 const text = 'my super first comment' 222 await command.createThread({ videoId: videoUUID, text: 'my super first comment' })
243 await addVideoCommentThread(server.url, server.accessToken, videoUUID, text) 223
244 let res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 224 const { data } = await command.listThreads({ videoId: videoUUID })
245 const comment: VideoComment = res.body.data[0] 225 const threadId2 = data[0].threadId
246 const threadId2 = comment.threadId
247 226
248 const text2 = 'a first answer to thread 4 by a third party' 227 const text2 = 'a first answer to thread 4 by a third party'
249 await addVideoCommentReply(server.url, userAccessTokenServer1, videoId, threadId2, text2) 228 await command.addReply({ token: userAccessTokenServer1, videoId, toCommentId: threadId2, text: text2 })
250 229
251 const text3 = 'my second answer to thread 4' 230 const text3 = 'my second answer to thread 4'
252 await addVideoCommentReply(server.url, server.accessToken, videoId, threadId2, text3) 231 await command.addReply({ videoId, toCommentId: threadId2, text: text3 })
253 232
254 res = await getVideoThreadComments(server.url, videoUUID, threadId2) 233 const tree = await command.getThread({ videoId: videoUUID, threadId: threadId2 })
255 const tree: VideoCommentThreadTree = res.body
256 expect(tree.comment.totalReplies).to.equal(tree.comment.totalRepliesFromVideoAuthor + 1) 234 expect(tree.comment.totalReplies).to.equal(tree.comment.totalRepliesFromVideoAuthor + 1)
257 }) 235 })
258 }) 236 })
259 237
260 describe('All instance comments', function () { 238 describe('All instance comments', function () {
261 async function getComments (options: any = {}) {
262 const res = await getAdminVideoComments(Object.assign({
263 url: server.url,
264 token: server.accessToken,
265 start: 0,
266 count: 10
267 }, options))
268
269 return { comments: res.body.data as VideoCommentAdmin[], total: res.body.total as number }
270 }
271 239
272 it('Should list instance comments as admin', async function () { 240 it('Should list instance comments as admin', async function () {
273 const { comments } = await getComments({ start: 0, count: 1 }) 241 const { data } = await command.listForAdmin({ start: 0, count: 1 })
274 242
275 expect(comments[0].text).to.equal('my second answer to thread 4') 243 expect(data[0].text).to.equal('my second answer to thread 4')
276 }) 244 })
277 245
278 it('Should filter instance comments by isLocal', async function () { 246 it('Should filter instance comments by isLocal', async function () {
279 const { total, comments } = await getComments({ isLocal: false }) 247 const { total, data } = await command.listForAdmin({ isLocal: false })
280 248
281 expect(comments).to.have.lengthOf(0) 249 expect(data).to.have.lengthOf(0)
282 expect(total).to.equal(0) 250 expect(total).to.equal(0)
283 }) 251 })
284 252
285 it('Should search instance comments by account', async function () { 253 it('Should search instance comments by account', async function () {
286 const { total, comments } = await getComments({ searchAccount: 'user' }) 254 const { total, data } = await command.listForAdmin({ searchAccount: 'user' })
287 255
288 expect(comments).to.have.lengthOf(1) 256 expect(data).to.have.lengthOf(1)
289 expect(total).to.equal(1) 257 expect(total).to.equal(1)
290 258
291 expect(comments[0].text).to.equal('a first answer to thread 4 by a third party') 259 expect(data[0].text).to.equal('a first answer to thread 4 by a third party')
292 }) 260 })
293 261
294 it('Should search instance comments by video', async function () { 262 it('Should search instance comments by video', async function () {
295 { 263 {
296 const { total, comments } = await getComments({ searchVideo: 'video' }) 264 const { total, data } = await command.listForAdmin({ searchVideo: 'video' })
297 265
298 expect(comments).to.have.lengthOf(7) 266 expect(data).to.have.lengthOf(7)
299 expect(total).to.equal(7) 267 expect(total).to.equal(7)
300 } 268 }
301 269
302 { 270 {
303 const { total, comments } = await getComments({ searchVideo: 'hello' }) 271 const { total, data } = await command.listForAdmin({ searchVideo: 'hello' })
304 272
305 expect(comments).to.have.lengthOf(0) 273 expect(data).to.have.lengthOf(0)
306 expect(total).to.equal(0) 274 expect(total).to.equal(0)
307 } 275 }
308 }) 276 })
309 277
310 it('Should search instance comments', async function () { 278 it('Should search instance comments', async function () {
311 const { total, comments } = await getComments({ search: 'super thread 3' }) 279 const { total, data } = await command.listForAdmin({ search: 'super thread 3' })
312 280
313 expect(comments).to.have.lengthOf(1)
314 expect(total).to.equal(1) 281 expect(total).to.equal(1)
315 expect(comments[0].text).to.equal('super thread 3') 282
283 expect(data).to.have.lengthOf(1)
284 expect(data[0].text).to.equal('super thread 3')
316 }) 285 })
317 }) 286 })
318 287
diff --git a/server/tests/api/videos/video-description.ts b/server/tests/api/videos/video-description.ts
index b8e98e45f..d22b4ed96 100644
--- a/server/tests/api/videos/video-description.ts
+++ b/server/tests/api/videos/video-description.ts
@@ -1,25 +1,13 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { 4import * as chai from 'chai'
6 cleanupTests, 5import { cleanupTests, createMultipleServers, doubleFollow, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
7 flushAndRunMultipleServers,
8 getVideo,
9 getVideoDescription,
10 getVideosList,
11 ServerInfo,
12 setAccessTokensToServers,
13 updateVideo,
14 uploadVideo
15} from '../../../../shared/extra-utils/index'
16import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
17import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
18 6
19const expect = chai.expect 7const expect = chai.expect
20 8
21describe('Test video description', function () { 9describe('Test video description', function () {
22 let servers: ServerInfo[] = [] 10 let servers: PeerTubeServer[] = []
23 let videoUUID = '' 11 let videoUUID = ''
24 let videoId: number 12 let videoId: number
25 const longDescription = 'my super description for server 1'.repeat(50) 13 const longDescription = 'my super description for server 1'.repeat(50)
@@ -28,7 +16,7 @@ describe('Test video description', function () {
28 this.timeout(40000) 16 this.timeout(40000)
29 17
30 // Run servers 18 // Run servers
31 servers = await flushAndRunMultipleServers(2) 19 servers = await createMultipleServers(2)
32 20
33 // Get the access tokens 21 // Get the access tokens
34 await setAccessTokensToServers(servers) 22 await setAccessTokensToServers(servers)
@@ -43,20 +31,19 @@ describe('Test video description', function () {
43 const attributes = { 31 const attributes = {
44 description: longDescription 32 description: longDescription
45 } 33 }
46 await uploadVideo(servers[0].url, servers[0].accessToken, attributes) 34 await servers[0].videos.upload({ attributes })
47 35
48 await waitJobs(servers) 36 await waitJobs(servers)
49 37
50 const res = await getVideosList(servers[0].url) 38 const { data } = await servers[0].videos.list()
51 39
52 videoId = res.body.data[0].id 40 videoId = data[0].id
53 videoUUID = res.body.data[0].uuid 41 videoUUID = data[0].uuid
54 }) 42 })
55 43
56 it('Should have a truncated description on each server', async function () { 44 it('Should have a truncated description on each server', async function () {
57 for (const server of servers) { 45 for (const server of servers) {
58 const res = await getVideo(server.url, videoUUID) 46 const video = await server.videos.get({ id: videoUUID })
59 const video = res.body
60 47
61 // 30 characters * 6 -> 240 characters 48 // 30 characters * 6 -> 240 characters
62 const truncatedDescription = 'my super description for server 1'.repeat(7) + 49 const truncatedDescription = 'my super description for server 1'.repeat(7) +
@@ -68,11 +55,10 @@ describe('Test video description', function () {
68 55
69 it('Should fetch long description on each server', async function () { 56 it('Should fetch long description on each server', async function () {
70 for (const server of servers) { 57 for (const server of servers) {
71 const res = await getVideo(server.url, videoUUID) 58 const video = await server.videos.get({ id: videoUUID })
72 const video = res.body
73 59
74 const res2 = await getVideoDescription(server.url, video.descriptionPath) 60 const { description } = await server.videos.getDescription({ descriptionPath: video.descriptionPath })
75 expect(res2.body.description).to.equal(longDescription) 61 expect(description).to.equal(longDescription)
76 } 62 }
77 }) 63 })
78 64
@@ -82,20 +68,19 @@ describe('Test video description', function () {
82 const attributes = { 68 const attributes = {
83 description: 'short description' 69 description: 'short description'
84 } 70 }
85 await updateVideo(servers[0].url, servers[0].accessToken, videoId, attributes) 71 await servers[0].videos.update({ id: videoId, attributes })
86 72
87 await waitJobs(servers) 73 await waitJobs(servers)
88 }) 74 })
89 75
90 it('Should have a small description on each server', async function () { 76 it('Should have a small description on each server', async function () {
91 for (const server of servers) { 77 for (const server of servers) {
92 const res = await getVideo(server.url, videoUUID) 78 const video = await server.videos.get({ id: videoUUID })
93 const video = res.body
94 79
95 expect(video.description).to.equal('short description') 80 expect(video.description).to.equal('short description')
96 81
97 const res2 = await getVideoDescription(server.url, video.descriptionPath) 82 const { description } = await server.videos.getDescription({ descriptionPath: video.descriptionPath })
98 expect(res2.body.description).to.equal('short description') 83 expect(description).to.equal('short description')
99 } 84 }
100 }) 85 })
101 86
diff --git a/server/tests/api/videos/video-hls.ts b/server/tests/api/videos/video-hls.ts
index 03ac3f321..961f0e617 100644
--- a/server/tests/api/videos/video-hls.ts
+++ b/server/tests/api/videos/video-hls.ts
@@ -2,38 +2,30 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { join } from 'path' 5import { basename, join } from 'path'
6import { removeFragmentedMP4Ext, uuidRegex } from '@shared/core-utils'
6import { 7import {
7 checkDirectoryIsEmpty, 8 checkDirectoryIsEmpty,
8 checkResolutionsInMasterPlaylist, 9 checkResolutionsInMasterPlaylist,
9 checkSegmentHash, 10 checkSegmentHash,
10 checkTmpIsEmpty, 11 checkTmpIsEmpty,
11 cleanupTests, 12 cleanupTests,
13 createMultipleServers,
12 doubleFollow, 14 doubleFollow,
13 flushAndRunMultipleServers,
14 getPlaylist,
15 getVideo,
16 makeRawRequest, 15 makeRawRequest,
17 removeVideo, 16 PeerTubeServer,
18 ServerInfo,
19 setAccessTokensToServers, 17 setAccessTokensToServers,
20 updateCustomSubConfig,
21 updateVideo,
22 uploadVideo,
23 waitJobs, 18 waitJobs,
24 webtorrentAdd 19 webtorrentAdd
25} from '../../../../shared/extra-utils' 20} from '@shared/extra-utils'
26import { VideoDetails } from '../../../../shared/models/videos' 21import { HttpStatusCode, VideoStreamingPlaylistType } from '@shared/models'
27import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type'
28import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants' 22import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants'
29import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
30 23
31const expect = chai.expect 24const expect = chai.expect
32 25
33async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOnly: boolean, resolutions = [ 240, 360, 480, 720 ]) { 26async function checkHlsPlaylist (servers: PeerTubeServer[], videoUUID: string, hlsOnly: boolean, resolutions = [ 240, 360, 480, 720 ]) {
34 for (const server of servers) { 27 for (const server of servers) {
35 const resVideoDetails = await getVideo(server.url, videoUUID) 28 const videoDetails = await server.videos.get({ id: videoUUID })
36 const videoDetails: VideoDetails = resVideoDetails.body
37 const baseUrl = `http://${videoDetails.account.host}` 29 const baseUrl = `http://${videoDetails.account.host}`
38 30
39 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1) 31 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
@@ -47,14 +39,17 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOn
47 if (hlsOnly) expect(videoDetails.files).to.have.lengthOf(0) 39 if (hlsOnly) expect(videoDetails.files).to.have.lengthOf(0)
48 else expect(videoDetails.files).to.have.lengthOf(resolutions.length) 40 else expect(videoDetails.files).to.have.lengthOf(resolutions.length)
49 41
42 // Check JSON files
50 for (const resolution of resolutions) { 43 for (const resolution of resolutions) {
51 const file = hlsFiles.find(f => f.resolution.id === resolution) 44 const file = hlsFiles.find(f => f.resolution.id === resolution)
52 expect(file).to.not.be.undefined 45 expect(file).to.not.be.undefined
53 46
54 expect(file.magnetUri).to.have.lengthOf.above(2) 47 expect(file.magnetUri).to.have.lengthOf.above(2)
55 expect(file.torrentUrl).to.equal(`http://${server.host}/lazy-static/torrents/${videoDetails.uuid}-${file.resolution.id}-hls.torrent`) 48 expect(file.torrentUrl).to.match(
56 expect(file.fileUrl).to.equal( 49 new RegExp(`http://${server.host}/lazy-static/torrents/${uuidRegex}-${file.resolution.id}-hls.torrent`)
57 `${baseUrl}/static/streaming-playlists/hls/${videoDetails.uuid}/${videoDetails.uuid}-${file.resolution.id}-fragmented.mp4` 50 )
51 expect(file.fileUrl).to.match(
52 new RegExp(`${baseUrl}/static/streaming-playlists/hls/${videoDetails.uuid}/${uuidRegex}-${file.resolution.id}-fragmented.mp4`)
58 ) 53 )
59 expect(file.resolution.label).to.equal(resolution + 'p') 54 expect(file.resolution.label).to.equal(resolution + 'p')
60 55
@@ -67,11 +62,11 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOn
67 expect(torrent.files[0].path).to.exist.and.to.not.equal('') 62 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
68 } 63 }
69 64
65 // Check master playlist
70 { 66 {
71 await checkResolutionsInMasterPlaylist(hlsPlaylist.playlistUrl, resolutions) 67 await checkResolutionsInMasterPlaylist({ server, playlistUrl: hlsPlaylist.playlistUrl, resolutions })
72 68
73 const res = await getPlaylist(hlsPlaylist.playlistUrl) 69 const masterPlaylist = await server.streamingPlaylists.get({ url: hlsPlaylist.playlistUrl })
74 const masterPlaylist = res.text
75 70
76 for (const resolution of resolutions) { 71 for (const resolution of resolutions) {
77 expect(masterPlaylist).to.contain(`${resolution}.m3u8`) 72 expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
@@ -79,12 +74,18 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOn
79 } 74 }
80 } 75 }
81 76
77 // Check resolution playlists
82 { 78 {
83 for (const resolution of resolutions) { 79 for (const resolution of resolutions) {
84 const res = await getPlaylist(`${baseUrl}/static/streaming-playlists/hls/${videoUUID}/${resolution}.m3u8`) 80 const file = hlsFiles.find(f => f.resolution.id === resolution)
81 const playlistName = removeFragmentedMP4Ext(basename(file.fileUrl)) + '.m3u8'
82
83 const subPlaylist = await server.streamingPlaylists.get({
84 url: `${baseUrl}/static/streaming-playlists/hls/${videoUUID}/${playlistName}`
85 })
85 86
86 const subPlaylist = res.text 87 expect(subPlaylist).to.match(new RegExp(`${uuidRegex}-${resolution}-fragmented.mp4`))
87 expect(subPlaylist).to.contain(`${videoUUID}-${resolution}-fragmented.mp4`) 88 expect(subPlaylist).to.contain(basename(file.fileUrl))
88 } 89 }
89 } 90 }
90 91
@@ -92,23 +93,31 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOn
92 const baseUrlAndPath = baseUrl + '/static/streaming-playlists/hls' 93 const baseUrlAndPath = baseUrl + '/static/streaming-playlists/hls'
93 94
94 for (const resolution of resolutions) { 95 for (const resolution of resolutions) {
95 await checkSegmentHash(baseUrlAndPath, baseUrlAndPath, videoUUID, resolution, hlsPlaylist) 96 await checkSegmentHash({
97 server,
98 baseUrlPlaylist: baseUrlAndPath,
99 baseUrlSegment: baseUrlAndPath,
100 videoUUID,
101 resolution,
102 hlsPlaylist
103 })
96 } 104 }
97 } 105 }
98 } 106 }
99} 107}
100 108
101describe('Test HLS videos', function () { 109describe('Test HLS videos', function () {
102 let servers: ServerInfo[] = [] 110 let servers: PeerTubeServer[] = []
103 let videoUUID = '' 111 let videoUUID = ''
104 let videoAudioUUID = '' 112 let videoAudioUUID = ''
105 113
106 function runTestSuite (hlsOnly: boolean) { 114 function runTestSuite (hlsOnly: boolean) {
115
107 it('Should upload a video and transcode it to HLS', async function () { 116 it('Should upload a video and transcode it to HLS', async function () {
108 this.timeout(120000) 117 this.timeout(120000)
109 118
110 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video 1', fixture: 'video_short.webm' }) 119 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video 1', fixture: 'video_short.webm' } })
111 videoUUID = res.body.video.uuid 120 videoUUID = uuid
112 121
113 await waitJobs(servers) 122 await waitJobs(servers)
114 123
@@ -118,8 +127,8 @@ describe('Test HLS videos', function () {
118 it('Should upload an audio file and transcode it to HLS', async function () { 127 it('Should upload an audio file and transcode it to HLS', async function () {
119 this.timeout(120000) 128 this.timeout(120000)
120 129
121 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video audio', fixture: 'sample.ogg' }) 130 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video audio', fixture: 'sample.ogg' } })
122 videoAudioUUID = res.body.video.uuid 131 videoAudioUUID = uuid
123 132
124 await waitJobs(servers) 133 await waitJobs(servers)
125 134
@@ -129,7 +138,7 @@ describe('Test HLS videos', function () {
129 it('Should update the video', async function () { 138 it('Should update the video', async function () {
130 this.timeout(10000) 139 this.timeout(10000)
131 140
132 await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, { name: 'video 1 updated' }) 141 await servers[0].videos.update({ id: videoUUID, attributes: { name: 'video 1 updated' } })
133 142
134 await waitJobs(servers) 143 await waitJobs(servers)
135 144
@@ -139,14 +148,14 @@ describe('Test HLS videos', function () {
139 it('Should delete videos', async function () { 148 it('Should delete videos', async function () {
140 this.timeout(10000) 149 this.timeout(10000)
141 150
142 await removeVideo(servers[0].url, servers[0].accessToken, videoUUID) 151 await servers[0].videos.remove({ id: videoUUID })
143 await removeVideo(servers[0].url, servers[0].accessToken, videoAudioUUID) 152 await servers[0].videos.remove({ id: videoAudioUUID })
144 153
145 await waitJobs(servers) 154 await waitJobs(servers)
146 155
147 for (const server of servers) { 156 for (const server of servers) {
148 await getVideo(server.url, videoUUID, HttpStatusCode.NOT_FOUND_404) 157 await server.videos.get({ id: videoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
149 await getVideo(server.url, videoAudioUUID, HttpStatusCode.NOT_FOUND_404) 158 await server.videos.get({ id: videoAudioUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
150 } 159 }
151 }) 160 })
152 161
@@ -176,7 +185,7 @@ describe('Test HLS videos', function () {
176 } 185 }
177 } 186 }
178 } 187 }
179 servers = await flushAndRunMultipleServers(2, configOverride) 188 servers = await createMultipleServers(2, configOverride)
180 189
181 // Get the access tokens 190 // Get the access tokens
182 await setAccessTokensToServers(servers) 191 await setAccessTokensToServers(servers)
@@ -192,24 +201,26 @@ describe('Test HLS videos', function () {
192 describe('With only HLS enabled', function () { 201 describe('With only HLS enabled', function () {
193 202
194 before(async function () { 203 before(async function () {
195 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 204 await servers[0].config.updateCustomSubConfig({
196 transcoding: { 205 newConfig: {
197 enabled: true, 206 transcoding: {
198 allowAudioFiles: true, 207 enabled: true,
199 resolutions: { 208 allowAudioFiles: true,
200 '240p': true, 209 resolutions: {
201 '360p': true, 210 '240p': true,
202 '480p': true, 211 '360p': true,
203 '720p': true, 212 '480p': true,
204 '1080p': true, 213 '720p': true,
205 '1440p': true, 214 '1080p': true,
206 '2160p': true 215 '1440p': true,
207 }, 216 '2160p': true
208 hls: { 217 },
209 enabled: true 218 hls: {
210 }, 219 enabled: true
211 webtorrent: { 220 },
212 enabled: false 221 webtorrent: {
222 enabled: false
223 }
213 } 224 }
214 } 225 }
215 }) 226 })
diff --git a/server/tests/api/videos/video-imports.ts b/server/tests/api/videos/video-imports.ts
index 80834ca86..2eac130d2 100644
--- a/server/tests/api/videos/video-imports.ts
+++ b/server/tests/api/videos/video-imports.ts
@@ -3,43 +3,30 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 areHttpImportTestsDisabled,
6 cleanupTests, 7 cleanupTests,
8 createMultipleServers,
7 doubleFollow, 9 doubleFollow,
8 flushAndRunMultipleServers, 10 FIXTURE_URLS,
9 getMyUserInformation, 11 PeerTubeServer,
10 getMyVideos,
11 getVideo,
12 getVideosList,
13 immutableAssign,
14 listVideoCaptions,
15 ServerInfo,
16 setAccessTokensToServers, 12 setAccessTokensToServers,
17 testCaptionFile, 13 testCaptionFile,
18 updateCustomSubConfig 14 testImage,
19} from '../../../../shared/extra-utils' 15 waitJobs
20import { areHttpImportTestsDisabled, testImage } from '../../../../shared/extra-utils/miscs/miscs' 16} from '@shared/extra-utils'
21import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 17import { VideoPrivacy, VideoResolution } from '@shared/models'
22import {
23 getMagnetURI,
24 getMyVideoImports,
25 getYoutubeHDRVideoUrl,
26 getYoutubeVideoUrl,
27 importVideo
28} from '../../../../shared/extra-utils/videos/video-imports'
29import { VideoCaption, VideoDetails, VideoImport, VideoPrivacy, VideoResolution } from '../../../../shared/models/videos'
30 18
31const expect = chai.expect 19const expect = chai.expect
32 20
33describe('Test video imports', function () { 21describe('Test video imports', function () {
34 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
35 let channelIdServer1: number 23 let channelIdServer1: number
36 let channelIdServer2: number 24 let channelIdServer2: number
37 25
38 if (areHttpImportTestsDisabled()) return 26 if (areHttpImportTestsDisabled()) return
39 27
40 async function checkVideosServer1 (url: string, idHttp: string, idMagnet: string, idTorrent: string) { 28 async function checkVideosServer1 (server: PeerTubeServer, idHttp: string, idMagnet: string, idTorrent: string) {
41 const resHttp = await getVideo(url, idHttp) 29 const videoHttp = await server.videos.get({ id: idHttp })
42 const videoHttp: VideoDetails = resHttp.body
43 30
44 expect(videoHttp.name).to.equal('small video - youtube') 31 expect(videoHttp.name).to.equal('small video - youtube')
45 // FIXME: youtube-dl seems broken 32 // FIXME: youtube-dl seems broken
@@ -56,10 +43,8 @@ describe('Test video imports', function () {
56 expect(originallyPublishedAt.getMonth()).to.equal(0) 43 expect(originallyPublishedAt.getMonth()).to.equal(0)
57 expect(originallyPublishedAt.getFullYear()).to.equal(2019) 44 expect(originallyPublishedAt.getFullYear()).to.equal(2019)
58 45
59 const resMagnet = await getVideo(url, idMagnet) 46 const videoMagnet = await server.videos.get({ id: idMagnet })
60 const videoMagnet: VideoDetails = resMagnet.body 47 const videoTorrent = await server.videos.get({ id: idTorrent })
61 const resTorrent = await getVideo(url, idTorrent)
62 const videoTorrent: VideoDetails = resTorrent.body
63 48
64 for (const video of [ videoMagnet, videoTorrent ]) { 49 for (const video of [ videoMagnet, videoTorrent ]) {
65 expect(video.category.label).to.equal('Misc') 50 expect(video.category.label).to.equal('Misc')
@@ -74,13 +59,12 @@ describe('Test video imports', function () {
74 expect(videoTorrent.name).to.contain('你好 世界 720p.mp4') 59 expect(videoTorrent.name).to.contain('你好 世界 720p.mp4')
75 expect(videoMagnet.name).to.contain('super peertube2 video') 60 expect(videoMagnet.name).to.contain('super peertube2 video')
76 61
77 const resCaptions = await listVideoCaptions(url, idHttp) 62 const bodyCaptions = await server.captions.list({ videoId: idHttp })
78 expect(resCaptions.body.total).to.equal(2) 63 expect(bodyCaptions.total).to.equal(2)
79 } 64 }
80 65
81 async function checkVideoServer2 (url: string, id: number | string) { 66 async function checkVideoServer2 (server: PeerTubeServer, id: number | string) {
82 const res = await getVideo(url, id) 67 const video = await server.videos.get({ id })
83 const video: VideoDetails = res.body
84 68
85 expect(video.name).to.equal('my super name') 69 expect(video.name).to.equal('my super name')
86 expect(video.category.label).to.equal('Entertainment') 70 expect(video.category.label).to.equal('Entertainment')
@@ -92,26 +76,26 @@ describe('Test video imports', function () {
92 76
93 expect(video.files).to.have.lengthOf(1) 77 expect(video.files).to.have.lengthOf(1)
94 78
95 const resCaptions = await listVideoCaptions(url, id) 79 const bodyCaptions = await server.captions.list({ videoId: id })
96 expect(resCaptions.body.total).to.equal(2) 80 expect(bodyCaptions.total).to.equal(2)
97 } 81 }
98 82
99 before(async function () { 83 before(async function () {
100 this.timeout(30_000) 84 this.timeout(30_000)
101 85
102 // Run servers 86 // Run servers
103 servers = await flushAndRunMultipleServers(2) 87 servers = await createMultipleServers(2)
104 88
105 await setAccessTokensToServers(servers) 89 await setAccessTokensToServers(servers)
106 90
107 { 91 {
108 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) 92 const { videoChannels } = await servers[0].users.getMyInfo()
109 channelIdServer1 = res.body.videoChannels[0].id 93 channelIdServer1 = videoChannels[0].id
110 } 94 }
111 95
112 { 96 {
113 const res = await getMyUserInformation(servers[1].url, servers[1].accessToken) 97 const { videoChannels } = await servers[1].users.getMyInfo()
114 channelIdServer2 = res.body.videoChannels[0].id 98 channelIdServer2 = videoChannels[0].id
115 } 99 }
116 100
117 await doubleFollow(servers[0], servers[1]) 101 await doubleFollow(servers[0], servers[1])
@@ -126,18 +110,18 @@ describe('Test video imports', function () {
126 } 110 }
127 111
128 { 112 {
129 const attributes = immutableAssign(baseAttributes, { targetUrl: getYoutubeVideoUrl() }) 113 const attributes = { ...baseAttributes, targetUrl: FIXTURE_URLS.youtube }
130 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 114 const { video } = await servers[0].imports.importVideo({ attributes })
131 expect(res.body.video.name).to.equal('small video - youtube') 115 expect(video.name).to.equal('small video - youtube')
132 116
133 expect(res.body.video.thumbnailPath).to.match(new RegExp(`^/static/thumbnails/.+.jpg$`)) 117 expect(video.thumbnailPath).to.match(new RegExp(`^/static/thumbnails/.+.jpg$`))
134 expect(res.body.video.previewPath).to.match(new RegExp(`^/lazy-static/previews/.+.jpg$`)) 118 expect(video.previewPath).to.match(new RegExp(`^/lazy-static/previews/.+.jpg$`))
135 119
136 await testImage(servers[0].url, 'video_import_thumbnail', res.body.video.thumbnailPath) 120 await testImage(servers[0].url, 'video_import_thumbnail', video.thumbnailPath)
137 await testImage(servers[0].url, 'video_import_preview', res.body.video.previewPath) 121 await testImage(servers[0].url, 'video_import_preview', video.previewPath)
138 122
139 const resCaptions = await listVideoCaptions(servers[0].url, res.body.video.id) 123 const bodyCaptions = await servers[0].captions.list({ videoId: video.id })
140 const videoCaptions: VideoCaption[] = resCaptions.body.data 124 const videoCaptions = bodyCaptions.data
141 expect(videoCaptions).to.have.lengthOf(2) 125 expect(videoCaptions).to.have.lengthOf(2)
142 126
143 const enCaption = videoCaptions.find(caption => caption.language.id === 'en') 127 const enCaption = videoCaptions.find(caption => caption.language.id === 'en')
@@ -176,52 +160,52 @@ Ajouter un sous-titre est vraiment facile`)
176 } 160 }
177 161
178 { 162 {
179 const attributes = immutableAssign(baseAttributes, { 163 const attributes = {
180 magnetUri: getMagnetURI(), 164 ...baseAttributes,
165 magnetUri: FIXTURE_URLS.magnet,
181 description: 'this is a super torrent description', 166 description: 'this is a super torrent description',
182 tags: [ 'tag_torrent1', 'tag_torrent2' ] 167 tags: [ 'tag_torrent1', 'tag_torrent2' ]
183 }) 168 }
184 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 169 const { video } = await servers[0].imports.importVideo({ attributes })
185 expect(res.body.video.name).to.equal('super peertube2 video') 170 expect(video.name).to.equal('super peertube2 video')
186 } 171 }
187 172
188 { 173 {
189 const attributes = immutableAssign(baseAttributes, { 174 const attributes = {
175 ...baseAttributes,
190 torrentfile: 'video-720p.torrent' as any, 176 torrentfile: 'video-720p.torrent' as any,
191 description: 'this is a super torrent description', 177 description: 'this is a super torrent description',
192 tags: [ 'tag_torrent1', 'tag_torrent2' ] 178 tags: [ 'tag_torrent1', 'tag_torrent2' ]
193 }) 179 }
194 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 180 const { video } = await servers[0].imports.importVideo({ attributes })
195 expect(res.body.video.name).to.equal('你好 世界 720p.mp4') 181 expect(video.name).to.equal('你好 世界 720p.mp4')
196 } 182 }
197 }) 183 })
198 184
199 it('Should list the videos to import in my videos on server 1', async function () { 185 it('Should list the videos to import in my videos on server 1', async function () {
200 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 5, 'createdAt') 186 const { total, data } = await servers[0].videos.listMyVideos({ sort: 'createdAt' })
201 187
202 expect(res.body.total).to.equal(3) 188 expect(total).to.equal(3)
203 189
204 const videos = res.body.data 190 expect(data).to.have.lengthOf(3)
205 expect(videos).to.have.lengthOf(3) 191 expect(data[0].name).to.equal('small video - youtube')
206 expect(videos[0].name).to.equal('small video - youtube') 192 expect(data[1].name).to.equal('super peertube2 video')
207 expect(videos[1].name).to.equal('super peertube2 video') 193 expect(data[2].name).to.equal('你好 世界 720p.mp4')
208 expect(videos[2].name).to.equal('你好 世界 720p.mp4')
209 }) 194 })
210 195
211 it('Should list the videos to import in my imports on server 1', async function () { 196 it('Should list the videos to import in my imports on server 1', async function () {
212 const res = await getMyVideoImports(servers[0].url, servers[0].accessToken, '-createdAt') 197 const { total, data: videoImports } = await servers[0].imports.getMyVideoImports({ sort: '-createdAt' })
198 expect(total).to.equal(3)
213 199
214 expect(res.body.total).to.equal(3)
215 const videoImports: VideoImport[] = res.body.data
216 expect(videoImports).to.have.lengthOf(3) 200 expect(videoImports).to.have.lengthOf(3)
217 201
218 expect(videoImports[2].targetUrl).to.equal(getYoutubeVideoUrl()) 202 expect(videoImports[2].targetUrl).to.equal(FIXTURE_URLS.youtube)
219 expect(videoImports[2].magnetUri).to.be.null 203 expect(videoImports[2].magnetUri).to.be.null
220 expect(videoImports[2].torrentName).to.be.null 204 expect(videoImports[2].torrentName).to.be.null
221 expect(videoImports[2].video.name).to.equal('small video - youtube') 205 expect(videoImports[2].video.name).to.equal('small video - youtube')
222 206
223 expect(videoImports[1].targetUrl).to.be.null 207 expect(videoImports[1].targetUrl).to.be.null
224 expect(videoImports[1].magnetUri).to.equal(getMagnetURI()) 208 expect(videoImports[1].magnetUri).to.equal(FIXTURE_URLS.magnet)
225 expect(videoImports[1].torrentName).to.be.null 209 expect(videoImports[1].torrentName).to.be.null
226 expect(videoImports[1].video.name).to.equal('super peertube2 video') 210 expect(videoImports[1].video.name).to.equal('super peertube2 video')
227 211
@@ -237,12 +221,12 @@ Ajouter un sous-titre est vraiment facile`)
237 await waitJobs(servers) 221 await waitJobs(servers)
238 222
239 for (const server of servers) { 223 for (const server of servers) {
240 const res = await getVideosList(server.url) 224 const { total, data } = await server.videos.list()
241 expect(res.body.total).to.equal(3) 225 expect(total).to.equal(3)
242 expect(res.body.data).to.have.lengthOf(3) 226 expect(data).to.have.lengthOf(3)
243 227
244 const [ videoHttp, videoMagnet, videoTorrent ] = res.body.data 228 const [ videoHttp, videoMagnet, videoTorrent ] = data
245 await checkVideosServer1(server.url, videoHttp.uuid, videoMagnet.uuid, videoTorrent.uuid) 229 await checkVideosServer1(server, videoHttp.uuid, videoMagnet.uuid, videoTorrent.uuid)
246 } 230 }
247 }) 231 })
248 232
@@ -250,7 +234,7 @@ Ajouter un sous-titre est vraiment facile`)
250 this.timeout(60_000) 234 this.timeout(60_000)
251 235
252 const attributes = { 236 const attributes = {
253 targetUrl: getYoutubeVideoUrl(), 237 targetUrl: FIXTURE_URLS.youtube,
254 channelId: channelIdServer2, 238 channelId: channelIdServer2,
255 privacy: VideoPrivacy.PUBLIC, 239 privacy: VideoPrivacy.PUBLIC,
256 category: 10, 240 category: 10,
@@ -260,8 +244,8 @@ Ajouter un sous-titre est vraiment facile`)
260 description: 'my super description', 244 description: 'my super description',
261 tags: [ 'supertag1', 'supertag2' ] 245 tags: [ 'supertag1', 'supertag2' ]
262 } 246 }
263 const res = await importVideo(servers[1].url, servers[1].accessToken, attributes) 247 const { video } = await servers[1].imports.importVideo({ attributes })
264 expect(res.body.video.name).to.equal('my super name') 248 expect(video.name).to.equal('my super name')
265 }) 249 })
266 250
267 it('Should have the videos listed on the two instances', async function () { 251 it('Should have the videos listed on the two instances', async function () {
@@ -270,14 +254,14 @@ Ajouter un sous-titre est vraiment facile`)
270 await waitJobs(servers) 254 await waitJobs(servers)
271 255
272 for (const server of servers) { 256 for (const server of servers) {
273 const res = await getVideosList(server.url) 257 const { total, data } = await server.videos.list()
274 expect(res.body.total).to.equal(4) 258 expect(total).to.equal(4)
275 expect(res.body.data).to.have.lengthOf(4) 259 expect(data).to.have.lengthOf(4)
276 260
277 await checkVideoServer2(server.url, res.body.data[0].uuid) 261 await checkVideoServer2(server, data[0].uuid)
278 262
279 const [ , videoHttp, videoMagnet, videoTorrent ] = res.body.data 263 const [ , videoHttp, videoMagnet, videoTorrent ] = data
280 await checkVideosServer1(server.url, videoHttp.uuid, videoMagnet.uuid, videoTorrent.uuid) 264 await checkVideosServer1(server, videoHttp.uuid, videoMagnet.uuid, videoTorrent.uuid)
281 } 265 }
282 }) 266 })
283 267
@@ -286,18 +270,17 @@ Ajouter un sous-titre est vraiment facile`)
286 270
287 const attributes = { 271 const attributes = {
288 name: 'transcoded video', 272 name: 'transcoded video',
289 magnetUri: getMagnetURI(), 273 magnetUri: FIXTURE_URLS.magnet,
290 channelId: channelIdServer2, 274 channelId: channelIdServer2,
291 privacy: VideoPrivacy.PUBLIC 275 privacy: VideoPrivacy.PUBLIC
292 } 276 }
293 const res = await importVideo(servers[1].url, servers[1].accessToken, attributes) 277 const { video } = await servers[1].imports.importVideo({ attributes })
294 const videoUUID = res.body.video.uuid 278 const videoUUID = video.uuid
295 279
296 await waitJobs(servers) 280 await waitJobs(servers)
297 281
298 for (const server of servers) { 282 for (const server of servers) {
299 const res = await getVideo(server.url, videoUUID) 283 const video = await server.videos.get({ id: videoUUID })
300 const video: VideoDetails = res.body
301 284
302 expect(video.name).to.equal('transcoded video') 285 expect(video.name).to.equal('transcoded video')
303 expect(video.files).to.have.lengthOf(4) 286 expect(video.files).to.have.lengthOf(4)
@@ -333,22 +316,21 @@ Ajouter un sous-titre est vraiment facile`)
333 } 316 }
334 } 317 }
335 } 318 }
336 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 319 await servers[0].config.updateCustomSubConfig({ newConfig: config })
337 320
338 const attributes = { 321 const attributes = {
339 name: 'hdr video', 322 name: 'hdr video',
340 targetUrl: getYoutubeHDRVideoUrl(), 323 targetUrl: FIXTURE_URLS.youtubeHDR,
341 channelId: channelIdServer1, 324 channelId: channelIdServer1,
342 privacy: VideoPrivacy.PUBLIC 325 privacy: VideoPrivacy.PUBLIC
343 } 326 }
344 const res1 = await importVideo(servers[0].url, servers[0].accessToken, attributes) 327 const { video: videoImported } = await servers[0].imports.importVideo({ attributes })
345 const videoUUID = res1.body.video.uuid 328 const videoUUID = videoImported.uuid
346 329
347 await waitJobs(servers) 330 await waitJobs(servers)
348 331
349 // test resolution 332 // test resolution
350 const res2 = await getVideo(servers[0].url, videoUUID) 333 const video = await servers[0].videos.get({ id: videoUUID })
351 const video: VideoDetails = res2.body
352 expect(video.name).to.equal('hdr video') 334 expect(video.name).to.equal('hdr video')
353 const maxResolution = Math.max.apply(Math, video.files.map(function (o) { return o.resolution.id })) 335 const maxResolution = Math.max.apply(Math, video.files.map(function (o) { return o.resolution.id }))
354 expect(maxResolution, 'expected max resolution not met').to.equals(VideoResolution.H_1080P) 336 expect(maxResolution, 'expected max resolution not met').to.equals(VideoResolution.H_1080P)
diff --git a/server/tests/api/videos/video-nsfw.ts b/server/tests/api/videos/video-nsfw.ts
index b16b484b9..b5d183d62 100644
--- a/server/tests/api/videos/video-nsfw.ts
+++ b/server/tests/api/videos/video-nsfw.ts
@@ -1,117 +1,96 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { cleanupTests, getVideosList, ServerInfo, setAccessTokensToServers, uploadVideo } from '../../../../shared/extra-utils/index' 4import * as chai from 'chai'
6import { userLogin } from '../../../../shared/extra-utils/users/login' 5import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
7import { createUser } from '../../../../shared/extra-utils/users/users' 6import { BooleanBothQuery, CustomConfig, ResultList, Video, VideosOverview } from '@shared/models'
8import { getMyVideos } from '../../../../shared/extra-utils/videos/videos'
9import {
10 flushAndRunServer,
11 getAccountVideos,
12 getConfig,
13 getCustomConfig,
14 getMyUserInformation,
15 getVideoChannelVideos,
16 getVideosListWithToken,
17 searchVideo,
18 searchVideoWithToken,
19 updateCustomConfig,
20 updateMyUser
21} from '../../../../shared/extra-utils'
22import { ServerConfig, VideosOverview } from '../../../../shared/models'
23import { CustomConfig } from '../../../../shared/models/server/custom-config.model'
24import { User } from '../../../../shared/models/users'
25import { getVideosOverview, getVideosOverviewWithToken } from '@shared/extra-utils/overviews/overviews'
26 7
27const expect = chai.expect 8const expect = chai.expect
28 9
29function createOverviewRes (res: any) { 10function createOverviewRes (overview: VideosOverview) {
30 const overview = res.body as VideosOverview
31
32 const videos = overview.categories[0].videos 11 const videos = overview.categories[0].videos
33 return { body: { data: videos, total: videos.length } } 12 return { data: videos, total: videos.length }
34} 13}
35 14
36describe('Test video NSFW policy', function () { 15describe('Test video NSFW policy', function () {
37 let server: ServerInfo 16 let server: PeerTubeServer
38 let userAccessToken: string 17 let userAccessToken: string
39 let customConfig: CustomConfig 18 let customConfig: CustomConfig
40 19
41 function getVideosFunctions (token?: string, query = {}) { 20 async function getVideosFunctions (token?: string, query: { nsfw?: BooleanBothQuery } = {}) {
42 return getMyUserInformation(server.url, server.accessToken) 21 const user = await server.users.getMyInfo()
43 .then(res => { 22
44 const user: User = res.body 23 const channelName = user.videoChannels[0].name
45 const videoChannelName = user.videoChannels[0].name 24 const accountName = user.account.name + '@' + user.account.host
46 const accountName = user.account.name + '@' + user.account.host 25
47 const hasQuery = Object.keys(query).length !== 0 26 const hasQuery = Object.keys(query).length !== 0
48 let promises: Promise<any>[] 27 let promises: Promise<ResultList<Video>>[]
49 28
50 if (token) { 29 if (token) {
51 promises = [ 30 promises = [
52 getVideosListWithToken(server.url, token, query), 31 server.search.advancedVideoSearch({ token, search: { search: 'n', sort: '-publishedAt', ...query } }),
53 searchVideoWithToken(server.url, 'n', token, query), 32 server.videos.listWithToken({ token, ...query }),
54 getAccountVideos(server.url, token, accountName, 0, 5, undefined, query), 33 server.videos.listByAccount({ token, handle: accountName, ...query }),
55 getVideoChannelVideos(server.url, token, videoChannelName, 0, 5, undefined, query) 34 server.videos.listByChannel({ token, handle: channelName, ...query })
56 ] 35 ]
57 36
58 // Overviews do not support video filters 37 // Overviews do not support video filters
59 if (!hasQuery) { 38 if (!hasQuery) {
60 promises.push(getVideosOverviewWithToken(server.url, 1, token).then(res => createOverviewRes(res))) 39 const p = server.overviews.getVideos({ page: 1, token })
61 } 40 .then(res => createOverviewRes(res))
62 41 promises.push(p)
63 return Promise.all(promises) 42 }
64 } 43
65 44 return Promise.all(promises)
66 promises = [ 45 }
67 getVideosList(server.url), 46
68 searchVideo(server.url, 'n'), 47 promises = [
69 getAccountVideos(server.url, undefined, accountName, 0, 5), 48 server.search.searchVideos({ search: 'n', sort: '-publishedAt' }),
70 getVideoChannelVideos(server.url, undefined, videoChannelName, 0, 5) 49 server.videos.list(),
71 ] 50 server.videos.listByAccount({ token: null, handle: accountName }),
72 51 server.videos.listByChannel({ token: null, handle: channelName })
73 // Overviews do not support video filters 52 ]
74 if (!hasQuery) { 53
75 promises.push(getVideosOverview(server.url, 1).then(res => createOverviewRes(res))) 54 // Overviews do not support video filters
76 } 55 if (!hasQuery) {
77 56 const p = server.overviews.getVideos({ page: 1 })
78 return Promise.all(promises) 57 .then(res => createOverviewRes(res))
79 }) 58 promises.push(p)
59 }
60
61 return Promise.all(promises)
80 } 62 }
81 63
82 before(async function () { 64 before(async function () {
83 this.timeout(50000) 65 this.timeout(50000)
84 server = await flushAndRunServer(1) 66 server = await createSingleServer(1)
85 67
86 // Get the access tokens 68 // Get the access tokens
87 await setAccessTokensToServers([ server ]) 69 await setAccessTokensToServers([ server ])
88 70
89 { 71 {
90 const attributes = { name: 'nsfw', nsfw: true, category: 1 } 72 const attributes = { name: 'nsfw', nsfw: true, category: 1 }
91 await uploadVideo(server.url, server.accessToken, attributes) 73 await server.videos.upload({ attributes })
92 } 74 }
93 75
94 { 76 {
95 const attributes = { name: 'normal', nsfw: false, category: 1 } 77 const attributes = { name: 'normal', nsfw: false, category: 1 }
96 await uploadVideo(server.url, server.accessToken, attributes) 78 await server.videos.upload({ attributes })
97 } 79 }
98 80
99 { 81 customConfig = await server.config.getCustomConfig()
100 const res = await getCustomConfig(server.url, server.accessToken)
101 customConfig = res.body
102 }
103 }) 82 })
104 83
105 describe('Instance default NSFW policy', function () { 84 describe('Instance default NSFW policy', function () {
85
106 it('Should display NSFW videos with display default NSFW policy', async function () { 86 it('Should display NSFW videos with display default NSFW policy', async function () {
107 const resConfig = await getConfig(server.url) 87 const serverConfig = await server.config.getConfig()
108 const serverConfig: ServerConfig = resConfig.body
109 expect(serverConfig.instance.defaultNSFWPolicy).to.equal('display') 88 expect(serverConfig.instance.defaultNSFWPolicy).to.equal('display')
110 89
111 for (const res of await getVideosFunctions()) { 90 for (const body of await getVideosFunctions()) {
112 expect(res.body.total).to.equal(2) 91 expect(body.total).to.equal(2)
113 92
114 const videos = res.body.data 93 const videos = body.data
115 expect(videos).to.have.lengthOf(2) 94 expect(videos).to.have.lengthOf(2)
116 expect(videos[0].name).to.equal('normal') 95 expect(videos[0].name).to.equal('normal')
117 expect(videos[1].name).to.equal('nsfw') 96 expect(videos[1].name).to.equal('nsfw')
@@ -120,16 +99,15 @@ describe('Test video NSFW policy', function () {
120 99
121 it('Should not display NSFW videos with do_not_list default NSFW policy', async function () { 100 it('Should not display NSFW videos with do_not_list default NSFW policy', async function () {
122 customConfig.instance.defaultNSFWPolicy = 'do_not_list' 101 customConfig.instance.defaultNSFWPolicy = 'do_not_list'
123 await updateCustomConfig(server.url, server.accessToken, customConfig) 102 await server.config.updateCustomConfig({ newCustomConfig: customConfig })
124 103
125 const resConfig = await getConfig(server.url) 104 const serverConfig = await server.config.getConfig()
126 const serverConfig: ServerConfig = resConfig.body
127 expect(serverConfig.instance.defaultNSFWPolicy).to.equal('do_not_list') 105 expect(serverConfig.instance.defaultNSFWPolicy).to.equal('do_not_list')
128 106
129 for (const res of await getVideosFunctions()) { 107 for (const body of await getVideosFunctions()) {
130 expect(res.body.total).to.equal(1) 108 expect(body.total).to.equal(1)
131 109
132 const videos = res.body.data 110 const videos = body.data
133 expect(videos).to.have.lengthOf(1) 111 expect(videos).to.have.lengthOf(1)
134 expect(videos[0].name).to.equal('normal') 112 expect(videos[0].name).to.equal('normal')
135 } 113 }
@@ -137,16 +115,15 @@ describe('Test video NSFW policy', function () {
137 115
138 it('Should display NSFW videos with blur default NSFW policy', async function () { 116 it('Should display NSFW videos with blur default NSFW policy', async function () {
139 customConfig.instance.defaultNSFWPolicy = 'blur' 117 customConfig.instance.defaultNSFWPolicy = 'blur'
140 await updateCustomConfig(server.url, server.accessToken, customConfig) 118 await server.config.updateCustomConfig({ newCustomConfig: customConfig })
141 119
142 const resConfig = await getConfig(server.url) 120 const serverConfig = await server.config.getConfig()
143 const serverConfig: ServerConfig = resConfig.body
144 expect(serverConfig.instance.defaultNSFWPolicy).to.equal('blur') 121 expect(serverConfig.instance.defaultNSFWPolicy).to.equal('blur')
145 122
146 for (const res of await getVideosFunctions()) { 123 for (const body of await getVideosFunctions()) {
147 expect(res.body.total).to.equal(2) 124 expect(body.total).to.equal(2)
148 125
149 const videos = res.body.data 126 const videos = body.data
150 expect(videos).to.have.lengthOf(2) 127 expect(videos).to.have.lengthOf(2)
151 expect(videos[0].name).to.equal('normal') 128 expect(videos[0].name).to.equal('normal')
152 expect(videos[1].name).to.equal('nsfw') 129 expect(videos[1].name).to.equal('nsfw')
@@ -159,24 +136,22 @@ describe('Test video NSFW policy', function () {
159 it('Should create a user having the default nsfw policy', async function () { 136 it('Should create a user having the default nsfw policy', async function () {
160 const username = 'user1' 137 const username = 'user1'
161 const password = 'my super password' 138 const password = 'my super password'
162 await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) 139 await server.users.create({ username: username, password: password })
163
164 userAccessToken = await userLogin(server, { username, password })
165 140
166 const res = await getMyUserInformation(server.url, userAccessToken) 141 userAccessToken = await server.login.getAccessToken({ username, password })
167 const user = res.body
168 142
143 const user = await server.users.getMyInfo({ token: userAccessToken })
169 expect(user.nsfwPolicy).to.equal('blur') 144 expect(user.nsfwPolicy).to.equal('blur')
170 }) 145 })
171 146
172 it('Should display NSFW videos with blur user NSFW policy', async function () { 147 it('Should display NSFW videos with blur user NSFW policy', async function () {
173 customConfig.instance.defaultNSFWPolicy = 'do_not_list' 148 customConfig.instance.defaultNSFWPolicy = 'do_not_list'
174 await updateCustomConfig(server.url, server.accessToken, customConfig) 149 await server.config.updateCustomConfig({ newCustomConfig: customConfig })
175 150
176 for (const res of await getVideosFunctions(userAccessToken)) { 151 for (const body of await getVideosFunctions(userAccessToken)) {
177 expect(res.body.total).to.equal(2) 152 expect(body.total).to.equal(2)
178 153
179 const videos = res.body.data 154 const videos = body.data
180 expect(videos).to.have.lengthOf(2) 155 expect(videos).to.have.lengthOf(2)
181 expect(videos[0].name).to.equal('normal') 156 expect(videos[0].name).to.equal('normal')
182 expect(videos[1].name).to.equal('nsfw') 157 expect(videos[1].name).to.equal('nsfw')
@@ -184,16 +159,12 @@ describe('Test video NSFW policy', function () {
184 }) 159 })
185 160
186 it('Should display NSFW videos with display user NSFW policy', async function () { 161 it('Should display NSFW videos with display user NSFW policy', async function () {
187 await updateMyUser({ 162 await server.users.updateMe({ nsfwPolicy: 'display' })
188 url: server.url,
189 accessToken: server.accessToken,
190 nsfwPolicy: 'display'
191 })
192 163
193 for (const res of await getVideosFunctions(server.accessToken)) { 164 for (const body of await getVideosFunctions(server.accessToken)) {
194 expect(res.body.total).to.equal(2) 165 expect(body.total).to.equal(2)
195 166
196 const videos = res.body.data 167 const videos = body.data
197 expect(videos).to.have.lengthOf(2) 168 expect(videos).to.have.lengthOf(2)
198 expect(videos[0].name).to.equal('normal') 169 expect(videos[0].name).to.equal('normal')
199 expect(videos[1].name).to.equal('nsfw') 170 expect(videos[1].name).to.equal('nsfw')
@@ -201,56 +172,51 @@ describe('Test video NSFW policy', function () {
201 }) 172 })
202 173
203 it('Should not display NSFW videos with do_not_list user NSFW policy', async function () { 174 it('Should not display NSFW videos with do_not_list user NSFW policy', async function () {
204 await updateMyUser({ 175 await server.users.updateMe({ nsfwPolicy: 'do_not_list' })
205 url: server.url,
206 accessToken: server.accessToken,
207 nsfwPolicy: 'do_not_list'
208 })
209 176
210 for (const res of await getVideosFunctions(server.accessToken)) { 177 for (const body of await getVideosFunctions(server.accessToken)) {
211 expect(res.body.total).to.equal(1) 178 expect(body.total).to.equal(1)
212 179
213 const videos = res.body.data 180 const videos = body.data
214 expect(videos).to.have.lengthOf(1) 181 expect(videos).to.have.lengthOf(1)
215 expect(videos[0].name).to.equal('normal') 182 expect(videos[0].name).to.equal('normal')
216 } 183 }
217 }) 184 })
218 185
219 it('Should be able to see my NSFW videos even with do_not_list user NSFW policy', async function () { 186 it('Should be able to see my NSFW videos even with do_not_list user NSFW policy', async function () {
220 const res = await getMyVideos(server.url, server.accessToken, 0, 5) 187 const { total, data } = await server.videos.listMyVideos()
221 expect(res.body.total).to.equal(2) 188 expect(total).to.equal(2)
222 189
223 const videos = res.body.data 190 expect(data).to.have.lengthOf(2)
224 expect(videos).to.have.lengthOf(2) 191 expect(data[0].name).to.equal('normal')
225 expect(videos[0].name).to.equal('normal') 192 expect(data[1].name).to.equal('nsfw')
226 expect(videos[1].name).to.equal('nsfw')
227 }) 193 })
228 194
229 it('Should display NSFW videos when the nsfw param === true', async function () { 195 it('Should display NSFW videos when the nsfw param === true', async function () {
230 for (const res of await getVideosFunctions(server.accessToken, { nsfw: true })) { 196 for (const body of await getVideosFunctions(server.accessToken, { nsfw: 'true' })) {
231 expect(res.body.total).to.equal(1) 197 expect(body.total).to.equal(1)
232 198
233 const videos = res.body.data 199 const videos = body.data
234 expect(videos).to.have.lengthOf(1) 200 expect(videos).to.have.lengthOf(1)
235 expect(videos[0].name).to.equal('nsfw') 201 expect(videos[0].name).to.equal('nsfw')
236 } 202 }
237 }) 203 })
238 204
239 it('Should hide NSFW videos when the nsfw param === true', async function () { 205 it('Should hide NSFW videos when the nsfw param === true', async function () {
240 for (const res of await getVideosFunctions(server.accessToken, { nsfw: false })) { 206 for (const body of await getVideosFunctions(server.accessToken, { nsfw: 'false' })) {
241 expect(res.body.total).to.equal(1) 207 expect(body.total).to.equal(1)
242 208
243 const videos = res.body.data 209 const videos = body.data
244 expect(videos).to.have.lengthOf(1) 210 expect(videos).to.have.lengthOf(1)
245 expect(videos[0].name).to.equal('normal') 211 expect(videos[0].name).to.equal('normal')
246 } 212 }
247 }) 213 })
248 214
249 it('Should display both videos when the nsfw param === both', async function () { 215 it('Should display both videos when the nsfw param === both', async function () {
250 for (const res of await getVideosFunctions(server.accessToken, { nsfw: 'both' })) { 216 for (const body of await getVideosFunctions(server.accessToken, { nsfw: 'both' })) {
251 expect(res.body.total).to.equal(2) 217 expect(body.total).to.equal(2)
252 218
253 const videos = res.body.data 219 const videos = body.data
254 expect(videos).to.have.lengthOf(2) 220 expect(videos).to.have.lengthOf(2)
255 expect(videos[0].name).to.equal('normal') 221 expect(videos[0].name).to.equal('normal')
256 expect(videos[1].name).to.equal('nsfw') 222 expect(videos[1].name).to.equal('nsfw')
diff --git a/server/tests/api/videos/video-playlist-thumbnails.ts b/server/tests/api/videos/video-playlist-thumbnails.ts
index a93a0b7de..f0b2ca169 100644
--- a/server/tests/api/videos/video-playlist-thumbnails.ts
+++ b/server/tests/api/videos/video-playlist-thumbnails.ts
@@ -1,21 +1,15 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 addVideoInPlaylist,
7 cleanupTests, 6 cleanupTests,
8 createVideoPlaylist, 7 createMultipleServers,
9 doubleFollow, 8 doubleFollow,
10 flushAndRunMultipleServers, 9 PeerTubeServer,
11 getVideoPlaylistsList,
12 removeVideoFromPlaylist,
13 reorderVideosPlaylist,
14 ServerInfo,
15 setAccessTokensToServers, 10 setAccessTokensToServers,
16 setDefaultVideoChannel, 11 setDefaultVideoChannel,
17 testImage, 12 testImage,
18 uploadVideoAndGetId,
19 waitJobs 13 waitJobs
20} from '../../../../shared/extra-utils' 14} from '../../../../shared/extra-utils'
21import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' 15import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
@@ -23,10 +17,10 @@ import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/
23const expect = chai.expect 17const expect = chai.expect
24 18
25describe('Playlist thumbnail', function () { 19describe('Playlist thumbnail', function () {
26 let servers: ServerInfo[] = [] 20 let servers: PeerTubeServer[] = []
27 21
28 let playlistWithoutThumbnail: number 22 let playlistWithoutThumbnailId: number
29 let playlistWithThumbnail: number 23 let playlistWithThumbnailId: number
30 24
31 let withThumbnailE1: number 25 let withThumbnailE1: number
32 let withThumbnailE2: number 26 let withThumbnailE2: number
@@ -36,22 +30,22 @@ describe('Playlist thumbnail', function () {
36 let video1: number 30 let video1: number
37 let video2: number 31 let video2: number
38 32
39 async function getPlaylistWithoutThumbnail (server: ServerInfo) { 33 async function getPlaylistWithoutThumbnail (server: PeerTubeServer) {
40 const res = await getVideoPlaylistsList(server.url, 0, 10) 34 const body = await server.playlists.list({ start: 0, count: 10 })
41 35
42 return res.body.data.find(p => p.displayName === 'playlist without thumbnail') 36 return body.data.find(p => p.displayName === 'playlist without thumbnail')
43 } 37 }
44 38
45 async function getPlaylistWithThumbnail (server: ServerInfo) { 39 async function getPlaylistWithThumbnail (server: PeerTubeServer) {
46 const res = await getVideoPlaylistsList(server.url, 0, 10) 40 const body = await server.playlists.list({ start: 0, count: 10 })
47 41
48 return res.body.data.find(p => p.displayName === 'playlist with thumbnail') 42 return body.data.find(p => p.displayName === 'playlist with thumbnail')
49 } 43 }
50 44
51 before(async function () { 45 before(async function () {
52 this.timeout(120000) 46 this.timeout(120000)
53 47
54 servers = await flushAndRunMultipleServers(2, { transcoding: { enabled: false } }) 48 servers = await createMultipleServers(2, { transcoding: { enabled: false } })
55 49
56 // Get the access tokens 50 // Get the access tokens
57 await setAccessTokensToServers(servers) 51 await setAccessTokensToServers(servers)
@@ -60,8 +54,8 @@ describe('Playlist thumbnail', function () {
60 // Server 1 and server 2 follow each other 54 // Server 1 and server 2 follow each other
61 await doubleFollow(servers[0], servers[1]) 55 await doubleFollow(servers[0], servers[1])
62 56
63 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 1' })).id 57 video1 = (await servers[0].videos.quickUpload({ name: 'video 1' })).id
64 video2 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 2' })).id 58 video2 = (await servers[0].videos.quickUpload({ name: 'video 2' })).id
65 59
66 await waitJobs(servers) 60 await waitJobs(servers)
67 }) 61 })
@@ -69,24 +63,20 @@ describe('Playlist thumbnail', function () {
69 it('Should automatically update the thumbnail when adding an element', async function () { 63 it('Should automatically update the thumbnail when adding an element', async function () {
70 this.timeout(30000) 64 this.timeout(30000)
71 65
72 const res = await createVideoPlaylist({ 66 const created = await servers[1].playlists.create({
73 url: servers[1].url, 67 attributes: {
74 token: servers[1].accessToken,
75 playlistAttrs: {
76 displayName: 'playlist without thumbnail', 68 displayName: 'playlist without thumbnail',
77 privacy: VideoPlaylistPrivacy.PUBLIC, 69 privacy: VideoPlaylistPrivacy.PUBLIC,
78 videoChannelId: servers[1].videoChannel.id 70 videoChannelId: servers[1].store.channel.id
79 } 71 }
80 }) 72 })
81 playlistWithoutThumbnail = res.body.videoPlaylist.id 73 playlistWithoutThumbnailId = created.id
82 74
83 const res2 = await addVideoInPlaylist({ 75 const added = await servers[1].playlists.addElement({
84 url: servers[1].url, 76 playlistId: playlistWithoutThumbnailId,
85 token: servers[1].accessToken, 77 attributes: { videoId: video1 }
86 playlistId: playlistWithoutThumbnail,
87 elementAttrs: { videoId: video1 }
88 }) 78 })
89 withoutThumbnailE1 = res2.body.videoPlaylistElement.id 79 withoutThumbnailE1 = added.id
90 80
91 await waitJobs(servers) 81 await waitJobs(servers)
92 82
@@ -99,25 +89,21 @@ describe('Playlist thumbnail', function () {
99 it('Should not update the thumbnail if we explicitly uploaded a thumbnail', async function () { 89 it('Should not update the thumbnail if we explicitly uploaded a thumbnail', async function () {
100 this.timeout(30000) 90 this.timeout(30000)
101 91
102 const res = await createVideoPlaylist({ 92 const created = await servers[1].playlists.create({
103 url: servers[1].url, 93 attributes: {
104 token: servers[1].accessToken,
105 playlistAttrs: {
106 displayName: 'playlist with thumbnail', 94 displayName: 'playlist with thumbnail',
107 privacy: VideoPlaylistPrivacy.PUBLIC, 95 privacy: VideoPlaylistPrivacy.PUBLIC,
108 videoChannelId: servers[1].videoChannel.id, 96 videoChannelId: servers[1].store.channel.id,
109 thumbnailfile: 'thumbnail.jpg' 97 thumbnailfile: 'thumbnail.jpg'
110 } 98 }
111 }) 99 })
112 playlistWithThumbnail = res.body.videoPlaylist.id 100 playlistWithThumbnailId = created.id
113 101
114 const res2 = await addVideoInPlaylist({ 102 const added = await servers[1].playlists.addElement({
115 url: servers[1].url, 103 playlistId: playlistWithThumbnailId,
116 token: servers[1].accessToken, 104 attributes: { videoId: video1 }
117 playlistId: playlistWithThumbnail,
118 elementAttrs: { videoId: video1 }
119 }) 105 })
120 withThumbnailE1 = res2.body.videoPlaylistElement.id 106 withThumbnailE1 = added.id
121 107
122 await waitJobs(servers) 108 await waitJobs(servers)
123 109
@@ -130,19 +116,15 @@ describe('Playlist thumbnail', function () {
130 it('Should automatically update the thumbnail when moving the first element', async function () { 116 it('Should automatically update the thumbnail when moving the first element', async function () {
131 this.timeout(30000) 117 this.timeout(30000)
132 118
133 const res = await addVideoInPlaylist({ 119 const added = await servers[1].playlists.addElement({
134 url: servers[1].url, 120 playlistId: playlistWithoutThumbnailId,
135 token: servers[1].accessToken, 121 attributes: { videoId: video2 }
136 playlistId: playlistWithoutThumbnail,
137 elementAttrs: { videoId: video2 }
138 }) 122 })
139 withoutThumbnailE2 = res.body.videoPlaylistElement.id 123 withoutThumbnailE2 = added.id
140 124
141 await reorderVideosPlaylist({ 125 await servers[1].playlists.reorderElements({
142 url: servers[1].url, 126 playlistId: playlistWithoutThumbnailId,
143 token: servers[1].accessToken, 127 attributes: {
144 playlistId: playlistWithoutThumbnail,
145 elementAttrs: {
146 startPosition: 1, 128 startPosition: 1,
147 insertAfterPosition: 2 129 insertAfterPosition: 2
148 } 130 }
@@ -159,19 +141,15 @@ describe('Playlist thumbnail', function () {
159 it('Should not update the thumbnail when moving the first element if we explicitly uploaded a thumbnail', async function () { 141 it('Should not update the thumbnail when moving the first element if we explicitly uploaded a thumbnail', async function () {
160 this.timeout(30000) 142 this.timeout(30000)
161 143
162 const res = await addVideoInPlaylist({ 144 const added = await servers[1].playlists.addElement({
163 url: servers[1].url, 145 playlistId: playlistWithThumbnailId,
164 token: servers[1].accessToken, 146 attributes: { videoId: video2 }
165 playlistId: playlistWithThumbnail,
166 elementAttrs: { videoId: video2 }
167 }) 147 })
168 withThumbnailE2 = res.body.videoPlaylistElement.id 148 withThumbnailE2 = added.id
169 149
170 await reorderVideosPlaylist({ 150 await servers[1].playlists.reorderElements({
171 url: servers[1].url, 151 playlistId: playlistWithThumbnailId,
172 token: servers[1].accessToken, 152 attributes: {
173 playlistId: playlistWithThumbnail,
174 elementAttrs: {
175 startPosition: 1, 153 startPosition: 1,
176 insertAfterPosition: 2 154 insertAfterPosition: 2
177 } 155 }
@@ -188,11 +166,9 @@ describe('Playlist thumbnail', function () {
188 it('Should automatically update the thumbnail when deleting the first element', async function () { 166 it('Should automatically update the thumbnail when deleting the first element', async function () {
189 this.timeout(30000) 167 this.timeout(30000)
190 168
191 await removeVideoFromPlaylist({ 169 await servers[1].playlists.removeElement({
192 url: servers[1].url, 170 playlistId: playlistWithoutThumbnailId,
193 token: servers[1].accessToken, 171 elementId: withoutThumbnailE1
194 playlistId: playlistWithoutThumbnail,
195 playlistElementId: withoutThumbnailE1
196 }) 172 })
197 173
198 await waitJobs(servers) 174 await waitJobs(servers)
@@ -206,11 +182,9 @@ describe('Playlist thumbnail', function () {
206 it('Should not update the thumbnail when deleting the first element if we explicitly uploaded a thumbnail', async function () { 182 it('Should not update the thumbnail when deleting the first element if we explicitly uploaded a thumbnail', async function () {
207 this.timeout(30000) 183 this.timeout(30000)
208 184
209 await removeVideoFromPlaylist({ 185 await servers[1].playlists.removeElement({
210 url: servers[1].url, 186 playlistId: playlistWithThumbnailId,
211 token: servers[1].accessToken, 187 elementId: withThumbnailE1
212 playlistId: playlistWithThumbnail,
213 playlistElementId: withThumbnailE1
214 }) 188 })
215 189
216 await waitJobs(servers) 190 await waitJobs(servers)
@@ -224,11 +198,9 @@ describe('Playlist thumbnail', function () {
224 it('Should the thumbnail when we delete the last element', async function () { 198 it('Should the thumbnail when we delete the last element', async function () {
225 this.timeout(30000) 199 this.timeout(30000)
226 200
227 await removeVideoFromPlaylist({ 201 await servers[1].playlists.removeElement({
228 url: servers[1].url, 202 playlistId: playlistWithoutThumbnailId,
229 token: servers[1].accessToken, 203 elementId: withoutThumbnailE2
230 playlistId: playlistWithoutThumbnail,
231 playlistElementId: withoutThumbnailE2
232 }) 204 })
233 205
234 await waitJobs(servers) 206 await waitJobs(servers)
@@ -242,11 +214,9 @@ describe('Playlist thumbnail', function () {
242 it('Should not update the thumbnail when we delete the last element if we explicitly uploaded a thumbnail', async function () { 214 it('Should not update the thumbnail when we delete the last element if we explicitly uploaded a thumbnail', async function () {
243 this.timeout(30000) 215 this.timeout(30000)
244 216
245 await removeVideoFromPlaylist({ 217 await servers[1].playlists.removeElement({
246 url: servers[1].url, 218 playlistId: playlistWithThumbnailId,
247 token: servers[1].accessToken, 219 elementId: withThumbnailE2
248 playlistId: playlistWithThumbnail,
249 playlistElementId: withThumbnailE2
250 }) 220 })
251 221
252 await waitJobs(servers) 222 await waitJobs(servers)
diff --git a/server/tests/api/videos/video-playlists.ts b/server/tests/api/videos/video-playlists.ts
index da8de054b..f42aee2ff 100644
--- a/server/tests/api/videos/video-playlists.ts
+++ b/server/tests/api/videos/video-playlists.ts
@@ -2,71 +2,33 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6import { 5import {
7 addVideoChannel,
8 addVideoInPlaylist,
9 addVideoToBlacklist,
10 checkPlaylistFilesWereRemoved, 6 checkPlaylistFilesWereRemoved,
11 cleanupTests, 7 cleanupTests,
12 createUser, 8 createMultipleServers,
13 createVideoPlaylist,
14 deleteVideoChannel,
15 deleteVideoPlaylist,
16 doubleFollow, 9 doubleFollow,
17 doVideosExistInMyPlaylist, 10 PeerTubeServer,
18 flushAndRunMultipleServers, 11 PlaylistsCommand,
19 generateUserAccessToken,
20 getAccessToken,
21 getAccountPlaylistsList,
22 getAccountPlaylistsListWithToken,
23 getMyUserInformation,
24 getPlaylistVideos,
25 getVideoChannelPlaylistsList,
26 getVideoPlaylist,
27 getVideoPlaylistPrivacies,
28 getVideoPlaylistsList,
29 getVideoPlaylistWithToken,
30 removeUser,
31 removeVideoFromBlacklist,
32 removeVideoFromPlaylist,
33 reorderVideosPlaylist,
34 ServerInfo,
35 setAccessTokensToServers, 12 setAccessTokensToServers,
36 setDefaultVideoChannel, 13 setDefaultVideoChannel,
37 testImage, 14 testImage,
38 unfollow,
39 updateVideo,
40 updateVideoPlaylist,
41 updateVideoPlaylistElement,
42 uploadVideo,
43 uploadVideoAndGetId,
44 userLogin,
45 wait, 15 wait,
46 waitJobs 16 waitJobs
47} from '../../../../shared/extra-utils' 17} from '@shared/extra-utils'
48import { 18import {
49 addAccountToAccountBlocklist, 19 HttpStatusCode,
50 addAccountToServerBlocklist, 20 VideoPlaylist,
51 addServerToAccountBlocklist, 21 VideoPlaylistCreateResult,
52 addServerToServerBlocklist, 22 VideoPlaylistElementType,
53 removeAccountFromAccountBlocklist, 23 VideoPlaylistPrivacy,
54 removeAccountFromServerBlocklist, 24 VideoPlaylistType,
55 removeServerFromAccountBlocklist, 25 VideoPrivacy
56 removeServerFromServerBlocklist 26} from '@shared/models'
57} from '../../../../shared/extra-utils/users/blocklist'
58import { User } from '../../../../shared/models/users'
59import { VideoPlaylistCreateResult, VideoPrivacy } from '../../../../shared/models/videos'
60import { VideoExistInPlaylist } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
61import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../../shared/models/videos/playlist/video-playlist-element.model'
62import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
63import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
64import { VideoPlaylist } from '../../../../shared/models/videos/playlist/video-playlist.model'
65 27
66const expect = chai.expect 28const expect = chai.expect
67 29
68async function checkPlaylistElementType ( 30async function checkPlaylistElementType (
69 servers: ServerInfo[], 31 servers: PeerTubeServer[],
70 playlistId: string, 32 playlistId: string,
71 type: VideoPlaylistElementType, 33 type: VideoPlaylistElementType,
72 position: number, 34 position: number,
@@ -74,10 +36,10 @@ async function checkPlaylistElementType (
74 total: number 36 total: number
75) { 37) {
76 for (const server of servers) { 38 for (const server of servers) {
77 const res = await getPlaylistVideos(server.url, server.accessToken, playlistId, 0, 10) 39 const body = await server.playlists.listVideos({ token: server.accessToken, playlistId, start: 0, count: 10 })
78 expect(res.body.total).to.equal(total) 40 expect(body.total).to.equal(total)
79 41
80 const videoElement: VideoPlaylistElement = res.body.data.find((e: VideoPlaylistElement) => e.position === position) 42 const videoElement = body.data.find(e => e.position === position)
81 expect(videoElement.type).to.equal(type, 'On server ' + server.url) 43 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
82 44
83 if (type === VideoPlaylistElementType.REGULAR) { 45 if (type === VideoPlaylistElementType.REGULAR) {
@@ -90,11 +52,11 @@ async function checkPlaylistElementType (
90} 52}
91 53
92describe('Test video playlists', function () { 54describe('Test video playlists', function () {
93 let servers: ServerInfo[] = [] 55 let servers: PeerTubeServer[] = []
94 56
95 let playlistServer2Id1: number 57 let playlistServer2Id1: number
96 let playlistServer2Id2: number 58 let playlistServer2Id2: number
97 let playlistServer2UUID2: number 59 let playlistServer2UUID2: string
98 60
99 let playlistServer1Id: number 61 let playlistServer1Id: number
100 let playlistServer1UUID: string 62 let playlistServer1UUID: string
@@ -106,12 +68,14 @@ describe('Test video playlists', function () {
106 68
107 let nsfwVideoServer1: number 69 let nsfwVideoServer1: number
108 70
109 let userAccessTokenServer1: string 71 let userTokenServer1: string
72
73 let commands: PlaylistsCommand[]
110 74
111 before(async function () { 75 before(async function () {
112 this.timeout(120000) 76 this.timeout(120000)
113 77
114 servers = await flushAndRunMultipleServers(3, { transcoding: { enabled: false } }) 78 servers = await createMultipleServers(3, { transcoding: { enabled: false } })
115 79
116 // Get the access tokens 80 // Get the access tokens
117 await setAccessTokensToServers(servers) 81 await setAccessTokensToServers(servers)
@@ -122,86 +86,78 @@ describe('Test video playlists', function () {
122 // Server 1 and server 3 follow each other 86 // Server 1 and server 3 follow each other
123 await doubleFollow(servers[0], servers[2]) 87 await doubleFollow(servers[0], servers[2])
124 88
89 commands = servers.map(s => s.playlists)
90
125 { 91 {
126 servers[0].videos = [] 92 servers[0].store.videos = []
127 servers[1].videos = [] 93 servers[1].store.videos = []
128 servers[2].videos = [] 94 servers[2].store.videos = []
129 95
130 for (const server of servers) { 96 for (const server of servers) {
131 for (let i = 0; i < 7; i++) { 97 for (let i = 0; i < 7; i++) {
132 const name = `video ${i} server ${server.serverNumber}` 98 const name = `video ${i} server ${server.serverNumber}`
133 const resVideo = await uploadVideo(server.url, server.accessToken, { name, nsfw: false }) 99 const video = await server.videos.upload({ attributes: { name, nsfw: false } })
134 100
135 server.videos.push(resVideo.body.video) 101 server.store.videos.push(video)
136 } 102 }
137 } 103 }
138 } 104 }
139 105
140 nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'NSFW video', nsfw: true })).id 106 nsfwVideoServer1 = (await servers[0].videos.quickUpload({ name: 'NSFW video', nsfw: true })).id
141 107
142 { 108 userTokenServer1 = await servers[0].users.generateUserAndToken('user1')
143 await createUser({
144 url: servers[0].url,
145 accessToken: servers[0].accessToken,
146 username: 'user1',
147 password: 'password'
148 })
149 userAccessTokenServer1 = await getAccessToken(servers[0].url, 'user1', 'password')
150 }
151 109
152 await waitJobs(servers) 110 await waitJobs(servers)
153 }) 111 })
154 112
155 describe('Get default playlists', function () { 113 describe('Get default playlists', function () {
114
156 it('Should list video playlist privacies', async function () { 115 it('Should list video playlist privacies', async function () {
157 const res = await getVideoPlaylistPrivacies(servers[0].url) 116 const privacies = await commands[0].getPrivacies()
158 117
159 const privacies = res.body
160 expect(Object.keys(privacies)).to.have.length.at.least(3) 118 expect(Object.keys(privacies)).to.have.length.at.least(3)
161
162 expect(privacies[3]).to.equal('Private') 119 expect(privacies[3]).to.equal('Private')
163 }) 120 })
164 121
165 it('Should list watch later playlist', async function () { 122 it('Should list watch later playlist', async function () {
166 const url = servers[0].url 123 const token = servers[0].accessToken
167 const accessToken = servers[0].accessToken
168 124
169 { 125 {
170 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.WATCH_LATER) 126 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.WATCH_LATER })
171 127
172 expect(res.body.total).to.equal(1) 128 expect(body.total).to.equal(1)
173 expect(res.body.data).to.have.lengthOf(1) 129 expect(body.data).to.have.lengthOf(1)
174 130
175 const playlist: VideoPlaylist = res.body.data[0] 131 const playlist = body.data[0]
176 expect(playlist.displayName).to.equal('Watch later') 132 expect(playlist.displayName).to.equal('Watch later')
177 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER) 133 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
178 expect(playlist.type.label).to.equal('Watch later') 134 expect(playlist.type.label).to.equal('Watch later')
179 } 135 }
180 136
181 { 137 {
182 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.REGULAR) 138 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.REGULAR })
183 139
184 expect(res.body.total).to.equal(0) 140 expect(body.total).to.equal(0)
185 expect(res.body.data).to.have.lengthOf(0) 141 expect(body.data).to.have.lengthOf(0)
186 } 142 }
187 143
188 { 144 {
189 const res = await getAccountPlaylistsList(url, 'root', 0, 5) 145 const body = await commands[0].listByAccount({ handle: 'root' })
190 expect(res.body.total).to.equal(0) 146 expect(body.total).to.equal(0)
191 expect(res.body.data).to.have.lengthOf(0) 147 expect(body.data).to.have.lengthOf(0)
192 } 148 }
193 }) 149 })
194 150
195 it('Should get private playlist for a classic user', async function () { 151 it('Should get private playlist for a classic user', async function () {
196 const token = await generateUserAccessToken(servers[0], 'toto') 152 const token = await servers[0].users.generateUserAndToken('toto')
197 153
198 const res = await getAccountPlaylistsListWithToken(servers[0].url, token, 'toto', 0, 5) 154 const body = await commands[0].listByAccount({ token, handle: 'toto' })
199 155
200 expect(res.body.total).to.equal(1) 156 expect(body.total).to.equal(1)
201 expect(res.body.data).to.have.lengthOf(1) 157 expect(body.data).to.have.lengthOf(1)
202 158
203 const playlistId = res.body.data[0].id 159 const playlistId = body.data[0].id
204 await getPlaylistVideos(servers[0].url, token, playlistId, 0, 5) 160 await commands[0].listVideos({ token, playlistId })
205 }) 161 })
206 }) 162 })
207 163
@@ -210,15 +166,13 @@ describe('Test video playlists', function () {
210 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () { 166 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
211 this.timeout(30000) 167 this.timeout(30000)
212 168
213 await createVideoPlaylist({ 169 await commands[0].create({
214 url: servers[0].url, 170 attributes: {
215 token: servers[0].accessToken,
216 playlistAttrs: {
217 displayName: 'my super playlist', 171 displayName: 'my super playlist',
218 privacy: VideoPlaylistPrivacy.PUBLIC, 172 privacy: VideoPlaylistPrivacy.PUBLIC,
219 description: 'my super description', 173 description: 'my super description',
220 thumbnailfile: 'thumbnail.jpg', 174 thumbnailfile: 'thumbnail.jpg',
221 videoChannelId: servers[0].videoChannel.id 175 videoChannelId: servers[0].store.channel.id
222 } 176 }
223 }) 177 })
224 178
@@ -227,14 +181,13 @@ describe('Test video playlists', function () {
227 await wait(3000) 181 await wait(3000)
228 182
229 for (const server of servers) { 183 for (const server of servers) {
230 const res = await getVideoPlaylistsList(server.url, 0, 5) 184 const body = await server.playlists.list({ start: 0, count: 5 })
231 expect(res.body.total).to.equal(1) 185 expect(body.total).to.equal(1)
232 expect(res.body.data).to.have.lengthOf(1) 186 expect(body.data).to.have.lengthOf(1)
233 187
234 const playlistFromList = res.body.data[0] as VideoPlaylist 188 const playlistFromList = body.data[0]
235 189
236 const res2 = await getVideoPlaylist(server.url, playlistFromList.uuid) 190 const playlistFromGet = await server.playlists.get({ playlistId: playlistFromList.uuid })
237 const playlistFromGet = res2.body as VideoPlaylist
238 191
239 for (const playlist of [ playlistFromGet, playlistFromList ]) { 192 for (const playlist of [ playlistFromGet, playlistFromList ]) {
240 expect(playlist.id).to.be.a('number') 193 expect(playlist.id).to.be.a('number')
@@ -264,46 +217,38 @@ describe('Test video playlists', function () {
264 this.timeout(30000) 217 this.timeout(30000)
265 218
266 { 219 {
267 const res = await createVideoPlaylist({ 220 const playlist = await servers[1].playlists.create({
268 url: servers[1].url, 221 attributes: {
269 token: servers[1].accessToken,
270 playlistAttrs: {
271 displayName: 'playlist 2', 222 displayName: 'playlist 2',
272 privacy: VideoPlaylistPrivacy.PUBLIC, 223 privacy: VideoPlaylistPrivacy.PUBLIC,
273 videoChannelId: servers[1].videoChannel.id 224 videoChannelId: servers[1].store.channel.id
274 } 225 }
275 }) 226 })
276 playlistServer2Id1 = res.body.videoPlaylist.id 227 playlistServer2Id1 = playlist.id
277 } 228 }
278 229
279 { 230 {
280 const res = await createVideoPlaylist({ 231 const playlist = await servers[1].playlists.create({
281 url: servers[1].url, 232 attributes: {
282 token: servers[1].accessToken,
283 playlistAttrs: {
284 displayName: 'playlist 3', 233 displayName: 'playlist 3',
285 privacy: VideoPlaylistPrivacy.PUBLIC, 234 privacy: VideoPlaylistPrivacy.PUBLIC,
286 thumbnailfile: 'thumbnail.jpg', 235 thumbnailfile: 'thumbnail.jpg',
287 videoChannelId: servers[1].videoChannel.id 236 videoChannelId: servers[1].store.channel.id
288 } 237 }
289 }) 238 })
290 239
291 playlistServer2Id2 = res.body.videoPlaylist.id 240 playlistServer2Id2 = playlist.id
292 playlistServer2UUID2 = res.body.videoPlaylist.uuid 241 playlistServer2UUID2 = playlist.uuid
293 } 242 }
294 243
295 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) { 244 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
296 await addVideoInPlaylist({ 245 await servers[1].playlists.addElement({
297 url: servers[1].url,
298 token: servers[1].accessToken,
299 playlistId: id, 246 playlistId: id,
300 elementAttrs: { videoId: servers[1].videos[0].id, startTimestamp: 1, stopTimestamp: 2 } 247 attributes: { videoId: servers[1].store.videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
301 }) 248 })
302 await addVideoInPlaylist({ 249 await servers[1].playlists.addElement({
303 url: servers[1].url,
304 token: servers[1].accessToken,
305 playlistId: id, 250 playlistId: id,
306 elementAttrs: { videoId: servers[1].videos[1].id } 251 attributes: { videoId: servers[1].store.videos[1].id }
307 }) 252 })
308 } 253 }
309 254
@@ -311,20 +256,20 @@ describe('Test video playlists', function () {
311 await wait(3000) 256 await wait(3000)
312 257
313 for (const server of [ servers[0], servers[1] ]) { 258 for (const server of [ servers[0], servers[1] ]) {
314 const res = await getVideoPlaylistsList(server.url, 0, 5) 259 const body = await server.playlists.list({ start: 0, count: 5 })
315 260
316 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2') 261 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
317 expect(playlist2).to.not.be.undefined 262 expect(playlist2).to.not.be.undefined
318 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath) 263 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
319 264
320 const playlist3 = res.body.data.find(p => p.displayName === 'playlist 3') 265 const playlist3 = body.data.find(p => p.displayName === 'playlist 3')
321 expect(playlist3).to.not.be.undefined 266 expect(playlist3).to.not.be.undefined
322 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath) 267 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
323 } 268 }
324 269
325 const res = await getVideoPlaylistsList(servers[2].url, 0, 5) 270 const body = await servers[2].playlists.list({ start: 0, count: 5 })
326 expect(res.body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined 271 expect(body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
327 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined 272 expect(body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
328 }) 273 })
329 274
330 it('Should have the playlist on server 3 after a new follow', async function () { 275 it('Should have the playlist on server 3 after a new follow', async function () {
@@ -333,13 +278,13 @@ describe('Test video playlists', function () {
333 // Server 2 and server 3 follow each other 278 // Server 2 and server 3 follow each other
334 await doubleFollow(servers[1], servers[2]) 279 await doubleFollow(servers[1], servers[2])
335 280
336 const res = await getVideoPlaylistsList(servers[2].url, 0, 5) 281 const body = await servers[2].playlists.list({ start: 0, count: 5 })
337 282
338 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2') 283 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
339 expect(playlist2).to.not.be.undefined 284 expect(playlist2).to.not.be.undefined
340 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath) 285 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
341 286
342 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined 287 expect(body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
343 }) 288 })
344 }) 289 })
345 290
@@ -349,22 +294,20 @@ describe('Test video playlists', function () {
349 this.timeout(30000) 294 this.timeout(30000)
350 295
351 { 296 {
352 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, 'createdAt') 297 const body = await servers[2].playlists.list({ start: 1, count: 2, sort: 'createdAt' })
353 298 expect(body.total).to.equal(3)
354 expect(res.body.total).to.equal(3)
355 299
356 const data: VideoPlaylist[] = res.body.data 300 const data = body.data
357 expect(data).to.have.lengthOf(2) 301 expect(data).to.have.lengthOf(2)
358 expect(data[0].displayName).to.equal('playlist 2') 302 expect(data[0].displayName).to.equal('playlist 2')
359 expect(data[1].displayName).to.equal('playlist 3') 303 expect(data[1].displayName).to.equal('playlist 3')
360 } 304 }
361 305
362 { 306 {
363 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, '-createdAt') 307 const body = await servers[2].playlists.list({ start: 1, count: 2, sort: '-createdAt' })
308 expect(body.total).to.equal(3)
364 309
365 expect(res.body.total).to.equal(3) 310 const data = body.data
366
367 const data: VideoPlaylist[] = res.body.data
368 expect(data).to.have.lengthOf(2) 311 expect(data).to.have.lengthOf(2)
369 expect(data[0].displayName).to.equal('playlist 2') 312 expect(data[0].displayName).to.equal('playlist 2')
370 expect(data[1].displayName).to.equal('my super playlist') 313 expect(data[1].displayName).to.equal('my super playlist')
@@ -375,11 +318,10 @@ describe('Test video playlists', function () {
375 this.timeout(30000) 318 this.timeout(30000)
376 319
377 { 320 {
378 const res = await getVideoChannelPlaylistsList(servers[0].url, 'root_channel', 0, 2, '-createdAt') 321 const body = await commands[0].listByChannel({ handle: 'root_channel', start: 0, count: 2, sort: '-createdAt' })
379 322 expect(body.total).to.equal(1)
380 expect(res.body.total).to.equal(1)
381 323
382 const data: VideoPlaylist[] = res.body.data 324 const data = body.data
383 expect(data).to.have.lengthOf(1) 325 expect(data).to.have.lengthOf(1)
384 expect(data[0].displayName).to.equal('my super playlist') 326 expect(data[0].displayName).to.equal('my super playlist')
385 } 327 }
@@ -389,41 +331,37 @@ describe('Test video playlists', function () {
389 this.timeout(30000) 331 this.timeout(30000)
390 332
391 { 333 {
392 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, '-createdAt') 334 const body = await servers[1].playlists.listByAccount({ handle: 'root', start: 1, count: 2, sort: '-createdAt' })
335 expect(body.total).to.equal(2)
393 336
394 expect(res.body.total).to.equal(2) 337 const data = body.data
395
396 const data: VideoPlaylist[] = res.body.data
397 expect(data).to.have.lengthOf(1) 338 expect(data).to.have.lengthOf(1)
398 expect(data[0].displayName).to.equal('playlist 2') 339 expect(data[0].displayName).to.equal('playlist 2')
399 } 340 }
400 341
401 { 342 {
402 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, 'createdAt') 343 const body = await servers[1].playlists.listByAccount({ handle: 'root', start: 1, count: 2, sort: 'createdAt' })
403 344 expect(body.total).to.equal(2)
404 expect(res.body.total).to.equal(2)
405 345
406 const data: VideoPlaylist[] = res.body.data 346 const data = body.data
407 expect(data).to.have.lengthOf(1) 347 expect(data).to.have.lengthOf(1)
408 expect(data[0].displayName).to.equal('playlist 3') 348 expect(data[0].displayName).to.equal('playlist 3')
409 } 349 }
410 350
411 { 351 {
412 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '3') 352 const body = await servers[1].playlists.listByAccount({ handle: 'root', sort: 'createdAt', search: '3' })
353 expect(body.total).to.equal(1)
413 354
414 expect(res.body.total).to.equal(1) 355 const data = body.data
415
416 const data: VideoPlaylist[] = res.body.data
417 expect(data).to.have.lengthOf(1) 356 expect(data).to.have.lengthOf(1)
418 expect(data[0].displayName).to.equal('playlist 3') 357 expect(data[0].displayName).to.equal('playlist 3')
419 } 358 }
420 359
421 { 360 {
422 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '4') 361 const body = await servers[1].playlists.listByAccount({ handle: 'root', sort: 'createdAt', search: '4' })
423 362 expect(body.total).to.equal(0)
424 expect(res.body.total).to.equal(0)
425 363
426 const data: VideoPlaylist[] = res.body.data 364 const data = body.data
427 expect(data).to.have.lengthOf(0) 365 expect(data).to.have.lengthOf(0)
428 } 366 }
429 }) 367 })
@@ -437,28 +375,22 @@ describe('Test video playlists', function () {
437 this.timeout(30000) 375 this.timeout(30000)
438 376
439 { 377 {
440 const res = await createVideoPlaylist({ 378 unlistedPlaylist = await servers[1].playlists.create({
441 url: servers[1].url, 379 attributes: {
442 token: servers[1].accessToken,
443 playlistAttrs: {
444 displayName: 'playlist unlisted', 380 displayName: 'playlist unlisted',
445 privacy: VideoPlaylistPrivacy.UNLISTED, 381 privacy: VideoPlaylistPrivacy.UNLISTED,
446 videoChannelId: servers[1].videoChannel.id 382 videoChannelId: servers[1].store.channel.id
447 } 383 }
448 }) 384 })
449 unlistedPlaylist = res.body.videoPlaylist
450 } 385 }
451 386
452 { 387 {
453 const res = await createVideoPlaylist({ 388 privatePlaylist = await servers[1].playlists.create({
454 url: servers[1].url, 389 attributes: {
455 token: servers[1].accessToken,
456 playlistAttrs: {
457 displayName: 'playlist private', 390 displayName: 'playlist private',
458 privacy: VideoPlaylistPrivacy.PRIVATE 391 privacy: VideoPlaylistPrivacy.PRIVATE
459 } 392 }
460 }) 393 })
461 privatePlaylist = res.body.videoPlaylist
462 } 394 }
463 395
464 await waitJobs(servers) 396 await waitJobs(servers)
@@ -468,15 +400,15 @@ describe('Test video playlists', function () {
468 it('Should not list unlisted or private playlists', async function () { 400 it('Should not list unlisted or private playlists', async function () {
469 for (const server of servers) { 401 for (const server of servers) {
470 const results = [ 402 const results = [
471 await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[1].port, 0, 5, '-createdAt'), 403 await server.playlists.listByAccount({ handle: 'root@localhost:' + servers[1].port, sort: '-createdAt' }),
472 await getVideoPlaylistsList(server.url, 0, 2, '-createdAt') 404 await server.playlists.list({ start: 0, count: 2, sort: '-createdAt' })
473 ] 405 ]
474 406
475 expect(results[0].body.total).to.equal(2) 407 expect(results[0].total).to.equal(2)
476 expect(results[1].body.total).to.equal(3) 408 expect(results[1].total).to.equal(3)
477 409
478 for (const res of results) { 410 for (const body of results) {
479 const data: VideoPlaylist[] = res.body.data 411 const data = body.data
480 expect(data).to.have.lengthOf(2) 412 expect(data).to.have.lengthOf(2)
481 expect(data[0].displayName).to.equal('playlist 3') 413 expect(data[0].displayName).to.equal('playlist 3')
482 expect(data[1].displayName).to.equal('playlist 2') 414 expect(data[1].displayName).to.equal('playlist 2')
@@ -485,23 +417,23 @@ describe('Test video playlists', function () {
485 }) 417 })
486 418
487 it('Should not get unlisted playlist using only the id', async function () { 419 it('Should not get unlisted playlist using only the id', async function () {
488 await getVideoPlaylist(servers[1].url, unlistedPlaylist.id, 404) 420 await servers[1].playlists.get({ playlistId: unlistedPlaylist.id, expectedStatus: 404 })
489 }) 421 })
490 422
491 it('Should get unlisted plyaylist using uuid or shortUUID', async function () { 423 it('Should get unlisted plyaylist using uuid or shortUUID', async function () {
492 await getVideoPlaylist(servers[1].url, unlistedPlaylist.uuid) 424 await servers[1].playlists.get({ playlistId: unlistedPlaylist.uuid })
493 await getVideoPlaylist(servers[1].url, unlistedPlaylist.shortUUID) 425 await servers[1].playlists.get({ playlistId: unlistedPlaylist.shortUUID })
494 }) 426 })
495 427
496 it('Should not get private playlist without token', async function () { 428 it('Should not get private playlist without token', async function () {
497 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) { 429 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
498 await getVideoPlaylist(servers[1].url, id, 401) 430 await servers[1].playlists.get({ playlistId: id, expectedStatus: 401 })
499 } 431 }
500 }) 432 })
501 433
502 it('Should get private playlist with a token', async function () { 434 it('Should get private playlist with a token', async function () {
503 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) { 435 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
504 await getVideoPlaylistWithToken(servers[1].url, servers[1].accessToken, id) 436 await servers[1].playlists.get({ token: servers[1].accessToken, playlistId: id })
505 } 437 }
506 }) 438 })
507 }) 439 })
@@ -511,15 +443,13 @@ describe('Test video playlists', function () {
511 it('Should update a playlist', async function () { 443 it('Should update a playlist', async function () {
512 this.timeout(30000) 444 this.timeout(30000)
513 445
514 await updateVideoPlaylist({ 446 await servers[1].playlists.update({
515 url: servers[1].url, 447 attributes: {
516 token: servers[1].accessToken,
517 playlistAttrs: {
518 displayName: 'playlist 3 updated', 448 displayName: 'playlist 3 updated',
519 description: 'description updated', 449 description: 'description updated',
520 privacy: VideoPlaylistPrivacy.UNLISTED, 450 privacy: VideoPlaylistPrivacy.UNLISTED,
521 thumbnailfile: 'thumbnail.jpg', 451 thumbnailfile: 'thumbnail.jpg',
522 videoChannelId: servers[1].videoChannel.id 452 videoChannelId: servers[1].store.channel.id
523 }, 453 },
524 playlistId: playlistServer2Id2 454 playlistId: playlistServer2Id2
525 }) 455 })
@@ -527,8 +457,7 @@ describe('Test video playlists', function () {
527 await waitJobs(servers) 457 await waitJobs(servers)
528 458
529 for (const server of servers) { 459 for (const server of servers) {
530 const res = await getVideoPlaylist(server.url, playlistServer2UUID2) 460 const playlist = await server.playlists.get({ playlistId: playlistServer2UUID2 })
531 const playlist: VideoPlaylist = res.body
532 461
533 expect(playlist.displayName).to.equal('playlist 3 updated') 462 expect(playlist.displayName).to.equal('playlist 3 updated')
534 expect(playlist.description).to.equal('description updated') 463 expect(playlist.description).to.equal('description updated')
@@ -554,39 +483,37 @@ describe('Test video playlists', function () {
554 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () { 483 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
555 this.timeout(30000) 484 this.timeout(30000)
556 485
557 const addVideo = (elementAttrs: any) => { 486 const addVideo = (attributes: any) => {
558 return addVideoInPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: playlistServer1Id, elementAttrs }) 487 return commands[0].addElement({ playlistId: playlistServer1Id, attributes })
559 } 488 }
560 489
561 const res = await createVideoPlaylist({ 490 const playlist = await commands[0].create({
562 url: servers[0].url, 491 attributes: {
563 token: servers[0].accessToken,
564 playlistAttrs: {
565 displayName: 'playlist 4', 492 displayName: 'playlist 4',
566 privacy: VideoPlaylistPrivacy.PUBLIC, 493 privacy: VideoPlaylistPrivacy.PUBLIC,
567 videoChannelId: servers[0].videoChannel.id 494 videoChannelId: servers[0].store.channel.id
568 } 495 }
569 }) 496 })
570 497
571 playlistServer1Id = res.body.videoPlaylist.id 498 playlistServer1Id = playlist.id
572 playlistServer1UUID = res.body.videoPlaylist.uuid 499 playlistServer1UUID = playlist.uuid
573 500
574 await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 }) 501 await addVideo({ videoId: servers[0].store.videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
575 await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 }) 502 await addVideo({ videoId: servers[2].store.videos[1].uuid, startTimestamp: 35 })
576 await addVideo({ videoId: servers[2].videos[2].uuid }) 503 await addVideo({ videoId: servers[2].store.videos[2].uuid })
577 { 504 {
578 const res = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 }) 505 const element = await addVideo({ videoId: servers[0].store.videos[3].uuid, stopTimestamp: 35 })
579 playlistElementServer1Video4 = res.body.videoPlaylistElement.id 506 playlistElementServer1Video4 = element.id
580 } 507 }
581 508
582 { 509 {
583 const res = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 }) 510 const element = await addVideo({ videoId: servers[0].store.videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
584 playlistElementServer1Video5 = res.body.videoPlaylistElement.id 511 playlistElementServer1Video5 = element.id
585 } 512 }
586 513
587 { 514 {
588 const res = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 }) 515 const element = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
589 playlistElementNSFW = res.body.videoPlaylistElement.id 516 playlistElementNSFW = element.id
590 517
591 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 }) 518 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
592 await addVideo({ videoId: nsfwVideoServer1 }) 519 await addVideo({ videoId: nsfwVideoServer1 })
@@ -599,64 +526,68 @@ describe('Test video playlists', function () {
599 this.timeout(30000) 526 this.timeout(30000)
600 527
601 for (const server of servers) { 528 for (const server of servers) {
602 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) 529 {
603 530 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
604 expect(res.body.total).to.equal(8) 531
605 532 expect(body.total).to.equal(8)
606 const videoElements: VideoPlaylistElement[] = res.body.data 533
607 expect(videoElements).to.have.lengthOf(8) 534 const videoElements = body.data
608 535 expect(videoElements).to.have.lengthOf(8)
609 expect(videoElements[0].video.name).to.equal('video 0 server 1') 536
610 expect(videoElements[0].position).to.equal(1) 537 expect(videoElements[0].video.name).to.equal('video 0 server 1')
611 expect(videoElements[0].startTimestamp).to.equal(15) 538 expect(videoElements[0].position).to.equal(1)
612 expect(videoElements[0].stopTimestamp).to.equal(28) 539 expect(videoElements[0].startTimestamp).to.equal(15)
613 540 expect(videoElements[0].stopTimestamp).to.equal(28)
614 expect(videoElements[1].video.name).to.equal('video 1 server 3') 541
615 expect(videoElements[1].position).to.equal(2) 542 expect(videoElements[1].video.name).to.equal('video 1 server 3')
616 expect(videoElements[1].startTimestamp).to.equal(35) 543 expect(videoElements[1].position).to.equal(2)
617 expect(videoElements[1].stopTimestamp).to.be.null 544 expect(videoElements[1].startTimestamp).to.equal(35)
618 545 expect(videoElements[1].stopTimestamp).to.be.null
619 expect(videoElements[2].video.name).to.equal('video 2 server 3') 546
620 expect(videoElements[2].position).to.equal(3) 547 expect(videoElements[2].video.name).to.equal('video 2 server 3')
621 expect(videoElements[2].startTimestamp).to.be.null 548 expect(videoElements[2].position).to.equal(3)
622 expect(videoElements[2].stopTimestamp).to.be.null 549 expect(videoElements[2].startTimestamp).to.be.null
623 550 expect(videoElements[2].stopTimestamp).to.be.null
624 expect(videoElements[3].video.name).to.equal('video 3 server 1') 551
625 expect(videoElements[3].position).to.equal(4) 552 expect(videoElements[3].video.name).to.equal('video 3 server 1')
626 expect(videoElements[3].startTimestamp).to.be.null 553 expect(videoElements[3].position).to.equal(4)
627 expect(videoElements[3].stopTimestamp).to.equal(35) 554 expect(videoElements[3].startTimestamp).to.be.null
628 555 expect(videoElements[3].stopTimestamp).to.equal(35)
629 expect(videoElements[4].video.name).to.equal('video 4 server 1') 556
630 expect(videoElements[4].position).to.equal(5) 557 expect(videoElements[4].video.name).to.equal('video 4 server 1')
631 expect(videoElements[4].startTimestamp).to.equal(45) 558 expect(videoElements[4].position).to.equal(5)
632 expect(videoElements[4].stopTimestamp).to.equal(60) 559 expect(videoElements[4].startTimestamp).to.equal(45)
633 560 expect(videoElements[4].stopTimestamp).to.equal(60)
634 expect(videoElements[5].video.name).to.equal('NSFW video') 561
635 expect(videoElements[5].position).to.equal(6) 562 expect(videoElements[5].video.name).to.equal('NSFW video')
636 expect(videoElements[5].startTimestamp).to.equal(5) 563 expect(videoElements[5].position).to.equal(6)
637 expect(videoElements[5].stopTimestamp).to.be.null 564 expect(videoElements[5].startTimestamp).to.equal(5)
638 565 expect(videoElements[5].stopTimestamp).to.be.null
639 expect(videoElements[6].video.name).to.equal('NSFW video') 566
640 expect(videoElements[6].position).to.equal(7) 567 expect(videoElements[6].video.name).to.equal('NSFW video')
641 expect(videoElements[6].startTimestamp).to.equal(4) 568 expect(videoElements[6].position).to.equal(7)
642 expect(videoElements[6].stopTimestamp).to.be.null 569 expect(videoElements[6].startTimestamp).to.equal(4)
643 570 expect(videoElements[6].stopTimestamp).to.be.null
644 expect(videoElements[7].video.name).to.equal('NSFW video') 571
645 expect(videoElements[7].position).to.equal(8) 572 expect(videoElements[7].video.name).to.equal('NSFW video')
646 expect(videoElements[7].startTimestamp).to.be.null 573 expect(videoElements[7].position).to.equal(8)
647 expect(videoElements[7].stopTimestamp).to.be.null 574 expect(videoElements[7].startTimestamp).to.be.null
648 575 expect(videoElements[7].stopTimestamp).to.be.null
649 const res3 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 2) 576 }
650 expect(res3.body.data).to.have.lengthOf(2) 577
578 {
579 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 2 })
580 expect(body.data).to.have.lengthOf(2)
581 }
651 } 582 }
652 }) 583 })
653 }) 584 })
654 585
655 describe('Element type', function () { 586 describe('Element type', function () {
656 let groupUser1: ServerInfo[] 587 let groupUser1: PeerTubeServer[]
657 let groupWithoutToken1: ServerInfo[] 588 let groupWithoutToken1: PeerTubeServer[]
658 let group1: ServerInfo[] 589 let group1: PeerTubeServer[]
659 let group2: ServerInfo[] 590 let group2: PeerTubeServer[]
660 591
661 let video1: string 592 let video1: string
662 let video2: string 593 let video2: string
@@ -665,31 +596,30 @@ describe('Test video playlists', function () {
665 before(async function () { 596 before(async function () {
666 this.timeout(60000) 597 this.timeout(60000)
667 598
668 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userAccessTokenServer1 }) ] 599 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userTokenServer1 }) ]
669 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ] 600 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
670 group1 = [ servers[0] ] 601 group1 = [ servers[0] ]
671 group2 = [ servers[1], servers[2] ] 602 group2 = [ servers[1], servers[2] ]
672 603
673 const res = await createVideoPlaylist({ 604 const playlist = await commands[0].create({
674 url: servers[0].url, 605 token: userTokenServer1,
675 token: userAccessTokenServer1, 606 attributes: {
676 playlistAttrs: {
677 displayName: 'playlist 56', 607 displayName: 'playlist 56',
678 privacy: VideoPlaylistPrivacy.PUBLIC, 608 privacy: VideoPlaylistPrivacy.PUBLIC,
679 videoChannelId: servers[0].videoChannel.id 609 videoChannelId: servers[0].store.channel.id
680 } 610 }
681 }) 611 })
682 612
683 const playlistServer1Id2 = res.body.videoPlaylist.id 613 const playlistServer1Id2 = playlist.id
684 playlistServer1UUID2 = res.body.videoPlaylist.uuid 614 playlistServer1UUID2 = playlist.uuid
685 615
686 const addVideo = (elementAttrs: any) => { 616 const addVideo = (attributes: any) => {
687 return addVideoInPlaylist({ url: servers[0].url, token: userAccessTokenServer1, playlistId: playlistServer1Id2, elementAttrs }) 617 return commands[0].addElement({ token: userTokenServer1, playlistId: playlistServer1Id2, attributes })
688 } 618 }
689 619
690 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userAccessTokenServer1 })).uuid 620 video1 = (await servers[0].videos.quickUpload({ name: 'video 89', token: userTokenServer1 })).uuid
691 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid 621 video2 = (await servers[1].videos.quickUpload({ name: 'video 90' })).uuid
692 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid 622 video3 = (await servers[0].videos.quickUpload({ name: 'video 91', nsfw: true })).uuid
693 623
694 await waitJobs(servers) 624 await waitJobs(servers)
695 625
@@ -707,7 +637,7 @@ describe('Test video playlists', function () {
707 const position = 1 637 const position = 1
708 638
709 { 639 {
710 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PRIVATE }) 640 await servers[0].videos.update({ id: video1, attributes: { privacy: VideoPrivacy.PRIVATE } })
711 await waitJobs(servers) 641 await waitJobs(servers)
712 642
713 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 643 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
@@ -717,7 +647,7 @@ describe('Test video playlists', function () {
717 } 647 }
718 648
719 { 649 {
720 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PUBLIC }) 650 await servers[0].videos.update({ id: video1, attributes: { privacy: VideoPrivacy.PUBLIC } })
721 await waitJobs(servers) 651 await waitJobs(servers)
722 652
723 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 653 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
@@ -735,7 +665,7 @@ describe('Test video playlists', function () {
735 const position = 1 665 const position = 1
736 666
737 { 667 {
738 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, video1, 'reason', true) 668 await servers[0].blacklist.add({ videoId: video1, reason: 'reason', unfederate: true })
739 await waitJobs(servers) 669 await waitJobs(servers)
740 670
741 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 671 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
@@ -745,7 +675,7 @@ describe('Test video playlists', function () {
745 } 675 }
746 676
747 { 677 {
748 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, video1) 678 await servers[0].blacklist.remove({ videoId: video1 })
749 await waitJobs(servers) 679 await waitJobs(servers)
750 680
751 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 681 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
@@ -759,56 +689,58 @@ describe('Test video playlists', function () {
759 it('Should update the element type if the account or server of the video is blocked', async function () { 689 it('Should update the element type if the account or server of the video is blocked', async function () {
760 this.timeout(90000) 690 this.timeout(90000)
761 691
692 const command = servers[0].blocklist
693
762 const name = 'video 90' 694 const name = 'video 90'
763 const position = 2 695 const position = 2
764 696
765 { 697 {
766 await addAccountToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port) 698 await command.addToMyBlocklist({ token: userTokenServer1, account: 'root@localhost:' + servers[1].port })
767 await waitJobs(servers) 699 await waitJobs(servers)
768 700
769 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3) 701 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
770 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 702 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
771 703
772 await removeAccountFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port) 704 await command.removeFromMyBlocklist({ token: userTokenServer1, account: 'root@localhost:' + servers[1].port })
773 await waitJobs(servers) 705 await waitJobs(servers)
774 706
775 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 707 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
776 } 708 }
777 709
778 { 710 {
779 await addServerToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port) 711 await command.addToMyBlocklist({ token: userTokenServer1, server: 'localhost:' + servers[1].port })
780 await waitJobs(servers) 712 await waitJobs(servers)
781 713
782 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3) 714 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
783 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 715 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
784 716
785 await removeServerFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port) 717 await command.removeFromMyBlocklist({ token: userTokenServer1, server: 'localhost:' + servers[1].port })
786 await waitJobs(servers) 718 await waitJobs(servers)
787 719
788 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 720 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
789 } 721 }
790 722
791 { 723 {
792 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port) 724 await command.addToServerBlocklist({ account: 'root@localhost:' + servers[1].port })
793 await waitJobs(servers) 725 await waitJobs(servers)
794 726
795 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3) 727 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
796 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 728 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
797 729
798 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port) 730 await command.removeFromServerBlocklist({ account: 'root@localhost:' + servers[1].port })
799 await waitJobs(servers) 731 await waitJobs(servers)
800 732
801 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 733 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
802 } 734 }
803 735
804 { 736 {
805 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port) 737 await command.addToServerBlocklist({ server: 'localhost:' + servers[1].port })
806 await waitJobs(servers) 738 await waitJobs(servers)
807 739
808 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3) 740 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
809 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 741 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
810 742
811 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port) 743 await command.removeFromServerBlocklist({ server: 'localhost:' + servers[1].port })
812 await waitJobs(servers) 744 await waitJobs(servers)
813 745
814 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 746 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
@@ -816,10 +748,10 @@ describe('Test video playlists', function () {
816 }) 748 })
817 749
818 it('Should hide the video if it is NSFW', async function () { 750 it('Should hide the video if it is NSFW', async function () {
819 const res = await getPlaylistVideos(servers[0].url, userAccessTokenServer1, playlistServer1UUID2, 0, 10, { nsfw: false }) 751 const body = await commands[0].listVideos({ token: userTokenServer1, playlistId: playlistServer1UUID2, query: { nsfw: 'false' } })
820 expect(res.body.total).to.equal(3) 752 expect(body.total).to.equal(3)
821 753
822 const elements: VideoPlaylistElement[] = res.body.data 754 const elements = body.data
823 const element = elements.find(e => e.position === 3) 755 const element = elements.find(e => e.position === 3)
824 756
825 expect(element).to.exist 757 expect(element).to.exist
@@ -835,11 +767,9 @@ describe('Test video playlists', function () {
835 this.timeout(30000) 767 this.timeout(30000)
836 768
837 { 769 {
838 await reorderVideosPlaylist({ 770 await commands[0].reorderElements({
839 url: servers[0].url,
840 token: servers[0].accessToken,
841 playlistId: playlistServer1Id, 771 playlistId: playlistServer1Id,
842 elementAttrs: { 772 attributes: {
843 startPosition: 2, 773 startPosition: 2,
844 insertAfterPosition: 3 774 insertAfterPosition: 3
845 } 775 }
@@ -848,8 +778,8 @@ describe('Test video playlists', function () {
848 await waitJobs(servers) 778 await waitJobs(servers)
849 779
850 for (const server of servers) { 780 for (const server of servers) {
851 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) 781 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
852 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name) 782 const names = body.data.map(v => v.video.name)
853 783
854 expect(names).to.deep.equal([ 784 expect(names).to.deep.equal([
855 'video 0 server 1', 785 'video 0 server 1',
@@ -865,11 +795,9 @@ describe('Test video playlists', function () {
865 } 795 }
866 796
867 { 797 {
868 await reorderVideosPlaylist({ 798 await commands[0].reorderElements({
869 url: servers[0].url,
870 token: servers[0].accessToken,
871 playlistId: playlistServer1Id, 799 playlistId: playlistServer1Id,
872 elementAttrs: { 800 attributes: {
873 startPosition: 1, 801 startPosition: 1,
874 reorderLength: 3, 802 reorderLength: 3,
875 insertAfterPosition: 4 803 insertAfterPosition: 4
@@ -879,8 +807,8 @@ describe('Test video playlists', function () {
879 await waitJobs(servers) 807 await waitJobs(servers)
880 808
881 for (const server of servers) { 809 for (const server of servers) {
882 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) 810 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
883 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name) 811 const names = body.data.map(v => v.video.name)
884 812
885 expect(names).to.deep.equal([ 813 expect(names).to.deep.equal([
886 'video 3 server 1', 814 'video 3 server 1',
@@ -896,11 +824,9 @@ describe('Test video playlists', function () {
896 } 824 }
897 825
898 { 826 {
899 await reorderVideosPlaylist({ 827 await commands[0].reorderElements({
900 url: servers[0].url,
901 token: servers[0].accessToken,
902 playlistId: playlistServer1Id, 828 playlistId: playlistServer1Id,
903 elementAttrs: { 829 attributes: {
904 startPosition: 6, 830 startPosition: 6,
905 insertAfterPosition: 3 831 insertAfterPosition: 3
906 } 832 }
@@ -909,8 +835,7 @@ describe('Test video playlists', function () {
909 await waitJobs(servers) 835 await waitJobs(servers)
910 836
911 for (const server of servers) { 837 for (const server of servers) {
912 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) 838 const { data: elements } = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
913 const elements: VideoPlaylistElement[] = res.body.data
914 const names = elements.map(v => v.video.name) 839 const names = elements.map(v => v.video.name)
915 840
916 expect(names).to.deep.equal([ 841 expect(names).to.deep.equal([
@@ -934,22 +859,18 @@ describe('Test video playlists', function () {
934 it('Should update startTimestamp/endTimestamp of some elements', async function () { 859 it('Should update startTimestamp/endTimestamp of some elements', async function () {
935 this.timeout(30000) 860 this.timeout(30000)
936 861
937 await updateVideoPlaylistElement({ 862 await commands[0].updateElement({
938 url: servers[0].url,
939 token: servers[0].accessToken,
940 playlistId: playlistServer1Id, 863 playlistId: playlistServer1Id,
941 playlistElementId: playlistElementServer1Video4, 864 elementId: playlistElementServer1Video4,
942 elementAttrs: { 865 attributes: {
943 startTimestamp: 1 866 startTimestamp: 1
944 } 867 }
945 }) 868 })
946 869
947 await updateVideoPlaylistElement({ 870 await commands[0].updateElement({
948 url: servers[0].url,
949 token: servers[0].accessToken,
950 playlistId: playlistServer1Id, 871 playlistId: playlistServer1Id,
951 playlistElementId: playlistElementServer1Video5, 872 elementId: playlistElementServer1Video5,
952 elementAttrs: { 873 attributes: {
953 stopTimestamp: null 874 stopTimestamp: null
954 } 875 }
955 }) 876 })
@@ -957,8 +878,7 @@ describe('Test video playlists', function () {
957 await waitJobs(servers) 878 await waitJobs(servers)
958 879
959 for (const server of servers) { 880 for (const server of servers) {
960 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) 881 const { data: elements } = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
961 const elements: VideoPlaylistElement[] = res.body.data
962 882
963 expect(elements[0].video.name).to.equal('video 3 server 1') 883 expect(elements[0].video.name).to.equal('video 3 server 1')
964 expect(elements[0].position).to.equal(1) 884 expect(elements[0].position).to.equal(1)
@@ -974,17 +894,16 @@ describe('Test video playlists', function () {
974 894
975 it('Should check videos existence in my playlist', async function () { 895 it('Should check videos existence in my playlist', async function () {
976 const videoIds = [ 896 const videoIds = [
977 servers[0].videos[0].id, 897 servers[0].store.videos[0].id,
978 42000, 898 42000,
979 servers[0].videos[3].id, 899 servers[0].store.videos[3].id,
980 43000, 900 43000,
981 servers[0].videos[4].id 901 servers[0].store.videos[4].id
982 ] 902 ]
983 const res = await doVideosExistInMyPlaylist(servers[0].url, servers[0].accessToken, videoIds) 903 const obj = await commands[0].videosExist({ videoIds })
984 const obj = res.body as VideoExistInPlaylist
985 904
986 { 905 {
987 const elem = obj[servers[0].videos[0].id] 906 const elem = obj[servers[0].store.videos[0].id]
988 expect(elem).to.have.lengthOf(1) 907 expect(elem).to.have.lengthOf(1)
989 expect(elem[0].playlistElementId).to.exist 908 expect(elem[0].playlistElementId).to.exist
990 expect(elem[0].playlistId).to.equal(playlistServer1Id) 909 expect(elem[0].playlistId).to.equal(playlistServer1Id)
@@ -993,7 +912,7 @@ describe('Test video playlists', function () {
993 } 912 }
994 913
995 { 914 {
996 const elem = obj[servers[0].videos[3].id] 915 const elem = obj[servers[0].store.videos[3].id]
997 expect(elem).to.have.lengthOf(1) 916 expect(elem).to.have.lengthOf(1)
998 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4) 917 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
999 expect(elem[0].playlistId).to.equal(playlistServer1Id) 918 expect(elem[0].playlistId).to.equal(playlistServer1Id)
@@ -1002,7 +921,7 @@ describe('Test video playlists', function () {
1002 } 921 }
1003 922
1004 { 923 {
1005 const elem = obj[servers[0].videos[4].id] 924 const elem = obj[servers[0].store.videos[4].id]
1006 expect(elem).to.have.lengthOf(1) 925 expect(elem).to.have.lengthOf(1)
1007 expect(elem[0].playlistId).to.equal(playlistServer1Id) 926 expect(elem[0].playlistId).to.equal(playlistServer1Id)
1008 expect(elem[0].startTimestamp).to.equal(45) 927 expect(elem[0].startTimestamp).to.equal(45)
@@ -1015,42 +934,29 @@ describe('Test video playlists', function () {
1015 934
1016 it('Should automatically update updatedAt field of playlists', async function () { 935 it('Should automatically update updatedAt field of playlists', async function () {
1017 const server = servers[1] 936 const server = servers[1]
1018 const videoId = servers[1].videos[5].id 937 const videoId = servers[1].store.videos[5].id
1019 938
1020 async function getPlaylistNames () { 939 async function getPlaylistNames () {
1021 const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, undefined, '-updatedAt') 940 const { data } = await server.playlists.listByAccount({ token: server.accessToken, handle: 'root', sort: '-updatedAt' })
1022 941
1023 return (res.body.data as VideoPlaylist[]).map(p => p.displayName) 942 return data.map(p => p.displayName)
1024 } 943 }
1025 944
1026 const elementAttrs = { videoId } 945 const attributes = { videoId }
1027 const res1 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id1, elementAttrs }) 946 const element1 = await server.playlists.addElement({ playlistId: playlistServer2Id1, attributes })
1028 const res2 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id2, elementAttrs }) 947 const element2 = await server.playlists.addElement({ playlistId: playlistServer2Id2, attributes })
1029
1030 const element1 = res1.body.videoPlaylistElement.id
1031 const element2 = res2.body.videoPlaylistElement.id
1032 948
1033 const names1 = await getPlaylistNames() 949 const names1 = await getPlaylistNames()
1034 expect(names1[0]).to.equal('playlist 3 updated') 950 expect(names1[0]).to.equal('playlist 3 updated')
1035 expect(names1[1]).to.equal('playlist 2') 951 expect(names1[1]).to.equal('playlist 2')
1036 952
1037 await removeVideoFromPlaylist({ 953 await server.playlists.removeElement({ playlistId: playlistServer2Id1, elementId: element1.id })
1038 url: server.url,
1039 token: server.accessToken,
1040 playlistId: playlistServer2Id1,
1041 playlistElementId: element1
1042 })
1043 954
1044 const names2 = await getPlaylistNames() 955 const names2 = await getPlaylistNames()
1045 expect(names2[0]).to.equal('playlist 2') 956 expect(names2[0]).to.equal('playlist 2')
1046 expect(names2[1]).to.equal('playlist 3 updated') 957 expect(names2[1]).to.equal('playlist 3 updated')
1047 958
1048 await removeVideoFromPlaylist({ 959 await server.playlists.removeElement({ playlistId: playlistServer2Id2, elementId: element2.id })
1049 url: server.url,
1050 token: server.accessToken,
1051 playlistId: playlistServer2Id2,
1052 playlistElementId: element2
1053 })
1054 960
1055 const names3 = await getPlaylistNames() 961 const names3 = await getPlaylistNames()
1056 expect(names3[0]).to.equal('playlist 3 updated') 962 expect(names3[0]).to.equal('playlist 3 updated')
@@ -1060,28 +966,16 @@ describe('Test video playlists', function () {
1060 it('Should delete some elements', async function () { 966 it('Should delete some elements', async function () {
1061 this.timeout(30000) 967 this.timeout(30000)
1062 968
1063 await removeVideoFromPlaylist({ 969 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementServer1Video4 })
1064 url: servers[0].url, 970 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementNSFW })
1065 token: servers[0].accessToken,
1066 playlistId: playlistServer1Id,
1067 playlistElementId: playlistElementServer1Video4
1068 })
1069
1070 await removeVideoFromPlaylist({
1071 url: servers[0].url,
1072 token: servers[0].accessToken,
1073 playlistId: playlistServer1Id,
1074 playlistElementId: playlistElementNSFW
1075 })
1076 971
1077 await waitJobs(servers) 972 await waitJobs(servers)
1078 973
1079 for (const server of servers) { 974 for (const server of servers) {
1080 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) 975 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
976 expect(body.total).to.equal(6)
1081 977
1082 expect(res.body.total).to.equal(6) 978 const elements = body.data
1083
1084 const elements: VideoPlaylistElement[] = res.body.data
1085 expect(elements).to.have.lengthOf(6) 979 expect(elements).to.have.lengthOf(6)
1086 980
1087 expect(elements[0].video.name).to.equal('video 0 server 1') 981 expect(elements[0].video.name).to.equal('video 0 server 1')
@@ -1107,34 +1001,31 @@ describe('Test video playlists', function () {
1107 it('Should be able to create a public playlist, and set it to private', async function () { 1001 it('Should be able to create a public playlist, and set it to private', async function () {
1108 this.timeout(30000) 1002 this.timeout(30000)
1109 1003
1110 const res = await createVideoPlaylist({ 1004 const videoPlaylistIds = await commands[0].create({
1111 url: servers[0].url, 1005 attributes: {
1112 token: servers[0].accessToken,
1113 playlistAttrs: {
1114 displayName: 'my super public playlist', 1006 displayName: 'my super public playlist',
1115 privacy: VideoPlaylistPrivacy.PUBLIC, 1007 privacy: VideoPlaylistPrivacy.PUBLIC,
1116 videoChannelId: servers[0].videoChannel.id 1008 videoChannelId: servers[0].store.channel.id
1117 } 1009 }
1118 }) 1010 })
1119 const videoPlaylistIds = res.body.videoPlaylist
1120 1011
1121 await waitJobs(servers) 1012 await waitJobs(servers)
1122 1013
1123 for (const server of servers) { 1014 for (const server of servers) {
1124 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.OK_200) 1015 await server.playlists.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1125 } 1016 }
1126 1017
1127 const playlistAttrs = { privacy: VideoPlaylistPrivacy.PRIVATE } 1018 const attributes = { privacy: VideoPlaylistPrivacy.PRIVATE }
1128 await updateVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: videoPlaylistIds.id, playlistAttrs }) 1019 await commands[0].update({ playlistId: videoPlaylistIds.id, attributes })
1129 1020
1130 await waitJobs(servers) 1021 await waitJobs(servers)
1131 1022
1132 for (const server of [ servers[1], servers[2] ]) { 1023 for (const server of [ servers[1], servers[2] ]) {
1133 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.NOT_FOUND_404) 1024 await server.playlists.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1134 } 1025 }
1135 await getVideoPlaylist(servers[0].url, videoPlaylistIds.uuid, HttpStatusCode.UNAUTHORIZED_401)
1136 1026
1137 await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistIds.uuid, HttpStatusCode.OK_200) 1027 await commands[0].get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
1028 await commands[0].get({ token: servers[0].accessToken, playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1138 }) 1029 })
1139 }) 1030 })
1140 1031
@@ -1143,12 +1034,12 @@ describe('Test video playlists', function () {
1143 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () { 1034 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1144 this.timeout(30000) 1035 this.timeout(30000)
1145 1036
1146 await deleteVideoPlaylist(servers[0].url, servers[0].accessToken, playlistServer1Id) 1037 await commands[0].delete({ playlistId: playlistServer1Id })
1147 1038
1148 await waitJobs(servers) 1039 await waitJobs(servers)
1149 1040
1150 for (const server of servers) { 1041 for (const server of servers) {
1151 await getVideoPlaylist(server.url, playlistServer1UUID, HttpStatusCode.NOT_FOUND_404) 1042 await server.playlists.get({ playlistId: playlistServer1UUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1152 } 1043 }
1153 }) 1044 })
1154 1045
@@ -1163,75 +1054,61 @@ describe('Test video playlists', function () {
1163 it('Should unfollow servers 1 and 2 and hide their playlists', async function () { 1054 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1164 this.timeout(30000) 1055 this.timeout(30000)
1165 1056
1166 const finder = data => data.find(p => p.displayName === 'my super playlist') 1057 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'my super playlist')
1167 1058
1168 { 1059 {
1169 const res = await getVideoPlaylistsList(servers[2].url, 0, 5) 1060 const body = await servers[2].playlists.list({ start: 0, count: 5 })
1170 expect(res.body.total).to.equal(3) 1061 expect(body.total).to.equal(3)
1171 expect(finder(res.body.data)).to.not.be.undefined 1062
1063 expect(finder(body.data)).to.not.be.undefined
1172 } 1064 }
1173 1065
1174 await unfollow(servers[2].url, servers[2].accessToken, servers[0]) 1066 await servers[2].follows.unfollow({ target: servers[0] })
1175 1067
1176 { 1068 {
1177 const res = await getVideoPlaylistsList(servers[2].url, 0, 5) 1069 const body = await servers[2].playlists.list({ start: 0, count: 5 })
1178 expect(res.body.total).to.equal(1) 1070 expect(body.total).to.equal(1)
1179 1071
1180 expect(finder(res.body.data)).to.be.undefined 1072 expect(finder(body.data)).to.be.undefined
1181 } 1073 }
1182 }) 1074 })
1183 1075
1184 it('Should delete a channel and put the associated playlist in private mode', async function () { 1076 it('Should delete a channel and put the associated playlist in private mode', async function () {
1185 this.timeout(30000) 1077 this.timeout(30000)
1186 1078
1187 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, { name: 'super_channel', displayName: 'super channel' }) 1079 const channel = await servers[0].channels.create({ attributes: { name: 'super_channel', displayName: 'super channel' } })
1188 const videoChannelId = res.body.videoChannel.id
1189 1080
1190 const res2 = await createVideoPlaylist({ 1081 const playlistCreated = await commands[0].create({
1191 url: servers[0].url, 1082 attributes: {
1192 token: servers[0].accessToken,
1193 playlistAttrs: {
1194 displayName: 'channel playlist', 1083 displayName: 'channel playlist',
1195 privacy: VideoPlaylistPrivacy.PUBLIC, 1084 privacy: VideoPlaylistPrivacy.PUBLIC,
1196 videoChannelId 1085 videoChannelId: channel.id
1197 } 1086 }
1198 }) 1087 })
1199 const videoPlaylistUUID = res2.body.videoPlaylist.uuid
1200 1088
1201 await waitJobs(servers) 1089 await waitJobs(servers)
1202 1090
1203 await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'super_channel') 1091 await servers[0].channels.delete({ channelName: 'super_channel' })
1204 1092
1205 await waitJobs(servers) 1093 await waitJobs(servers)
1206 1094
1207 const res3 = await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistUUID) 1095 const body = await commands[0].get({ token: servers[0].accessToken, playlistId: playlistCreated.uuid })
1208 expect(res3.body.displayName).to.equal('channel playlist') 1096 expect(body.displayName).to.equal('channel playlist')
1209 expect(res3.body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE) 1097 expect(body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1210 1098
1211 await getVideoPlaylist(servers[1].url, videoPlaylistUUID, HttpStatusCode.NOT_FOUND_404) 1099 await servers[1].playlists.get({ playlistId: playlistCreated.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1212 }) 1100 })
1213 1101
1214 it('Should delete an account and delete its playlists', async function () { 1102 it('Should delete an account and delete its playlists', async function () {
1215 this.timeout(30000) 1103 this.timeout(30000)
1216 1104
1217 const user = { username: 'user_1', password: 'password' } 1105 const { userId, token } = await servers[0].users.generate('user_1')
1218 const res = await createUser({
1219 url: servers[0].url,
1220 accessToken: servers[0].accessToken,
1221 username: user.username,
1222 password: user.password
1223 })
1224
1225 const userId = res.body.user.id
1226 const userAccessToken = await userLogin(servers[0], user)
1227 1106
1228 const resChannel = await getMyUserInformation(servers[0].url, userAccessToken) 1107 const { videoChannels } = await servers[0].users.getMyInfo({ token })
1229 const userChannel = (resChannel.body as User).videoChannels[0] 1108 const userChannel = videoChannels[0]
1230 1109
1231 await createVideoPlaylist({ 1110 await commands[0].create({
1232 url: servers[0].url, 1111 attributes: {
1233 token: userAccessToken,
1234 playlistAttrs: {
1235 displayName: 'playlist to be deleted', 1112 displayName: 'playlist to be deleted',
1236 privacy: VideoPlaylistPrivacy.PUBLIC, 1113 privacy: VideoPlaylistPrivacy.PUBLIC,
1237 videoChannelId: userChannel.id 1114 videoChannelId: userChannel.id
@@ -1240,22 +1117,24 @@ describe('Test video playlists', function () {
1240 1117
1241 await waitJobs(servers) 1118 await waitJobs(servers)
1242 1119
1243 const finder = data => data.find(p => p.displayName === 'playlist to be deleted') 1120 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'playlist to be deleted')
1244 1121
1245 { 1122 {
1246 for (const server of [ servers[0], servers[1] ]) { 1123 for (const server of [ servers[0], servers[1] ]) {
1247 const res = await getVideoPlaylistsList(server.url, 0, 15) 1124 const body = await server.playlists.list({ start: 0, count: 15 })
1248 expect(finder(res.body.data)).to.not.be.undefined 1125
1126 expect(finder(body.data)).to.not.be.undefined
1249 } 1127 }
1250 } 1128 }
1251 1129
1252 await removeUser(servers[0].url, userId, servers[0].accessToken) 1130 await servers[0].users.remove({ userId })
1253 await waitJobs(servers) 1131 await waitJobs(servers)
1254 1132
1255 { 1133 {
1256 for (const server of [ servers[0], servers[1] ]) { 1134 for (const server of [ servers[0], servers[1] ]) {
1257 const res = await getVideoPlaylistsList(server.url, 0, 15) 1135 const body = await server.playlists.list({ start: 0, count: 15 })
1258 expect(finder(res.body.data)).to.be.undefined 1136
1137 expect(finder(body.data)).to.be.undefined
1259 } 1138 }
1260 } 1139 }
1261 }) 1140 })
diff --git a/server/tests/api/videos/video-privacy.ts b/server/tests/api/videos/video-privacy.ts
index 950aeb7cf..b51b3bcdd 100644
--- a/server/tests/api/videos/video-privacy.ts
+++ b/server/tests/api/videos/video-privacy.ts
@@ -2,28 +2,13 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 5import { cleanupTests, createSingleServer, doubleFollow, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
6import { Video, VideoCreateResult } from '@shared/models' 6import { HttpStatusCode, VideoCreateResult, VideoPrivacy } from '@shared/models'
7import {
8 cleanupTests,
9 flushAndRunServer,
10 getVideosList,
11 getVideosListWithToken,
12 ServerInfo,
13 setAccessTokensToServers,
14 uploadVideo
15} from '../../../../shared/extra-utils/index'
16import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
17import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
18import { userLogin } from '../../../../shared/extra-utils/users/login'
19import { createUser } from '../../../../shared/extra-utils/users/users'
20import { getMyVideos, getVideo, getVideoWithToken, updateVideo } from '../../../../shared/extra-utils/videos/videos'
21import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
22 7
23const expect = chai.expect 8const expect = chai.expect
24 9
25describe('Test video privacy', function () { 10describe('Test video privacy', function () {
26 const servers: ServerInfo[] = [] 11 const servers: PeerTubeServer[] = []
27 let anotherUserToken: string 12 let anotherUserToken: string
28 13
29 let privateVideoId: number 14 let privateVideoId: number
@@ -49,8 +34,8 @@ describe('Test video privacy', function () {
49 this.timeout(50000) 34 this.timeout(50000)
50 35
51 // Run servers 36 // Run servers
52 servers.push(await flushAndRunServer(1, dontFederateUnlistedConfig)) 37 servers.push(await createSingleServer(1, dontFederateUnlistedConfig))
53 servers.push(await flushAndRunServer(2)) 38 servers.push(await createSingleServer(2))
54 39
55 // Get the access tokens 40 // Get the access tokens
56 await setAccessTokensToServers(servers) 41 await setAccessTokensToServers(servers)
@@ -66,55 +51,53 @@ describe('Test video privacy', function () {
66 51
67 for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) { 52 for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) {
68 const attributes = { privacy } 53 const attributes = { privacy }
69 await uploadVideo(servers[0].url, servers[0].accessToken, attributes) 54 await servers[0].videos.upload({ attributes })
70 } 55 }
71 56
72 await waitJobs(servers) 57 await waitJobs(servers)
73 }) 58 })
74 59
75 it('Should not have these private and internal videos on server 2', async function () { 60 it('Should not have these private and internal videos on server 2', async function () {
76 const res = await getVideosList(servers[1].url) 61 const { total, data } = await servers[1].videos.list()
77 62
78 expect(res.body.total).to.equal(0) 63 expect(total).to.equal(0)
79 expect(res.body.data).to.have.lengthOf(0) 64 expect(data).to.have.lengthOf(0)
80 }) 65 })
81 66
82 it('Should not list the private and internal videos for an unauthenticated user on server 1', async function () { 67 it('Should not list the private and internal videos for an unauthenticated user on server 1', async function () {
83 const res = await getVideosList(servers[0].url) 68 const { total, data } = await servers[0].videos.list()
84 69
85 expect(res.body.total).to.equal(0) 70 expect(total).to.equal(0)
86 expect(res.body.data).to.have.lengthOf(0) 71 expect(data).to.have.lengthOf(0)
87 }) 72 })
88 73
89 it('Should not list the private video and list the internal video for an authenticated user on server 1', async function () { 74 it('Should not list the private video and list the internal video for an authenticated user on server 1', async function () {
90 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) 75 const { total, data } = await servers[0].videos.listWithToken()
91 76
92 expect(res.body.total).to.equal(1) 77 expect(total).to.equal(1)
93 expect(res.body.data).to.have.lengthOf(1) 78 expect(data).to.have.lengthOf(1)
94 79
95 expect(res.body.data[0].privacy.id).to.equal(VideoPrivacy.INTERNAL) 80 expect(data[0].privacy.id).to.equal(VideoPrivacy.INTERNAL)
96 }) 81 })
97 82
98 it('Should list my (private and internal) videos', async function () { 83 it('Should list my (private and internal) videos', async function () {
99 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 10) 84 const { total, data } = await servers[0].videos.listMyVideos()
100 85
101 expect(res.body.total).to.equal(2) 86 expect(total).to.equal(2)
102 expect(res.body.data).to.have.lengthOf(2) 87 expect(data).to.have.lengthOf(2)
103 88
104 const videos: Video[] = res.body.data 89 const privateVideo = data.find(v => v.privacy.id === VideoPrivacy.PRIVATE)
105
106 const privateVideo = videos.find(v => v.privacy.id === VideoPrivacy.PRIVATE)
107 privateVideoId = privateVideo.id 90 privateVideoId = privateVideo.id
108 privateVideoUUID = privateVideo.uuid 91 privateVideoUUID = privateVideo.uuid
109 92
110 const internalVideo = videos.find(v => v.privacy.id === VideoPrivacy.INTERNAL) 93 const internalVideo = data.find(v => v.privacy.id === VideoPrivacy.INTERNAL)
111 internalVideoId = internalVideo.id 94 internalVideoId = internalVideo.id
112 internalVideoUUID = internalVideo.uuid 95 internalVideoUUID = internalVideo.uuid
113 }) 96 })
114 97
115 it('Should not be able to watch the private/internal video with non authenticated user', async function () { 98 it('Should not be able to watch the private/internal video with non authenticated user', async function () {
116 await getVideo(servers[0].url, privateVideoUUID, HttpStatusCode.UNAUTHORIZED_401) 99 await servers[0].videos.get({ id: privateVideoUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
117 await getVideo(servers[0].url, internalVideoUUID, HttpStatusCode.UNAUTHORIZED_401) 100 await servers[0].videos.get({ id: internalVideoUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
118 }) 101 })
119 102
120 it('Should not be able to watch the private video with another user', async function () { 103 it('Should not be able to watch the private video with another user', async function () {
@@ -124,18 +107,23 @@ describe('Test video privacy', function () {
124 username: 'hello', 107 username: 'hello',
125 password: 'super password' 108 password: 'super password'
126 } 109 }
127 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 110 await servers[0].users.create({ username: user.username, password: user.password })
111
112 anotherUserToken = await servers[0].login.getAccessToken(user)
128 113
129 anotherUserToken = await userLogin(servers[0], user) 114 await servers[0].videos.getWithToken({
130 await getVideoWithToken(servers[0].url, anotherUserToken, privateVideoUUID, HttpStatusCode.FORBIDDEN_403) 115 token: anotherUserToken,
116 id: privateVideoUUID,
117 expectedStatus: HttpStatusCode.FORBIDDEN_403
118 })
131 }) 119 })
132 120
133 it('Should be able to watch the internal video with another user', async function () { 121 it('Should be able to watch the internal video with another user', async function () {
134 await getVideoWithToken(servers[0].url, anotherUserToken, internalVideoUUID, HttpStatusCode.OK_200) 122 await servers[0].videos.getWithToken({ token: anotherUserToken, id: internalVideoUUID })
135 }) 123 })
136 124
137 it('Should be able to watch the private video with the correct user', async function () { 125 it('Should be able to watch the private video with the correct user', async function () {
138 await getVideoWithToken(servers[0].url, servers[0].accessToken, privateVideoUUID, HttpStatusCode.OK_200) 126 await servers[0].videos.getWithToken({ id: privateVideoUUID })
139 }) 127 })
140 }) 128 })
141 129
@@ -148,7 +136,7 @@ describe('Test video privacy', function () {
148 name: 'unlisted video', 136 name: 'unlisted video',
149 privacy: VideoPrivacy.UNLISTED 137 privacy: VideoPrivacy.UNLISTED
150 } 138 }
151 await uploadVideo(servers[1].url, servers[1].accessToken, attributes) 139 await servers[1].videos.upload({ attributes })
152 140
153 // Server 2 has transcoding enabled 141 // Server 2 has transcoding enabled
154 await waitJobs(servers) 142 await waitJobs(servers)
@@ -156,32 +144,32 @@ describe('Test video privacy', function () {
156 144
157 it('Should not have this unlisted video listed on server 1 and 2', async function () { 145 it('Should not have this unlisted video listed on server 1 and 2', async function () {
158 for (const server of servers) { 146 for (const server of servers) {
159 const res = await getVideosList(server.url) 147 const { total, data } = await server.videos.list()
160 148
161 expect(res.body.total).to.equal(0) 149 expect(total).to.equal(0)
162 expect(res.body.data).to.have.lengthOf(0) 150 expect(data).to.have.lengthOf(0)
163 } 151 }
164 }) 152 })
165 153
166 it('Should list my (unlisted) videos', async function () { 154 it('Should list my (unlisted) videos', async function () {
167 const res = await getMyVideos(servers[1].url, servers[1].accessToken, 0, 1) 155 const { total, data } = await servers[1].videos.listMyVideos()
168 156
169 expect(res.body.total).to.equal(1) 157 expect(total).to.equal(1)
170 expect(res.body.data).to.have.lengthOf(1) 158 expect(data).to.have.lengthOf(1)
171 159
172 unlistedVideo = res.body.data[0] 160 unlistedVideo = data[0]
173 }) 161 })
174 162
175 it('Should not be able to get this unlisted video using its id', async function () { 163 it('Should not be able to get this unlisted video using its id', async function () {
176 await getVideo(servers[1].url, unlistedVideo.id, 404) 164 await servers[1].videos.get({ id: unlistedVideo.id, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
177 }) 165 })
178 166
179 it('Should be able to get this unlisted video using its uuid/shortUUID', async function () { 167 it('Should be able to get this unlisted video using its uuid/shortUUID', async function () {
180 for (const server of servers) { 168 for (const server of servers) {
181 for (const id of [ unlistedVideo.uuid, unlistedVideo.shortUUID ]) { 169 for (const id of [ unlistedVideo.uuid, unlistedVideo.shortUUID ]) {
182 const res = await getVideo(server.url, id) 170 const video = await server.videos.get({ id })
183 171
184 expect(res.body.name).to.equal('unlisted video') 172 expect(video.name).to.equal('unlisted video')
185 } 173 }
186 } 174 }
187 }) 175 })
@@ -193,28 +181,28 @@ describe('Test video privacy', function () {
193 name: 'unlisted video', 181 name: 'unlisted video',
194 privacy: VideoPrivacy.UNLISTED 182 privacy: VideoPrivacy.UNLISTED
195 } 183 }
196 await uploadVideo(servers[0].url, servers[0].accessToken, attributes) 184 await servers[0].videos.upload({ attributes })
197 185
198 await waitJobs(servers) 186 await waitJobs(servers)
199 }) 187 })
200 188
201 it('Should list my new unlisted video', async function () { 189 it('Should list my new unlisted video', async function () {
202 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 3) 190 const { total, data } = await servers[0].videos.listMyVideos()
203 191
204 expect(res.body.total).to.equal(3) 192 expect(total).to.equal(3)
205 expect(res.body.data).to.have.lengthOf(3) 193 expect(data).to.have.lengthOf(3)
206 194
207 nonFederatedUnlistedVideoUUID = res.body.data[0].uuid 195 nonFederatedUnlistedVideoUUID = data[0].uuid
208 }) 196 })
209 197
210 it('Should be able to get non-federated unlisted video from origin', async function () { 198 it('Should be able to get non-federated unlisted video from origin', async function () {
211 const res = await getVideo(servers[0].url, nonFederatedUnlistedVideoUUID) 199 const video = await servers[0].videos.get({ id: nonFederatedUnlistedVideoUUID })
212 200
213 expect(res.body.name).to.equal('unlisted video') 201 expect(video.name).to.equal('unlisted video')
214 }) 202 })
215 203
216 it('Should not be able to get non-federated unlisted video from federated server', async function () { 204 it('Should not be able to get non-federated unlisted video from federated server', async function () {
217 await getVideo(servers[1].url, nonFederatedUnlistedVideoUUID, HttpStatusCode.NOT_FOUND_404) 205 await servers[1].videos.get({ id: nonFederatedUnlistedVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
218 }) 206 })
219 }) 207 })
220 208
@@ -226,20 +214,20 @@ describe('Test video privacy', function () {
226 now = Date.now() 214 now = Date.now()
227 215
228 { 216 {
229 const attribute = { 217 const attributes = {
230 name: 'private video becomes public', 218 name: 'private video becomes public',
231 privacy: VideoPrivacy.PUBLIC 219 privacy: VideoPrivacy.PUBLIC
232 } 220 }
233 221
234 await updateVideo(servers[0].url, servers[0].accessToken, privateVideoId, attribute) 222 await servers[0].videos.update({ id: privateVideoId, attributes })
235 } 223 }
236 224
237 { 225 {
238 const attribute = { 226 const attributes = {
239 name: 'internal video becomes public', 227 name: 'internal video becomes public',
240 privacy: VideoPrivacy.PUBLIC 228 privacy: VideoPrivacy.PUBLIC
241 } 229 }
242 await updateVideo(servers[0].url, servers[0].accessToken, internalVideoId, attribute) 230 await servers[0].videos.update({ id: internalVideoId, attributes })
243 } 231 }
244 232
245 await waitJobs(servers) 233 await waitJobs(servers)
@@ -247,13 +235,12 @@ describe('Test video privacy', function () {
247 235
248 it('Should have this new public video listed on server 1 and 2', async function () { 236 it('Should have this new public video listed on server 1 and 2', async function () {
249 for (const server of servers) { 237 for (const server of servers) {
250 const res = await getVideosList(server.url) 238 const { total, data } = await server.videos.list()
251 expect(res.body.total).to.equal(2) 239 expect(total).to.equal(2)
252 expect(res.body.data).to.have.lengthOf(2) 240 expect(data).to.have.lengthOf(2)
253 241
254 const videos: Video[] = res.body.data 242 const privateVideo = data.find(v => v.name === 'private video becomes public')
255 const privateVideo = videos.find(v => v.name === 'private video becomes public') 243 const internalVideo = data.find(v => v.name === 'internal video becomes public')
256 const internalVideo = videos.find(v => v.name === 'internal video becomes public')
257 244
258 expect(privateVideo).to.not.be.undefined 245 expect(privateVideo).to.not.be.undefined
259 expect(internalVideo).to.not.be.undefined 246 expect(internalVideo).to.not.be.undefined
@@ -270,27 +257,25 @@ describe('Test video privacy', function () {
270 it('Should set these videos as private and internal', async function () { 257 it('Should set these videos as private and internal', async function () {
271 this.timeout(10000) 258 this.timeout(10000)
272 259
273 await updateVideo(servers[0].url, servers[0].accessToken, internalVideoId, { privacy: VideoPrivacy.PRIVATE }) 260 await servers[0].videos.update({ id: internalVideoId, attributes: { privacy: VideoPrivacy.PRIVATE } })
274 await updateVideo(servers[0].url, servers[0].accessToken, privateVideoId, { privacy: VideoPrivacy.INTERNAL }) 261 await servers[0].videos.update({ id: privateVideoId, attributes: { privacy: VideoPrivacy.INTERNAL } })
275 262
276 await waitJobs(servers) 263 await waitJobs(servers)
277 264
278 for (const server of servers) { 265 for (const server of servers) {
279 const res = await getVideosList(server.url) 266 const { total, data } = await server.videos.list()
280 267
281 expect(res.body.total).to.equal(0) 268 expect(total).to.equal(0)
282 expect(res.body.data).to.have.lengthOf(0) 269 expect(data).to.have.lengthOf(0)
283 } 270 }
284 271
285 { 272 {
286 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 5) 273 const { total, data } = await servers[0].videos.listMyVideos()
287 const videos = res.body.data 274 expect(total).to.equal(3)
288 275 expect(data).to.have.lengthOf(3)
289 expect(res.body.total).to.equal(3)
290 expect(videos).to.have.lengthOf(3)
291 276
292 const privateVideo = videos.find(v => v.name === 'private video becomes public') 277 const privateVideo = data.find(v => v.name === 'private video becomes public')
293 const internalVideo = videos.find(v => v.name === 'internal video becomes public') 278 const internalVideo = data.find(v => v.name === 'internal video becomes public')
294 279
295 expect(privateVideo).to.not.be.undefined 280 expect(privateVideo).to.not.be.undefined
296 expect(internalVideo).to.not.be.undefined 281 expect(internalVideo).to.not.be.undefined
diff --git a/server/tests/api/videos/video-schedule-update.ts b/server/tests/api/videos/video-schedule-update.ts
index 204f43611..3f7738784 100644
--- a/server/tests/api/videos/video-schedule-update.ts
+++ b/server/tests/api/videos/video-schedule-update.ts
@@ -1,22 +1,17 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { VideoPrivacy } from '../../../../shared/models/videos' 4import * as chai from 'chai'
6import { 5import {
7 cleanupTests, 6 cleanupTests,
7 createMultipleServers,
8 doubleFollow, 8 doubleFollow,
9 flushAndRunMultipleServers, 9 PeerTubeServer,
10 getMyVideos,
11 getVideosList,
12 getVideoWithToken,
13 ServerInfo,
14 setAccessTokensToServers, 10 setAccessTokensToServers,
15 updateVideo, 11 wait,
16 uploadVideo, 12 waitJobs
17 wait 13} from '@shared/extra-utils'
18} from '../../../../shared/extra-utils' 14import { VideoPrivacy } from '@shared/models'
19import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
20 15
21const expect = chai.expect 16const expect = chai.expect
22 17
@@ -28,14 +23,14 @@ function in10Seconds () {
28} 23}
29 24
30describe('Test video update scheduler', function () { 25describe('Test video update scheduler', function () {
31 let servers: ServerInfo[] = [] 26 let servers: PeerTubeServer[] = []
32 let video2UUID: string 27 let video2UUID: string
33 28
34 before(async function () { 29 before(async function () {
35 this.timeout(30000) 30 this.timeout(30000)
36 31
37 // Run servers 32 // Run servers
38 servers = await flushAndRunMultipleServers(2) 33 servers = await createMultipleServers(2)
39 34
40 await setAccessTokensToServers(servers) 35 await setAccessTokensToServers(servers)
41 36
@@ -45,35 +40,34 @@ describe('Test video update scheduler', function () {
45 it('Should upload a video and schedule an update in 10 seconds', async function () { 40 it('Should upload a video and schedule an update in 10 seconds', async function () {
46 this.timeout(10000) 41 this.timeout(10000)
47 42
48 const videoAttributes = { 43 const attributes = {
49 name: 'video 1', 44 name: 'video 1',
50 privacy: VideoPrivacy.PRIVATE, 45 privacy: VideoPrivacy.PRIVATE,
51 scheduleUpdate: { 46 scheduleUpdate: {
52 updateAt: in10Seconds().toISOString(), 47 updateAt: in10Seconds().toISOString(),
53 privacy: VideoPrivacy.PUBLIC 48 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
54 } 49 }
55 } 50 }
56 51
57 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 52 await servers[0].videos.upload({ attributes })
58 53
59 await waitJobs(servers) 54 await waitJobs(servers)
60 }) 55 })
61 56
62 it('Should not list the video (in privacy mode)', async function () { 57 it('Should not list the video (in privacy mode)', async function () {
63 for (const server of servers) { 58 for (const server of servers) {
64 const res = await getVideosList(server.url) 59 const { total } = await server.videos.list()
65 60
66 expect(res.body.total).to.equal(0) 61 expect(total).to.equal(0)
67 } 62 }
68 }) 63 })
69 64
70 it('Should have my scheduled video in my account videos', async function () { 65 it('Should have my scheduled video in my account videos', async function () {
71 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 5) 66 const { total, data } = await servers[0].videos.listMyVideos()
72 expect(res.body.total).to.equal(1) 67 expect(total).to.equal(1)
73 68
74 const videoFromList = res.body.data[0] 69 const videoFromList = data[0]
75 const res2 = await getVideoWithToken(servers[0].url, servers[0].accessToken, videoFromList.uuid) 70 const videoFromGet = await servers[0].videos.getWithToken({ id: videoFromList.uuid })
76 const videoFromGet = res2.body
77 71
78 for (const video of [ videoFromList, videoFromGet ]) { 72 for (const video of [ videoFromList, videoFromGet ]) {
79 expect(video.name).to.equal('video 1') 73 expect(video.name).to.equal('video 1')
@@ -90,23 +84,23 @@ describe('Test video update scheduler', function () {
90 await waitJobs(servers) 84 await waitJobs(servers)
91 85
92 for (const server of servers) { 86 for (const server of servers) {
93 const res = await getVideosList(server.url) 87 const { total, data } = await server.videos.list()
94 88
95 expect(res.body.total).to.equal(1) 89 expect(total).to.equal(1)
96 expect(res.body.data[0].name).to.equal('video 1') 90 expect(data[0].name).to.equal('video 1')
97 } 91 }
98 }) 92 })
99 93
100 it('Should upload a video without scheduling an update', async function () { 94 it('Should upload a video without scheduling an update', async function () {
101 this.timeout(10000) 95 this.timeout(10000)
102 96
103 const videoAttributes = { 97 const attributes = {
104 name: 'video 2', 98 name: 'video 2',
105 privacy: VideoPrivacy.PRIVATE 99 privacy: VideoPrivacy.PRIVATE
106 } 100 }
107 101
108 const res = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 102 const { uuid } = await servers[0].videos.upload({ attributes })
109 video2UUID = res.body.video.uuid 103 video2UUID = uuid
110 104
111 await waitJobs(servers) 105 await waitJobs(servers)
112 }) 106 })
@@ -114,31 +108,31 @@ describe('Test video update scheduler', function () {
114 it('Should update a video by scheduling an update', async function () { 108 it('Should update a video by scheduling an update', async function () {
115 this.timeout(10000) 109 this.timeout(10000)
116 110
117 const videoAttributes = { 111 const attributes = {
118 name: 'video 2 updated', 112 name: 'video 2 updated',
119 scheduleUpdate: { 113 scheduleUpdate: {
120 updateAt: in10Seconds().toISOString(), 114 updateAt: in10Seconds().toISOString(),
121 privacy: VideoPrivacy.PUBLIC 115 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
122 } 116 }
123 } 117 }
124 118
125 await updateVideo(servers[0].url, servers[0].accessToken, video2UUID, videoAttributes) 119 await servers[0].videos.update({ id: video2UUID, attributes })
126 await waitJobs(servers) 120 await waitJobs(servers)
127 }) 121 })
128 122
129 it('Should not display the updated video', async function () { 123 it('Should not display the updated video', async function () {
130 for (const server of servers) { 124 for (const server of servers) {
131 const res = await getVideosList(server.url) 125 const { total } = await server.videos.list()
132 126
133 expect(res.body.total).to.equal(1) 127 expect(total).to.equal(1)
134 } 128 }
135 }) 129 })
136 130
137 it('Should have my scheduled updated video in my account videos', async function () { 131 it('Should have my scheduled updated video in my account videos', async function () {
138 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 5) 132 const { total, data } = await servers[0].videos.listMyVideos()
139 expect(res.body.total).to.equal(2) 133 expect(total).to.equal(2)
140 134
141 const video = res.body.data.find(v => v.uuid === video2UUID) 135 const video = data.find(v => v.uuid === video2UUID)
142 expect(video).not.to.be.undefined 136 expect(video).not.to.be.undefined
143 137
144 expect(video.name).to.equal('video 2 updated') 138 expect(video.name).to.equal('video 2 updated')
@@ -155,11 +149,10 @@ describe('Test video update scheduler', function () {
155 await waitJobs(servers) 149 await waitJobs(servers)
156 150
157 for (const server of servers) { 151 for (const server of servers) {
158 const res = await getVideosList(server.url) 152 const { total, data } = await server.videos.list()
159 153 expect(total).to.equal(2)
160 expect(res.body.total).to.equal(2)
161 154
162 const video = res.body.data.find(v => v.uuid === video2UUID) 155 const video = data.find(v => v.uuid === video2UUID)
163 expect(video).not.to.be.undefined 156 expect(video).not.to.be.undefined
164 expect(video.name).to.equal('video 2 updated') 157 expect(video.name).to.equal('video 2 updated')
165 } 158 }
diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts
index ea5ffd239..2a09e95bf 100644
--- a/server/tests/api/videos/video-transcoder.ts
+++ b/server/tests/api/videos/video-transcoder.ts
@@ -2,36 +2,23 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { FfprobeData } from 'fluent-ffmpeg'
6import { omit } from 'lodash' 5import { omit } from 'lodash'
7import { join } from 'path'
8import { Job } from '@shared/models'
9import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants'
10import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
11import { 6import {
12 buildAbsoluteFixturePath, 7 buildAbsoluteFixturePath,
13 buildServerDirectory,
14 cleanupTests, 8 cleanupTests,
9 createMultipleServers,
15 doubleFollow, 10 doubleFollow,
16 flushAndRunMultipleServers,
17 generateHighBitrateVideo, 11 generateHighBitrateVideo,
18 generateVideoWithFramerate, 12 generateVideoWithFramerate,
19 getJobsListPaginationAndSort, 13 getFileSize,
20 getMyVideos,
21 getServerFileSize,
22 getVideo,
23 getVideoFileMetadataUrl,
24 getVideosList,
25 makeGetRequest, 14 makeGetRequest,
26 ServerInfo, 15 PeerTubeServer,
27 setAccessTokensToServers, 16 setAccessTokensToServers,
28 updateCustomSubConfig,
29 uploadVideo,
30 uploadVideoAndGetId,
31 waitJobs, 17 waitJobs,
32 webtorrentAdd 18 webtorrentAdd
33} from '../../../../shared/extra-utils' 19} from '@shared/extra-utils'
34import { getMaxBitrate, VideoDetails, VideoResolution, VideoState } from '../../../../shared/models/videos' 20import { getMaxBitrate, HttpStatusCode, VideoResolution, VideoState } from '@shared/models'
21import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants'
35import { 22import {
36 canDoQuickTranscode, 23 canDoQuickTranscode,
37 getAudioStream, 24 getAudioStream,
@@ -43,37 +30,39 @@ import {
43 30
44const expect = chai.expect 31const expect = chai.expect
45 32
46function updateConfigForTranscoding (server: ServerInfo) { 33function updateConfigForTranscoding (server: PeerTubeServer) {
47 return updateCustomSubConfig(server.url, server.accessToken, { 34 return server.config.updateCustomSubConfig({
48 transcoding: { 35 newConfig: {
49 enabled: true, 36 transcoding: {
50 allowAdditionalExtensions: true, 37 enabled: true,
51 allowAudioFiles: true, 38 allowAdditionalExtensions: true,
52 hls: { enabled: true }, 39 allowAudioFiles: true,
53 webtorrent: { enabled: true }, 40 hls: { enabled: true },
54 resolutions: { 41 webtorrent: { enabled: true },
55 '0p': false, 42 resolutions: {
56 '240p': true, 43 '0p': false,
57 '360p': true, 44 '240p': true,
58 '480p': true, 45 '360p': true,
59 '720p': true, 46 '480p': true,
60 '1080p': true, 47 '720p': true,
61 '1440p': true, 48 '1080p': true,
62 '2160p': true 49 '1440p': true,
50 '2160p': true
51 }
63 } 52 }
64 } 53 }
65 }) 54 })
66} 55}
67 56
68describe('Test video transcoding', function () { 57describe('Test video transcoding', function () {
69 let servers: ServerInfo[] = [] 58 let servers: PeerTubeServer[] = []
70 let video4k: string 59 let video4k: string
71 60
72 before(async function () { 61 before(async function () {
73 this.timeout(30_000) 62 this.timeout(30_000)
74 63
75 // Run servers 64 // Run servers
76 servers = await flushAndRunMultipleServers(2) 65 servers = await createMultipleServers(2)
77 66
78 await setAccessTokensToServers(servers) 67 await setAccessTokensToServers(servers)
79 68
@@ -87,21 +76,20 @@ describe('Test video transcoding', function () {
87 it('Should not transcode video on server 1', async function () { 76 it('Should not transcode video on server 1', async function () {
88 this.timeout(60_000) 77 this.timeout(60_000)
89 78
90 const videoAttributes = { 79 const attributes = {
91 name: 'my super name for server 1', 80 name: 'my super name for server 1',
92 description: 'my super description for server 1', 81 description: 'my super description for server 1',
93 fixture: 'video_short.webm' 82 fixture: 'video_short.webm'
94 } 83 }
95 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 84 await servers[0].videos.upload({ attributes })
96 85
97 await waitJobs(servers) 86 await waitJobs(servers)
98 87
99 for (const server of servers) { 88 for (const server of servers) {
100 const res = await getVideosList(server.url) 89 const { data } = await server.videos.list()
101 const video = res.body.data[0] 90 const video = data[0]
102 91
103 const res2 = await getVideo(server.url, video.id) 92 const videoDetails = await server.videos.get({ id: video.id })
104 const videoDetails = res2.body
105 expect(videoDetails.files).to.have.lengthOf(1) 93 expect(videoDetails.files).to.have.lengthOf(1)
106 94
107 const magnetUri = videoDetails.files[0].magnetUri 95 const magnetUri = videoDetails.files[0].magnetUri
@@ -117,21 +105,20 @@ describe('Test video transcoding', function () {
117 it('Should transcode video on server 2', async function () { 105 it('Should transcode video on server 2', async function () {
118 this.timeout(120_000) 106 this.timeout(120_000)
119 107
120 const videoAttributes = { 108 const attributes = {
121 name: 'my super name for server 2', 109 name: 'my super name for server 2',
122 description: 'my super description for server 2', 110 description: 'my super description for server 2',
123 fixture: 'video_short.webm' 111 fixture: 'video_short.webm'
124 } 112 }
125 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 113 await servers[1].videos.upload({ attributes })
126 114
127 await waitJobs(servers) 115 await waitJobs(servers)
128 116
129 for (const server of servers) { 117 for (const server of servers) {
130 const res = await getVideosList(server.url) 118 const { data } = await server.videos.list()
131 119
132 const video = res.body.data.find(v => v.name === videoAttributes.name) 120 const video = data.find(v => v.name === attributes.name)
133 const res2 = await getVideo(server.url, video.id) 121 const videoDetails = await server.videos.get({ id: video.id })
134 const videoDetails = res2.body
135 122
136 expect(videoDetails.files).to.have.lengthOf(4) 123 expect(videoDetails.files).to.have.lengthOf(4)
137 124
@@ -150,47 +137,50 @@ describe('Test video transcoding', function () {
150 137
151 { 138 {
152 // Upload the video, but wait transcoding 139 // Upload the video, but wait transcoding
153 const videoAttributes = { 140 const attributes = {
154 name: 'waiting video', 141 name: 'waiting video',
155 fixture: 'video_short1.webm', 142 fixture: 'video_short1.webm',
156 waitTranscoding: true 143 waitTranscoding: true
157 } 144 }
158 const resVideo = await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 145 const { uuid } = await servers[1].videos.upload({ attributes })
159 const videoId = resVideo.body.video.uuid 146 const videoId = uuid
160 147
161 // Should be in transcode state 148 // Should be in transcode state
162 const { body } = await getVideo(servers[1].url, videoId) 149 const body = await servers[1].videos.get({ id: videoId })
163 expect(body.name).to.equal('waiting video') 150 expect(body.name).to.equal('waiting video')
164 expect(body.state.id).to.equal(VideoState.TO_TRANSCODE) 151 expect(body.state.id).to.equal(VideoState.TO_TRANSCODE)
165 expect(body.state.label).to.equal('To transcode') 152 expect(body.state.label).to.equal('To transcode')
166 expect(body.waitTranscoding).to.be.true 153 expect(body.waitTranscoding).to.be.true
167 154
168 // Should have my video 155 {
169 const resMyVideos = await getMyVideos(servers[1].url, servers[1].accessToken, 0, 10) 156 // Should have my video
170 const videoToFindInMine = resMyVideos.body.data.find(v => v.name === videoAttributes.name) 157 const { data } = await servers[1].videos.listMyVideos()
171 expect(videoToFindInMine).not.to.be.undefined 158 const videoToFindInMine = data.find(v => v.name === attributes.name)
172 expect(videoToFindInMine.state.id).to.equal(VideoState.TO_TRANSCODE) 159 expect(videoToFindInMine).not.to.be.undefined
173 expect(videoToFindInMine.state.label).to.equal('To transcode') 160 expect(videoToFindInMine.state.id).to.equal(VideoState.TO_TRANSCODE)
174 expect(videoToFindInMine.waitTranscoding).to.be.true 161 expect(videoToFindInMine.state.label).to.equal('To transcode')
162 expect(videoToFindInMine.waitTranscoding).to.be.true
163 }
175 164
176 // Should not list this video 165 {
177 const resVideos = await getVideosList(servers[1].url) 166 // Should not list this video
178 const videoToFindInList = resVideos.body.data.find(v => v.name === videoAttributes.name) 167 const { data } = await servers[1].videos.list()
179 expect(videoToFindInList).to.be.undefined 168 const videoToFindInList = data.find(v => v.name === attributes.name)
169 expect(videoToFindInList).to.be.undefined
170 }
180 171
181 // Server 1 should not have the video yet 172 // Server 1 should not have the video yet
182 await getVideo(servers[0].url, videoId, HttpStatusCode.NOT_FOUND_404) 173 await servers[0].videos.get({ id: videoId, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
183 } 174 }
184 175
185 await waitJobs(servers) 176 await waitJobs(servers)
186 177
187 for (const server of servers) { 178 for (const server of servers) {
188 const res = await getVideosList(server.url) 179 const { data } = await server.videos.list()
189 const videoToFind = res.body.data.find(v => v.name === 'waiting video') 180 const videoToFind = data.find(v => v.name === 'waiting video')
190 expect(videoToFind).not.to.be.undefined 181 expect(videoToFind).not.to.be.undefined
191 182
192 const res2 = await getVideo(server.url, videoToFind.id) 183 const videoDetails = await server.videos.get({ id: videoToFind.id })
193 const videoDetails: VideoDetails = res2.body
194 184
195 expect(videoDetails.state.id).to.equal(VideoState.PUBLISHED) 185 expect(videoDetails.state.id).to.equal(VideoState.PUBLISHED)
196 expect(videoDetails.state.label).to.equal('Published') 186 expect(videoDetails.state.label).to.equal('Published')
@@ -211,22 +201,20 @@ describe('Test video transcoding', function () {
211 } 201 }
212 202
213 for (const fixture of [ 'video_short.mkv', 'video_short.avi' ]) { 203 for (const fixture of [ 'video_short.mkv', 'video_short.avi' ]) {
214 const videoAttributes = { 204 const attributes = {
215 name: fixture, 205 name: fixture,
216 fixture 206 fixture
217 } 207 }
218 208
219 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 209 await servers[1].videos.upload({ attributes })
220 210
221 await waitJobs(servers) 211 await waitJobs(servers)
222 212
223 for (const server of servers) { 213 for (const server of servers) {
224 const res = await getVideosList(server.url) 214 const { data } = await server.videos.list()
225
226 const video = res.body.data.find(v => v.name === videoAttributes.name)
227 const res2 = await getVideo(server.url, video.id)
228 const videoDetails = res2.body
229 215
216 const video = data.find(v => v.name === attributes.name)
217 const videoDetails = await server.videos.get({ id: video.id })
230 expect(videoDetails.files).to.have.lengthOf(4) 218 expect(videoDetails.files).to.have.lengthOf(4)
231 219
232 const magnetUri = videoDetails.files[0].magnetUri 220 const magnetUri = videoDetails.files[0].magnetUri
@@ -238,22 +226,20 @@ describe('Test video transcoding', function () {
238 it('Should transcode a 4k video', async function () { 226 it('Should transcode a 4k video', async function () {
239 this.timeout(200_000) 227 this.timeout(200_000)
240 228
241 const videoAttributes = { 229 const attributes = {
242 name: '4k video', 230 name: '4k video',
243 fixture: 'video_short_4k.mp4' 231 fixture: 'video_short_4k.mp4'
244 } 232 }
245 233
246 const resUpload = await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 234 const { uuid } = await servers[1].videos.upload({ attributes })
247 video4k = resUpload.body.video.uuid 235 video4k = uuid
248 236
249 await waitJobs(servers) 237 await waitJobs(servers)
250 238
251 const resolutions = [ 240, 360, 480, 720, 1080, 1440, 2160 ] 239 const resolutions = [ 240, 360, 480, 720, 1080, 1440, 2160 ]
252 240
253 for (const server of servers) { 241 for (const server of servers) {
254 const res = await getVideo(server.url, video4k) 242 const videoDetails = await server.videos.get({ id: video4k })
255 const videoDetails: VideoDetails = res.body
256
257 expect(videoDetails.files).to.have.lengthOf(resolutions.length) 243 expect(videoDetails.files).to.have.lengthOf(resolutions.length)
258 244
259 for (const r of resolutions) { 245 for (const r of resolutions) {
@@ -269,24 +255,24 @@ describe('Test video transcoding', function () {
269 it('Should transcode high bit rate mp3 to proper bit rate', async function () { 255 it('Should transcode high bit rate mp3 to proper bit rate', async function () {
270 this.timeout(60_000) 256 this.timeout(60_000)
271 257
272 const videoAttributes = { 258 const attributes = {
273 name: 'mp3_256k', 259 name: 'mp3_256k',
274 fixture: 'video_short_mp3_256k.mp4' 260 fixture: 'video_short_mp3_256k.mp4'
275 } 261 }
276 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 262 await servers[1].videos.upload({ attributes })
277 263
278 await waitJobs(servers) 264 await waitJobs(servers)
279 265
280 for (const server of servers) { 266 for (const server of servers) {
281 const res = await getVideosList(server.url) 267 const { data } = await server.videos.list()
282 268
283 const video = res.body.data.find(v => v.name === videoAttributes.name) 269 const video = data.find(v => v.name === attributes.name)
284 const res2 = await getVideo(server.url, video.id) 270 const videoDetails = await server.videos.get({ id: video.id })
285 const videoDetails: VideoDetails = res2.body
286 271
287 expect(videoDetails.files).to.have.lengthOf(4) 272 expect(videoDetails.files).to.have.lengthOf(4)
288 273
289 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-240.mp4')) 274 const file = videoDetails.files.find(f => f.resolution.id === 240)
275 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
290 const probe = await getAudioStream(path) 276 const probe = await getAudioStream(path)
291 277
292 if (probe.audioStream) { 278 if (probe.audioStream) {
@@ -301,23 +287,23 @@ describe('Test video transcoding', function () {
301 it('Should transcode video with no audio and have no audio itself', async function () { 287 it('Should transcode video with no audio and have no audio itself', async function () {
302 this.timeout(60_000) 288 this.timeout(60_000)
303 289
304 const videoAttributes = { 290 const attributes = {
305 name: 'no_audio', 291 name: 'no_audio',
306 fixture: 'video_short_no_audio.mp4' 292 fixture: 'video_short_no_audio.mp4'
307 } 293 }
308 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 294 await servers[1].videos.upload({ attributes })
309 295
310 await waitJobs(servers) 296 await waitJobs(servers)
311 297
312 for (const server of servers) { 298 for (const server of servers) {
313 const res = await getVideosList(server.url) 299 const { data } = await server.videos.list()
314 300
315 const video = res.body.data.find(v => v.name === videoAttributes.name) 301 const video = data.find(v => v.name === attributes.name)
316 const res2 = await getVideo(server.url, video.id) 302 const videoDetails = await server.videos.get({ id: video.id })
317 const videoDetails: VideoDetails = res2.body 303
304 const file = videoDetails.files.find(f => f.resolution.id === 240)
305 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
318 306
319 expect(videoDetails.files).to.have.lengthOf(4)
320 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-240.mp4'))
321 const probe = await getAudioStream(path) 307 const probe = await getAudioStream(path)
322 expect(probe).to.not.have.property('audioStream') 308 expect(probe).to.not.have.property('audioStream')
323 } 309 }
@@ -326,26 +312,27 @@ describe('Test video transcoding', function () {
326 it('Should leave the audio untouched, but properly transcode the video', async function () { 312 it('Should leave the audio untouched, but properly transcode the video', async function () {
327 this.timeout(60_000) 313 this.timeout(60_000)
328 314
329 const videoAttributes = { 315 const attributes = {
330 name: 'untouched_audio', 316 name: 'untouched_audio',
331 fixture: 'video_short.mp4' 317 fixture: 'video_short.mp4'
332 } 318 }
333 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 319 await servers[1].videos.upload({ attributes })
334 320
335 await waitJobs(servers) 321 await waitJobs(servers)
336 322
337 for (const server of servers) { 323 for (const server of servers) {
338 const res = await getVideosList(server.url) 324 const { data } = await server.videos.list()
339 325
340 const video = res.body.data.find(v => v.name === videoAttributes.name) 326 const video = data.find(v => v.name === attributes.name)
341 const res2 = await getVideo(server.url, video.id) 327 const videoDetails = await server.videos.get({ id: video.id })
342 const videoDetails: VideoDetails = res2.body
343 328
344 expect(videoDetails.files).to.have.lengthOf(4) 329 expect(videoDetails.files).to.have.lengthOf(4)
345 330
346 const fixturePath = buildAbsoluteFixturePath(videoAttributes.fixture) 331 const fixturePath = buildAbsoluteFixturePath(attributes.fixture)
347 const fixtureVideoProbe = await getAudioStream(fixturePath) 332 const fixtureVideoProbe = await getAudioStream(fixturePath)
348 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-240.mp4')) 333
334 const file = videoDetails.files.find(f => f.resolution.id === 240)
335 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
349 336
350 const videoProbe = await getAudioStream(path) 337 const videoProbe = await getAudioStream(path)
351 338
@@ -364,19 +351,21 @@ describe('Test video transcoding', function () {
364 function runSuite (mode: 'legacy' | 'resumable') { 351 function runSuite (mode: 'legacy' | 'resumable') {
365 352
366 before(async function () { 353 before(async function () {
367 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, { 354 await servers[1].config.updateCustomSubConfig({
368 transcoding: { 355 newConfig: {
369 hls: { enabled: true }, 356 transcoding: {
370 webtorrent: { enabled: true }, 357 hls: { enabled: true },
371 resolutions: { 358 webtorrent: { enabled: true },
372 '0p': false, 359 resolutions: {
373 '240p': false, 360 '0p': false,
374 '360p': false, 361 '240p': false,
375 '480p': false, 362 '360p': false,
376 '720p': false, 363 '480p': false,
377 '1080p': false, 364 '720p': false,
378 '1440p': false, 365 '1080p': false,
379 '2160p': false 366 '1440p': false,
367 '2160p': false
368 }
380 } 369 }
381 } 370 }
382 }) 371 })
@@ -385,22 +374,21 @@ describe('Test video transcoding', function () {
385 it('Should merge an audio file with the preview file', async function () { 374 it('Should merge an audio file with the preview file', async function () {
386 this.timeout(60_000) 375 this.timeout(60_000)
387 376
388 const videoAttributesArg = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' } 377 const attributes = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' }
389 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributesArg, HttpStatusCode.OK_200, mode) 378 await servers[1].videos.upload({ attributes, mode })
390 379
391 await waitJobs(servers) 380 await waitJobs(servers)
392 381
393 for (const server of servers) { 382 for (const server of servers) {
394 const res = await getVideosList(server.url) 383 const { data } = await server.videos.list()
395 384
396 const video = res.body.data.find(v => v.name === 'audio_with_preview') 385 const video = data.find(v => v.name === 'audio_with_preview')
397 const res2 = await getVideo(server.url, video.id) 386 const videoDetails = await server.videos.get({ id: video.id })
398 const videoDetails: VideoDetails = res2.body
399 387
400 expect(videoDetails.files).to.have.lengthOf(1) 388 expect(videoDetails.files).to.have.lengthOf(1)
401 389
402 await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, statusCodeExpected: HttpStatusCode.OK_200 }) 390 await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
403 await makeGetRequest({ url: server.url, path: videoDetails.previewPath, statusCodeExpected: HttpStatusCode.OK_200 }) 391 await makeGetRequest({ url: server.url, path: videoDetails.previewPath, expectedStatus: HttpStatusCode.OK_200 })
404 392
405 const magnetUri = videoDetails.files[0].magnetUri 393 const magnetUri = videoDetails.files[0].magnetUri
406 expect(magnetUri).to.contain('.mp4') 394 expect(magnetUri).to.contain('.mp4')
@@ -410,22 +398,21 @@ describe('Test video transcoding', function () {
410 it('Should upload an audio file and choose a default background image', async function () { 398 it('Should upload an audio file and choose a default background image', async function () {
411 this.timeout(60_000) 399 this.timeout(60_000)
412 400
413 const videoAttributesArg = { name: 'audio_without_preview', fixture: 'sample.ogg' } 401 const attributes = { name: 'audio_without_preview', fixture: 'sample.ogg' }
414 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributesArg, HttpStatusCode.OK_200, mode) 402 await servers[1].videos.upload({ attributes, mode })
415 403
416 await waitJobs(servers) 404 await waitJobs(servers)
417 405
418 for (const server of servers) { 406 for (const server of servers) {
419 const res = await getVideosList(server.url) 407 const { data } = await server.videos.list()
420 408
421 const video = res.body.data.find(v => v.name === 'audio_without_preview') 409 const video = data.find(v => v.name === 'audio_without_preview')
422 const res2 = await getVideo(server.url, video.id) 410 const videoDetails = await server.videos.get({ id: video.id })
423 const videoDetails = res2.body
424 411
425 expect(videoDetails.files).to.have.lengthOf(1) 412 expect(videoDetails.files).to.have.lengthOf(1)
426 413
427 await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, statusCodeExpected: HttpStatusCode.OK_200 }) 414 await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
428 await makeGetRequest({ url: server.url, path: videoDetails.previewPath, statusCodeExpected: HttpStatusCode.OK_200 }) 415 await makeGetRequest({ url: server.url, path: videoDetails.previewPath, expectedStatus: HttpStatusCode.OK_200 })
429 416
430 const magnetUri = videoDetails.files[0].magnetUri 417 const magnetUri = videoDetails.files[0].magnetUri
431 expect(magnetUri).to.contain('.mp4') 418 expect(magnetUri).to.contain('.mp4')
@@ -435,26 +422,27 @@ describe('Test video transcoding', function () {
435 it('Should upload an audio file and create an audio version only', async function () { 422 it('Should upload an audio file and create an audio version only', async function () {
436 this.timeout(60_000) 423 this.timeout(60_000)
437 424
438 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, { 425 await servers[1].config.updateCustomSubConfig({
439 transcoding: { 426 newConfig: {
440 hls: { enabled: true }, 427 transcoding: {
441 webtorrent: { enabled: true }, 428 hls: { enabled: true },
442 resolutions: { 429 webtorrent: { enabled: true },
443 '0p': true, 430 resolutions: {
444 '240p': false, 431 '0p': true,
445 '360p': false 432 '240p': false,
433 '360p': false
434 }
446 } 435 }
447 } 436 }
448 }) 437 })
449 438
450 const videoAttributesArg = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' } 439 const attributes = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' }
451 const resVideo = await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributesArg, HttpStatusCode.OK_200, mode) 440 const { id } = await servers[1].videos.upload({ attributes, mode })
452 441
453 await waitJobs(servers) 442 await waitJobs(servers)
454 443
455 for (const server of servers) { 444 for (const server of servers) {
456 const res2 = await getVideo(server.url, resVideo.body.video.id) 445 const videoDetails = await server.videos.get({ id })
457 const videoDetails: VideoDetails = res2.body
458 446
459 for (const files of [ videoDetails.files, videoDetails.streamingPlaylists[0].files ]) { 447 for (const files of [ videoDetails.files, videoDetails.streamingPlaylists[0].files ]) {
460 expect(files).to.have.lengthOf(2) 448 expect(files).to.have.lengthOf(2)
@@ -480,21 +468,20 @@ describe('Test video transcoding', function () {
480 it('Should transcode a 60 FPS video', async function () { 468 it('Should transcode a 60 FPS video', async function () {
481 this.timeout(60_000) 469 this.timeout(60_000)
482 470
483 const videoAttributes = { 471 const attributes = {
484 name: 'my super 30fps name for server 2', 472 name: 'my super 30fps name for server 2',
485 description: 'my super 30fps description for server 2', 473 description: 'my super 30fps description for server 2',
486 fixture: '60fps_720p_small.mp4' 474 fixture: '60fps_720p_small.mp4'
487 } 475 }
488 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 476 await servers[1].videos.upload({ attributes })
489 477
490 await waitJobs(servers) 478 await waitJobs(servers)
491 479
492 for (const server of servers) { 480 for (const server of servers) {
493 const res = await getVideosList(server.url) 481 const { data } = await server.videos.list()
494 482
495 const video = res.body.data.find(v => v.name === videoAttributes.name) 483 const video = data.find(v => v.name === attributes.name)
496 const res2 = await getVideo(server.url, video.id) 484 const videoDetails = await server.videos.get({ id: video.id })
497 const videoDetails: VideoDetails = res2.body
498 485
499 expect(videoDetails.files).to.have.lengthOf(4) 486 expect(videoDetails.files).to.have.lengthOf(4)
500 expect(videoDetails.files[0].fps).to.be.above(58).and.below(62) 487 expect(videoDetails.files[0].fps).to.be.above(58).and.below(62)
@@ -502,14 +489,16 @@ describe('Test video transcoding', function () {
502 expect(videoDetails.files[2].fps).to.be.below(31) 489 expect(videoDetails.files[2].fps).to.be.below(31)
503 expect(videoDetails.files[3].fps).to.be.below(31) 490 expect(videoDetails.files[3].fps).to.be.below(31)
504 491
505 for (const resolution of [ '240', '360', '480' ]) { 492 for (const resolution of [ 240, 360, 480 ]) {
506 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-' + resolution + '.mp4')) 493 const file = videoDetails.files.find(f => f.resolution.id === resolution)
494 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
507 const fps = await getVideoFileFPS(path) 495 const fps = await getVideoFileFPS(path)
508 496
509 expect(fps).to.be.below(31) 497 expect(fps).to.be.below(31)
510 } 498 }
511 499
512 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-720.mp4')) 500 const file = videoDetails.files.find(f => f.resolution.id === 720)
501 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
513 const fps = await getVideoFileFPS(path) 502 const fps = await getVideoFileFPS(path)
514 503
515 expect(fps).to.be.above(58).and.below(62) 504 expect(fps).to.be.above(58).and.below(62)
@@ -528,29 +517,32 @@ describe('Test video transcoding', function () {
528 expect(fps).to.be.equal(59) 517 expect(fps).to.be.equal(59)
529 } 518 }
530 519
531 const videoAttributes = { 520 const attributes = {
532 name: '59fps video', 521 name: '59fps video',
533 description: '59fps video', 522 description: '59fps video',
534 fixture: tempFixturePath 523 fixture: tempFixturePath
535 } 524 }
536 525
537 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 526 await servers[1].videos.upload({ attributes })
538 527
539 await waitJobs(servers) 528 await waitJobs(servers)
540 529
541 for (const server of servers) { 530 for (const server of servers) {
542 const res = await getVideosList(server.url) 531 const { data } = await server.videos.list()
543 532
544 const video = res.body.data.find(v => v.name === videoAttributes.name) 533 const { id } = data.find(v => v.name === attributes.name)
534 const video = await server.videos.get({ id })
545 535
546 { 536 {
547 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-240.mp4')) 537 const file = video.files.find(f => f.resolution.id === 240)
538 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
548 const fps = await getVideoFileFPS(path) 539 const fps = await getVideoFileFPS(path)
549 expect(fps).to.be.equal(25) 540 expect(fps).to.be.equal(25)
550 } 541 }
551 542
552 { 543 {
553 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-720.mp4')) 544 const file = video.files.find(f => f.resolution.id === 720)
545 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
554 const fps = await getVideoFileFPS(path) 546 const fps = await getVideoFileFPS(path)
555 expect(fps).to.be.equal(59) 547 expect(fps).to.be.equal(59)
556 } 548 }
@@ -559,6 +551,7 @@ describe('Test video transcoding', function () {
559 }) 551 })
560 552
561 describe('Bitrate control', function () { 553 describe('Bitrate control', function () {
554
562 it('Should respect maximum bitrate values', async function () { 555 it('Should respect maximum bitrate values', async function () {
563 this.timeout(160_000) 556 this.timeout(160_000)
564 557
@@ -571,30 +564,32 @@ describe('Test video transcoding', function () {
571 expect(bitrate).to.be.above(getMaxBitrate(VideoResolution.H_1080P, 25, VIDEO_TRANSCODING_FPS)) 564 expect(bitrate).to.be.above(getMaxBitrate(VideoResolution.H_1080P, 25, VIDEO_TRANSCODING_FPS))
572 } 565 }
573 566
574 const videoAttributes = { 567 const attributes = {
575 name: 'high bitrate video', 568 name: 'high bitrate video',
576 description: 'high bitrate video', 569 description: 'high bitrate video',
577 fixture: tempFixturePath 570 fixture: tempFixturePath
578 } 571 }
579 572
580 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 573 await servers[1].videos.upload({ attributes })
581 574
582 await waitJobs(servers) 575 await waitJobs(servers)
583 576
584 for (const server of servers) { 577 for (const server of servers) {
585 const res = await getVideosList(server.url) 578 const { data } = await server.videos.list()
586 579
587 const video = res.body.data.find(v => v.name === videoAttributes.name) 580 const { id } = data.find(v => v.name === attributes.name)
581 const video = await server.videos.get({ id })
588 582
589 for (const resolution of [ '240', '360', '480', '720', '1080' ]) { 583 for (const resolution of [ 240, 360, 480, 720, 1080 ]) {
590 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-' + resolution + '.mp4')) 584 const file = video.files.find(f => f.resolution.id === resolution)
585 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
591 586
592 const bitrate = await getVideoFileBitrate(path) 587 const bitrate = await getVideoFileBitrate(path)
593 const fps = await getVideoFileFPS(path) 588 const fps = await getVideoFileFPS(path)
594 const resolution2 = await getVideoFileResolution(path) 589 const { videoFileResolution } = await getVideoFileResolution(path)
595 590
596 expect(resolution2.videoFileResolution.toString()).to.equal(resolution) 591 expect(videoFileResolution).to.equal(resolution)
597 expect(bitrate).to.be.below(getMaxBitrate(resolution2.videoFileResolution, fps, VIDEO_TRANSCODING_FPS)) 592 expect(bitrate).to.be.below(getMaxBitrate(videoFileResolution, fps, VIDEO_TRANSCODING_FPS))
598 } 593 }
599 } 594 }
600 }) 595 })
@@ -602,7 +597,7 @@ describe('Test video transcoding', function () {
602 it('Should not transcode to an higher bitrate than the original file', async function () { 597 it('Should not transcode to an higher bitrate than the original file', async function () {
603 this.timeout(160_000) 598 this.timeout(160_000)
604 599
605 const config = { 600 const newConfig = {
606 transcoding: { 601 transcoding: {
607 enabled: true, 602 enabled: true,
608 resolutions: { 603 resolutions: {
@@ -618,22 +613,25 @@ describe('Test video transcoding', function () {
618 hls: { enabled: true } 613 hls: { enabled: true }
619 } 614 }
620 } 615 }
621 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, config) 616 await servers[1].config.updateCustomSubConfig({ newConfig })
622 617
623 const videoAttributes = { 618 const attributes = {
624 name: 'low bitrate', 619 name: 'low bitrate',
625 fixture: 'low-bitrate.mp4' 620 fixture: 'low-bitrate.mp4'
626 } 621 }
627 622
628 const resUpload = await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 623 const { id } = await servers[1].videos.upload({ attributes })
629 const videoUUID = resUpload.body.video.uuid
630 624
631 await waitJobs(servers) 625 await waitJobs(servers)
632 626
627 const video = await servers[1].videos.get({ id })
628
633 const resolutions = [ 240, 360, 480, 720, 1080 ] 629 const resolutions = [ 240, 360, 480, 720, 1080 ]
634 for (const r of resolutions) { 630 for (const r of resolutions) {
635 const path = `videos/${videoUUID}-${r}.mp4` 631 const file = video.files.find(f => f.resolution.id === r)
636 const size = await getServerFileSize(servers[1], path) 632
633 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
634 const size = await getFileSize(path)
637 expect(size, `${path} not below ${60_000}`).to.be.below(60_000) 635 expect(size, `${path} not below ${60_000}`).to.be.below(60_000)
638 } 636 }
639 }) 637 })
@@ -644,11 +642,13 @@ describe('Test video transcoding', function () {
644 it('Should provide valid ffprobe data', async function () { 642 it('Should provide valid ffprobe data', async function () {
645 this.timeout(160_000) 643 this.timeout(160_000)
646 644
647 const videoUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'ffprobe data' })).uuid 645 const videoUUID = (await servers[1].videos.quickUpload({ name: 'ffprobe data' })).uuid
648 await waitJobs(servers) 646 await waitJobs(servers)
649 647
650 { 648 {
651 const path = buildServerDirectory(servers[1], join('videos', videoUUID + '-240.mp4')) 649 const video = await servers[1].videos.get({ id: videoUUID })
650 const file = video.files.find(f => f.resolution.id === 240)
651 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
652 const metadata = await getMetadataFromFile(path) 652 const metadata = await getMetadataFromFile(path)
653 653
654 // expected format properties 654 // expected format properties
@@ -678,8 +678,7 @@ describe('Test video transcoding', function () {
678 } 678 }
679 679
680 for (const server of servers) { 680 for (const server of servers) {
681 const res2 = await getVideo(server.url, videoUUID) 681 const videoDetails = await server.videos.get({ id: videoUUID })
682 const videoDetails: VideoDetails = res2.body
683 682
684 const videoFiles = videoDetails.files 683 const videoFiles = videoDetails.files
685 .concat(videoDetails.streamingPlaylists[0].files) 684 .concat(videoDetails.streamingPlaylists[0].files)
@@ -691,8 +690,7 @@ describe('Test video transcoding', function () {
691 expect(file.metadataUrl).to.contain(servers[1].url) 690 expect(file.metadataUrl).to.contain(servers[1].url)
692 expect(file.metadataUrl).to.contain(videoUUID) 691 expect(file.metadataUrl).to.contain(videoUUID)
693 692
694 const res3 = await getVideoFileMetadataUrl(file.metadataUrl) 693 const metadata = await server.videos.getFileMetadata({ url: file.metadataUrl })
695 const metadata: FfprobeData = res3.body
696 expect(metadata).to.have.nested.property('format.size') 694 expect(metadata).to.have.nested.property('format.size')
697 } 695 }
698 } 696 }
@@ -709,17 +707,14 @@ describe('Test video transcoding', function () {
709 describe('Transcoding job queue', function () { 707 describe('Transcoding job queue', function () {
710 708
711 it('Should have the appropriate priorities for transcoding jobs', async function () { 709 it('Should have the appropriate priorities for transcoding jobs', async function () {
712 const res = await getJobsListPaginationAndSort({ 710 const body = await servers[1].jobs.getJobsList({
713 url: servers[1].url,
714 accessToken: servers[1].accessToken,
715 start: 0, 711 start: 0,
716 count: 100, 712 count: 100,
717 sort: '-createdAt', 713 sort: '-createdAt',
718 jobType: 'video-transcoding' 714 jobType: 'video-transcoding'
719 }) 715 })
720 716
721 const jobs = res.body.data as Job[] 717 const jobs = body.data
722
723 const transcodingJobs = jobs.filter(j => j.data.videoUUID === video4k) 718 const transcodingJobs = jobs.filter(j => j.data.videoUUID === video4k)
724 719
725 expect(transcodingJobs).to.have.lengthOf(14) 720 expect(transcodingJobs).to.have.lengthOf(14)
diff --git a/server/tests/api/videos/videos-filter.ts b/server/tests/api/videos/videos-filter.ts
index 7428b82c5..2306807bf 100644
--- a/server/tests/api/videos/videos-filter.ts
+++ b/server/tests/api/videos/videos-filter.ts
@@ -1,25 +1,18 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import { expect } from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 createUser, 7 createMultipleServers,
8 doubleFollow, 8 doubleFollow,
9 flushAndRunMultipleServers,
10 makeGetRequest, 9 makeGetRequest,
11 ServerInfo, 10 PeerTubeServer,
12 setAccessTokensToServers, 11 setAccessTokensToServers
13 uploadVideo, 12} from '@shared/extra-utils'
14 userLogin 13import { HttpStatusCode, UserRole, Video, VideoPrivacy } from '@shared/models'
15} from '../../../../shared/extra-utils' 14
16import { Video, VideoPrivacy } from '../../../../shared/models/videos' 15async function getVideosNames (server: PeerTubeServer, token: string, filter: string, expectedStatus = HttpStatusCode.OK_200) {
17import { UserRole } from '../../../../shared/models/users'
18import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
19
20const expect = chai.expect
21
22async function getVideosNames (server: ServerInfo, token: string, filter: string, statusCodeExpected = HttpStatusCode.OK_200) {
23 const paths = [ 16 const paths = [
24 '/api/v1/video-channels/root_channel/videos', 17 '/api/v1/video-channels/root_channel/videos',
25 '/api/v1/accounts/root/videos', 18 '/api/v1/accounts/root/videos',
@@ -38,7 +31,7 @@ async function getVideosNames (server: ServerInfo, token: string, filter: string
38 sort: 'createdAt', 31 sort: 'createdAt',
39 filter 32 filter
40 }, 33 },
41 statusCodeExpected 34 expectedStatus
42 }) 35 })
43 36
44 videosResults.push(res.body.data.map(v => v.name)) 37 videosResults.push(res.body.data.map(v => v.name))
@@ -48,42 +41,32 @@ async function getVideosNames (server: ServerInfo, token: string, filter: string
48} 41}
49 42
50describe('Test videos filter', function () { 43describe('Test videos filter', function () {
51 let servers: ServerInfo[] 44 let servers: PeerTubeServer[]
52 45
53 // --------------------------------------------------------------- 46 // ---------------------------------------------------------------
54 47
55 before(async function () { 48 before(async function () {
56 this.timeout(160000) 49 this.timeout(160000)
57 50
58 servers = await flushAndRunMultipleServers(2) 51 servers = await createMultipleServers(2)
59 52
60 await setAccessTokensToServers(servers) 53 await setAccessTokensToServers(servers)
61 54
62 for (const server of servers) { 55 for (const server of servers) {
63 const moderator = { username: 'moderator', password: 'my super password' } 56 const moderator = { username: 'moderator', password: 'my super password' }
64 await createUser( 57 await server.users.create({ username: moderator.username, password: moderator.password, role: UserRole.MODERATOR })
65 { 58 server['moderatorAccessToken'] = await server.login.getAccessToken(moderator)
66 url: server.url,
67 accessToken: server.accessToken,
68 username: moderator.username,
69 password: moderator.password,
70 videoQuota: undefined,
71 videoQuotaDaily: undefined,
72 role: UserRole.MODERATOR
73 }
74 )
75 server['moderatorAccessToken'] = await userLogin(server, moderator)
76 59
77 await uploadVideo(server.url, server.accessToken, { name: 'public ' + server.serverNumber }) 60 await server.videos.upload({ attributes: { name: 'public ' + server.serverNumber } })
78 61
79 { 62 {
80 const attributes = { name: 'unlisted ' + server.serverNumber, privacy: VideoPrivacy.UNLISTED } 63 const attributes = { name: 'unlisted ' + server.serverNumber, privacy: VideoPrivacy.UNLISTED }
81 await uploadVideo(server.url, server.accessToken, attributes) 64 await server.videos.upload({ attributes })
82 } 65 }
83 66
84 { 67 {
85 const attributes = { name: 'private ' + server.serverNumber, privacy: VideoPrivacy.PRIVATE } 68 const attributes = { name: 'private ' + server.serverNumber, privacy: VideoPrivacy.PRIVATE }
86 await uploadVideo(server.url, server.accessToken, attributes) 69 await server.videos.upload({ attributes })
87 } 70 }
88 } 71 }
89 72
diff --git a/server/tests/api/videos/videos-history.ts b/server/tests/api/videos/videos-history.ts
index b25cff879..e4bc0bb3a 100644
--- a/server/tests/api/videos/videos-history.ts
+++ b/server/tests/api/videos/videos-history.ts
@@ -1,74 +1,66 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 createUser, 7 createSingleServer,
8 flushAndRunServer, 8 HistoryCommand,
9 getVideosListWithToken,
10 getVideoWithToken,
11 killallServers, 9 killallServers,
12 reRunServer, 10 PeerTubeServer,
13 searchVideoWithToken,
14 ServerInfo,
15 setAccessTokensToServers, 11 setAccessTokensToServers,
16 updateMyUser,
17 uploadVideo,
18 userLogin,
19 wait 12 wait
20} from '../../../../shared/extra-utils' 13} from '@shared/extra-utils'
21import { Video, VideoDetails } from '../../../../shared/models/videos' 14import { HttpStatusCode, Video } from '@shared/models'
22import { listMyVideosHistory, removeMyVideosHistory, userWatchVideo } from '../../../../shared/extra-utils/videos/video-history'
23import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
24 15
25const expect = chai.expect 16const expect = chai.expect
26 17
27describe('Test videos history', function () { 18describe('Test videos history', function () {
28 let server: ServerInfo = null 19 let server: PeerTubeServer = null
29 let video1UUID: string 20 let video1UUID: string
30 let video2UUID: string 21 let video2UUID: string
31 let video3UUID: string 22 let video3UUID: string
32 let video3WatchedDate: Date 23 let video3WatchedDate: Date
33 let userAccessToken: string 24 let userAccessToken: string
25 let command: HistoryCommand
34 26
35 before(async function () { 27 before(async function () {
36 this.timeout(30000) 28 this.timeout(30000)
37 29
38 server = await flushAndRunServer(1) 30 server = await createSingleServer(1)
39 31
40 await setAccessTokensToServers([ server ]) 32 await setAccessTokensToServers([ server ])
41 33
34 command = server.history
35
42 { 36 {
43 const res = await uploadVideo(server.url, server.accessToken, { name: 'video 1' }) 37 const { uuid } = await server.videos.upload({ attributes: { name: 'video 1' } })
44 video1UUID = res.body.video.uuid 38 video1UUID = uuid
45 } 39 }
46 40
47 { 41 {
48 const res = await uploadVideo(server.url, server.accessToken, { name: 'video 2' }) 42 const { uuid } = await server.videos.upload({ attributes: { name: 'video 2' } })
49 video2UUID = res.body.video.uuid 43 video2UUID = uuid
50 } 44 }
51 45
52 { 46 {
53 const res = await uploadVideo(server.url, server.accessToken, { name: 'video 3' }) 47 const { uuid } = await server.videos.upload({ attributes: { name: 'video 3' } })
54 video3UUID = res.body.video.uuid 48 video3UUID = uuid
55 } 49 }
56 50
57 const user = { 51 const user = {
58 username: 'user_1', 52 username: 'user_1',
59 password: 'super password' 53 password: 'super password'
60 } 54 }
61 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 55 await server.users.create({ username: user.username, password: user.password })
62 userAccessToken = await userLogin(server, user) 56 userAccessToken = await server.login.getAccessToken(user)
63 }) 57 })
64 58
65 it('Should get videos, without watching history', async function () { 59 it('Should get videos, without watching history', async function () {
66 const res = await getVideosListWithToken(server.url, server.accessToken) 60 const { data } = await server.videos.listWithToken()
67 const videos: Video[] = res.body.data
68 61
69 for (const video of videos) { 62 for (const video of data) {
70 const resDetail = await getVideoWithToken(server.url, server.accessToken, video.id) 63 const videoDetails = await server.videos.getWithToken({ id: video.id })
71 const videoDetails: VideoDetails = resDetail.body
72 64
73 expect(video.userHistory).to.be.undefined 65 expect(video.userHistory).to.be.undefined
74 expect(videoDetails.userHistory).to.be.undefined 66 expect(videoDetails.userHistory).to.be.undefined
@@ -76,21 +68,21 @@ describe('Test videos history', function () {
76 }) 68 })
77 69
78 it('Should watch the first and second video', async function () { 70 it('Should watch the first and second video', async function () {
79 await userWatchVideo(server.url, server.accessToken, video2UUID, 8) 71 await command.wathVideo({ videoId: video2UUID, currentTime: 8 })
80 await userWatchVideo(server.url, server.accessToken, video1UUID, 3) 72 await command.wathVideo({ videoId: video1UUID, currentTime: 3 })
81 }) 73 })
82 74
83 it('Should return the correct history when listing, searching and getting videos', async function () { 75 it('Should return the correct history when listing, searching and getting videos', async function () {
84 const videosOfVideos: Video[][] = [] 76 const videosOfVideos: Video[][] = []
85 77
86 { 78 {
87 const res = await getVideosListWithToken(server.url, server.accessToken) 79 const { data } = await server.videos.listWithToken()
88 videosOfVideos.push(res.body.data) 80 videosOfVideos.push(data)
89 } 81 }
90 82
91 { 83 {
92 const res = await searchVideoWithToken(server.url, 'video', server.accessToken) 84 const body = await server.search.searchVideos({ token: server.accessToken, search: 'video' })
93 videosOfVideos.push(res.body.data) 85 videosOfVideos.push(body.data)
94 } 86 }
95 87
96 for (const videos of videosOfVideos) { 88 for (const videos of videosOfVideos) {
@@ -108,24 +100,21 @@ describe('Test videos history', function () {
108 } 100 }
109 101
110 { 102 {
111 const resDetail = await getVideoWithToken(server.url, server.accessToken, video1UUID) 103 const videoDetails = await server.videos.getWithToken({ id: video1UUID })
112 const videoDetails: VideoDetails = resDetail.body
113 104
114 expect(videoDetails.userHistory).to.not.be.undefined 105 expect(videoDetails.userHistory).to.not.be.undefined
115 expect(videoDetails.userHistory.currentTime).to.equal(3) 106 expect(videoDetails.userHistory.currentTime).to.equal(3)
116 } 107 }
117 108
118 { 109 {
119 const resDetail = await getVideoWithToken(server.url, server.accessToken, video2UUID) 110 const videoDetails = await server.videos.getWithToken({ id: video2UUID })
120 const videoDetails: VideoDetails = resDetail.body
121 111
122 expect(videoDetails.userHistory).to.not.be.undefined 112 expect(videoDetails.userHistory).to.not.be.undefined
123 expect(videoDetails.userHistory.currentTime).to.equal(8) 113 expect(videoDetails.userHistory.currentTime).to.equal(8)
124 } 114 }
125 115
126 { 116 {
127 const resDetail = await getVideoWithToken(server.url, server.accessToken, video3UUID) 117 const videoDetails = await server.videos.getWithToken({ id: video3UUID })
128 const videoDetails: VideoDetails = resDetail.body
129 118
130 expect(videoDetails.userHistory).to.be.undefined 119 expect(videoDetails.userHistory).to.be.undefined
131 } 120 }
@@ -133,71 +122,64 @@ describe('Test videos history', function () {
133 122
134 it('Should have these videos when listing my history', async function () { 123 it('Should have these videos when listing my history', async function () {
135 video3WatchedDate = new Date() 124 video3WatchedDate = new Date()
136 await userWatchVideo(server.url, server.accessToken, video3UUID, 2) 125 await command.wathVideo({ videoId: video3UUID, currentTime: 2 })
137 126
138 const res = await listMyVideosHistory(server.url, server.accessToken) 127 const body = await command.list()
139 128
140 expect(res.body.total).to.equal(3) 129 expect(body.total).to.equal(3)
141 130
142 const videos: Video[] = res.body.data 131 const videos = body.data
143 expect(videos[0].name).to.equal('video 3') 132 expect(videos[0].name).to.equal('video 3')
144 expect(videos[1].name).to.equal('video 1') 133 expect(videos[1].name).to.equal('video 1')
145 expect(videos[2].name).to.equal('video 2') 134 expect(videos[2].name).to.equal('video 2')
146 }) 135 })
147 136
148 it('Should not have videos history on another user', async function () { 137 it('Should not have videos history on another user', async function () {
149 const res = await listMyVideosHistory(server.url, userAccessToken) 138 const body = await command.list({ token: userAccessToken })
150 139
151 expect(res.body.total).to.equal(0) 140 expect(body.total).to.equal(0)
152 expect(res.body.data).to.have.lengthOf(0) 141 expect(body.data).to.have.lengthOf(0)
153 }) 142 })
154 143
155 it('Should be able to search through videos in my history', async function () { 144 it('Should be able to search through videos in my history', async function () {
156 const res = await listMyVideosHistory(server.url, server.accessToken, '2') 145 const body = await command.list({ search: '2' })
157 146 expect(body.total).to.equal(1)
158 expect(res.body.total).to.equal(1)
159 147
160 const videos: Video[] = res.body.data 148 const videos = body.data
161 expect(videos[0].name).to.equal('video 2') 149 expect(videos[0].name).to.equal('video 2')
162 }) 150 })
163 151
164 it('Should clear my history', async function () { 152 it('Should clear my history', async function () {
165 await removeMyVideosHistory(server.url, server.accessToken, video3WatchedDate.toISOString()) 153 await command.remove({ beforeDate: video3WatchedDate.toISOString() })
166 }) 154 })
167 155
168 it('Should have my history cleared', async function () { 156 it('Should have my history cleared', async function () {
169 const res = await listMyVideosHistory(server.url, server.accessToken) 157 const body = await command.list()
158 expect(body.total).to.equal(1)
170 159
171 expect(res.body.total).to.equal(1) 160 const videos = body.data
172
173 const videos: Video[] = res.body.data
174 expect(videos[0].name).to.equal('video 3') 161 expect(videos[0].name).to.equal('video 3')
175 }) 162 })
176 163
177 it('Should disable videos history', async function () { 164 it('Should disable videos history', async function () {
178 await updateMyUser({ 165 await server.users.updateMe({
179 url: server.url,
180 accessToken: server.accessToken,
181 videosHistoryEnabled: false 166 videosHistoryEnabled: false
182 }) 167 })
183 168
184 await userWatchVideo(server.url, server.accessToken, video2UUID, 8, HttpStatusCode.CONFLICT_409) 169 await command.wathVideo({ videoId: video2UUID, currentTime: 8, expectedStatus: HttpStatusCode.CONFLICT_409 })
185 }) 170 })
186 171
187 it('Should re-enable videos history', async function () { 172 it('Should re-enable videos history', async function () {
188 await updateMyUser({ 173 await server.users.updateMe({
189 url: server.url,
190 accessToken: server.accessToken,
191 videosHistoryEnabled: true 174 videosHistoryEnabled: true
192 }) 175 })
193 176
194 await userWatchVideo(server.url, server.accessToken, video1UUID, 8) 177 await command.wathVideo({ videoId: video1UUID, currentTime: 8 })
195
196 const res = await listMyVideosHistory(server.url, server.accessToken)
197 178
198 expect(res.body.total).to.equal(2) 179 const body = await command.list()
180 expect(body.total).to.equal(2)
199 181
200 const videos: Video[] = res.body.data 182 const videos = body.data
201 expect(videos[0].name).to.equal('video 1') 183 expect(videos[0].name).to.equal('video 1')
202 expect(videos[1].name).to.equal('video 3') 184 expect(videos[1].name).to.equal('video 3')
203 }) 185 })
@@ -205,30 +187,29 @@ describe('Test videos history', function () {
205 it('Should not clean old history', async function () { 187 it('Should not clean old history', async function () {
206 this.timeout(50000) 188 this.timeout(50000)
207 189
208 killallServers([ server ]) 190 await killallServers([ server ])
209 191
210 await reRunServer(server, { history: { videos: { max_age: '10 days' } } }) 192 await server.run({ history: { videos: { max_age: '10 days' } } })
211 193
212 await wait(6000) 194 await wait(6000)
213 195
214 // Should still have history 196 // Should still have history
215 197
216 const res = await listMyVideosHistory(server.url, server.accessToken) 198 const body = await command.list()
217 199 expect(body.total).to.equal(2)
218 expect(res.body.total).to.equal(2)
219 }) 200 })
220 201
221 it('Should clean old history', async function () { 202 it('Should clean old history', async function () {
222 this.timeout(50000) 203 this.timeout(50000)
223 204
224 killallServers([ server ]) 205 await killallServers([ server ])
225 206
226 await reRunServer(server, { history: { videos: { max_age: '5 seconds' } } }) 207 await server.run({ history: { videos: { max_age: '5 seconds' } } })
227 208
228 await wait(6000) 209 await wait(6000)
229 210
230 const res = await listMyVideosHistory(server.url, server.accessToken) 211 const body = await command.list()
231 expect(res.body.total).to.equal(0) 212 expect(body.total).to.equal(0)
232 }) 213 })
233 214
234 after(async function () { 215 after(async function () {
diff --git a/server/tests/api/videos/videos-overview.ts b/server/tests/api/videos/videos-overview.ts
index c266a1dc5..70aa66549 100644
--- a/server/tests/api/videos/videos-overview.ts
+++ b/server/tests/api/videos/videos-overview.ts
@@ -2,29 +2,15 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5 5import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers, wait } from '@shared/extra-utils'
6import { 6import { VideosOverview } from '@shared/models'
7 cleanupTests,
8 flushAndRunServer,
9 generateUserAccessToken,
10 ServerInfo,
11 setAccessTokensToServers,
12 uploadVideo,
13 wait
14} from '../../../../shared/extra-utils'
15import { getVideosOverview, getVideosOverviewWithToken } from '../../../../shared/extra-utils/overviews/overviews'
16import { VideosOverview } from '../../../../shared/models/overviews'
17import { addAccountToAccountBlocklist } from '@shared/extra-utils/users/blocklist'
18import { Response } from 'superagent'
19 7
20const expect = chai.expect 8const expect = chai.expect
21 9
22describe('Test a videos overview', function () { 10describe('Test a videos overview', function () {
23 let server: ServerInfo = null 11 let server: PeerTubeServer = null
24
25 function testOverviewCount (res: Response, expected: number) {
26 const overview: VideosOverview = res.body
27 12
13 function testOverviewCount (overview: VideosOverview, expected: number) {
28 expect(overview.tags).to.have.lengthOf(expected) 14 expect(overview.tags).to.have.lengthOf(expected)
29 expect(overview.categories).to.have.lengthOf(expected) 15 expect(overview.categories).to.have.lengthOf(expected)
30 expect(overview.channels).to.have.lengthOf(expected) 16 expect(overview.channels).to.have.lengthOf(expected)
@@ -33,15 +19,15 @@ describe('Test a videos overview', function () {
33 before(async function () { 19 before(async function () {
34 this.timeout(30000) 20 this.timeout(30000)
35 21
36 server = await flushAndRunServer(1) 22 server = await createSingleServer(1)
37 23
38 await setAccessTokensToServers([ server ]) 24 await setAccessTokensToServers([ server ])
39 }) 25 })
40 26
41 it('Should send empty overview', async function () { 27 it('Should send empty overview', async function () {
42 const res = await getVideosOverview(server.url, 1) 28 const body = await server.overviews.getVideos({ page: 1 })
43 29
44 testOverviewCount(res, 0) 30 testOverviewCount(body, 0)
45 }) 31 })
46 32
47 it('Should upload 5 videos in a specific category, tag and channel but not include them in overview', async function () { 33 it('Should upload 5 videos in a specific category, tag and channel but not include them in overview', async function () {
@@ -49,40 +35,45 @@ describe('Test a videos overview', function () {
49 35
50 await wait(3000) 36 await wait(3000)
51 37
52 await uploadVideo(server.url, server.accessToken, { 38 await server.videos.upload({
53 name: 'video 0', 39 attributes: {
54 category: 3, 40 name: 'video 0',
55 tags: [ 'coucou1', 'coucou2' ] 41 category: 3,
42 tags: [ 'coucou1', 'coucou2' ]
43 }
56 }) 44 })
57 45
58 const res = await getVideosOverview(server.url, 1) 46 const body = await server.overviews.getVideos({ page: 1 })
59 47
60 testOverviewCount(res, 0) 48 testOverviewCount(body, 0)
61 }) 49 })
62 50
63 it('Should upload another video and include all videos in the overview', async function () { 51 it('Should upload another video and include all videos in the overview', async function () {
64 this.timeout(30000) 52 this.timeout(30000)
65 53
66 for (let i = 1; i < 6; i++) { 54 {
67 await uploadVideo(server.url, server.accessToken, { 55 for (let i = 1; i < 6; i++) {
68 name: 'video ' + i, 56 await server.videos.upload({
69 category: 3, 57 attributes: {
70 tags: [ 'coucou1', 'coucou2' ] 58 name: 'video ' + i,
71 }) 59 category: 3,
60 tags: [ 'coucou1', 'coucou2' ]
61 }
62 })
63 }
64
65 await wait(3000)
72 } 66 }
73 67
74 await wait(3000)
75
76 { 68 {
77 const res = await getVideosOverview(server.url, 1) 69 const body = await server.overviews.getVideos({ page: 1 })
78 70
79 testOverviewCount(res, 1) 71 testOverviewCount(body, 1)
80 } 72 }
81 73
82 { 74 {
83 const res = await getVideosOverview(server.url, 2) 75 const overview = await server.overviews.getVideos({ page: 2 })
84 76
85 const overview: VideosOverview = res.body
86 expect(overview.tags).to.have.lengthOf(1) 77 expect(overview.tags).to.have.lengthOf(1)
87 expect(overview.categories).to.have.lengthOf(0) 78 expect(overview.categories).to.have.lengthOf(0)
88 expect(overview.channels).to.have.lengthOf(0) 79 expect(overview.channels).to.have.lengthOf(0)
@@ -90,20 +81,10 @@ describe('Test a videos overview', function () {
90 }) 81 })
91 82
92 it('Should have the correct overview', async function () { 83 it('Should have the correct overview', async function () {
93 const res1 = await getVideosOverview(server.url, 1) 84 const overview1 = await server.overviews.getVideos({ page: 1 })
94 const res2 = await getVideosOverview(server.url, 2) 85 const overview2 = await server.overviews.getVideos({ page: 2 })
95
96 const overview1: VideosOverview = res1.body
97 const overview2: VideosOverview = res2.body
98
99 const tmp = [
100 overview1.tags,
101 overview1.categories,
102 overview1.channels,
103 overview2.tags
104 ]
105 86
106 for (const arr of tmp) { 87 for (const arr of [ overview1.tags, overview1.categories, overview1.channels, overview2.tags ]) {
107 expect(arr).to.have.lengthOf(1) 88 expect(arr).to.have.lengthOf(1)
108 89
109 const obj = arr[0] 90 const obj = arr[0]
@@ -127,20 +108,20 @@ describe('Test a videos overview', function () {
127 }) 108 })
128 109
129 it('Should hide muted accounts', async function () { 110 it('Should hide muted accounts', async function () {
130 const token = await generateUserAccessToken(server, 'choco') 111 const token = await server.users.generateUserAndToken('choco')
131 112
132 await addAccountToAccountBlocklist(server.url, token, 'root@' + server.host) 113 await server.blocklist.addToMyBlocklist({ token, account: 'root@' + server.host })
133 114
134 { 115 {
135 const res = await getVideosOverview(server.url, 1) 116 const body = await server.overviews.getVideos({ page: 1 })
136 117
137 testOverviewCount(res, 1) 118 testOverviewCount(body, 1)
138 } 119 }
139 120
140 { 121 {
141 const res = await getVideosOverviewWithToken(server.url, 1, token) 122 const body = await server.overviews.getVideos({ page: 1, token })
142 123
143 testOverviewCount(res, 0) 124 testOverviewCount(body, 0)
144 } 125 }
145 }) 126 })
146 127
diff --git a/server/tests/api/videos/videos-views-cleaner.ts b/server/tests/api/videos/videos-views-cleaner.ts
index b89f33217..82268b1be 100644
--- a/server/tests/api/videos/videos-views-cleaner.ts
+++ b/server/tests/api/videos/videos-views-cleaner.ts
@@ -1,19 +1,14 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 closeAllSequelize, 7 createMultipleServers,
8 countVideoViewsOf,
9 doubleFollow, 8 doubleFollow,
10 flushAndRunMultipleServers,
11 killallServers, 9 killallServers,
12 reRunServer, 10 PeerTubeServer,
13 ServerInfo,
14 setAccessTokensToServers, 11 setAccessTokensToServers,
15 uploadVideoAndGetId,
16 viewVideo,
17 wait, 12 wait,
18 waitJobs 13 waitJobs
19} from '../../../../shared/extra-utils' 14} from '../../../../shared/extra-utils'
@@ -21,7 +16,7 @@ import {
21const expect = chai.expect 16const expect = chai.expect
22 17
23describe('Test video views cleaner', function () { 18describe('Test video views cleaner', function () {
24 let servers: ServerInfo[] 19 let servers: PeerTubeServer[]
25 20
26 let videoIdServer1: string 21 let videoIdServer1: string
27 let videoIdServer2: string 22 let videoIdServer2: string
@@ -29,20 +24,20 @@ describe('Test video views cleaner', function () {
29 before(async function () { 24 before(async function () {
30 this.timeout(120000) 25 this.timeout(120000)
31 26
32 servers = await flushAndRunMultipleServers(2) 27 servers = await createMultipleServers(2)
33 await setAccessTokensToServers(servers) 28 await setAccessTokensToServers(servers)
34 29
35 await doubleFollow(servers[0], servers[1]) 30 await doubleFollow(servers[0], servers[1])
36 31
37 videoIdServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video server 1' })).uuid 32 videoIdServer1 = (await servers[0].videos.quickUpload({ name: 'video server 1' })).uuid
38 videoIdServer2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video server 2' })).uuid 33 videoIdServer2 = (await servers[1].videos.quickUpload({ name: 'video server 2' })).uuid
39 34
40 await waitJobs(servers) 35 await waitJobs(servers)
41 36
42 await viewVideo(servers[0].url, videoIdServer1) 37 await servers[0].videos.view({ id: videoIdServer1 })
43 await viewVideo(servers[1].url, videoIdServer1) 38 await servers[1].videos.view({ id: videoIdServer1 })
44 await viewVideo(servers[0].url, videoIdServer2) 39 await servers[0].videos.view({ id: videoIdServer2 })
45 await viewVideo(servers[1].url, videoIdServer2) 40 await servers[1].videos.view({ id: videoIdServer2 })
46 41
47 await waitJobs(servers) 42 await waitJobs(servers)
48 }) 43 })
@@ -50,9 +45,9 @@ describe('Test video views cleaner', function () {
50 it('Should not clean old video views', async function () { 45 it('Should not clean old video views', async function () {
51 this.timeout(50000) 46 this.timeout(50000)
52 47
53 killallServers([ servers[0] ]) 48 await killallServers([ servers[0] ])
54 49
55 await reRunServer(servers[0], { views: { videos: { remote: { max_age: '10 days' } } } }) 50 await servers[0].run({ views: { videos: { remote: { max_age: '10 days' } } } })
56 51
57 await wait(6000) 52 await wait(6000)
58 53
@@ -60,14 +55,14 @@ describe('Test video views cleaner', function () {
60 55
61 { 56 {
62 for (const server of servers) { 57 for (const server of servers) {
63 const total = await countVideoViewsOf(server.internalServerNumber, videoIdServer1) 58 const total = await server.sql.countVideoViewsOf(videoIdServer1)
64 expect(total).to.equal(2, 'Server ' + server.serverNumber + ' does not have the correct amount of views') 59 expect(total).to.equal(2, 'Server ' + server.serverNumber + ' does not have the correct amount of views')
65 } 60 }
66 } 61 }
67 62
68 { 63 {
69 for (const server of servers) { 64 for (const server of servers) {
70 const total = await countVideoViewsOf(server.internalServerNumber, videoIdServer2) 65 const total = await server.sql.countVideoViewsOf(videoIdServer2)
71 expect(total).to.equal(2, 'Server ' + server.serverNumber + ' does not have the correct amount of views') 66 expect(total).to.equal(2, 'Server ' + server.serverNumber + ' does not have the correct amount of views')
72 } 67 }
73 } 68 }
@@ -76,9 +71,9 @@ describe('Test video views cleaner', function () {
76 it('Should clean old video views', async function () { 71 it('Should clean old video views', async function () {
77 this.timeout(50000) 72 this.timeout(50000)
78 73
79 killallServers([ servers[0] ]) 74 await killallServers([ servers[0] ])
80 75
81 await reRunServer(servers[0], { views: { videos: { remote: { max_age: '5 seconds' } } } }) 76 await servers[0].run({ views: { videos: { remote: { max_age: '5 seconds' } } } })
82 77
83 await wait(6000) 78 await wait(6000)
84 79
@@ -86,23 +81,21 @@ describe('Test video views cleaner', function () {
86 81
87 { 82 {
88 for (const server of servers) { 83 for (const server of servers) {
89 const total = await countVideoViewsOf(server.internalServerNumber, videoIdServer1) 84 const total = await server.sql.countVideoViewsOf(videoIdServer1)
90 expect(total).to.equal(2) 85 expect(total).to.equal(2)
91 } 86 }
92 } 87 }
93 88
94 { 89 {
95 const totalServer1 = await countVideoViewsOf(servers[0].internalServerNumber, videoIdServer2) 90 const totalServer1 = await servers[0].sql.countVideoViewsOf(videoIdServer2)
96 expect(totalServer1).to.equal(0) 91 expect(totalServer1).to.equal(0)
97 92
98 const totalServer2 = await countVideoViewsOf(servers[1].internalServerNumber, videoIdServer2) 93 const totalServer2 = await servers[1].sql.countVideoViewsOf(videoIdServer2)
99 expect(totalServer2).to.equal(2) 94 expect(totalServer2).to.equal(2)
100 } 95 }
101 }) 96 })
102 97
103 after(async function () { 98 after(async function () {
104 await closeAllSequelize(servers)
105
106 await cleanupTests(servers) 99 await cleanupTests(servers)
107 }) 100 })
108}) 101})
diff --git a/server/tests/cli/create-import-video-file-job.ts b/server/tests/cli/create-import-video-file-job.ts
index 49758ff56..bddcff5e7 100644
--- a/server/tests/cli/create-import-video-file-job.ts
+++ b/server/tests/cli/create-import-video-file-job.ts
@@ -2,21 +2,8 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoFile } from '@shared/models/videos/video-file.model' 5import { cleanupTests, createMultipleServers, doubleFollow, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
6import { 6import { VideoFile } from '@shared/models'
7 cleanupTests,
8 doubleFollow,
9 execCLI,
10 flushAndRunMultipleServers,
11 getEnvCli,
12 getVideo,
13 getVideosList,
14 ServerInfo,
15 setAccessTokensToServers,
16 uploadVideo
17} from '../../../shared/extra-utils'
18import { waitJobs } from '../../../shared/extra-utils/server/jobs'
19import { VideoDetails } from '../../../shared/models/videos'
20 7
21const expect = chai.expect 8const expect = chai.expect
22 9
@@ -33,7 +20,7 @@ function assertVideoProperties (video: VideoFile, resolution: number, extname: s
33describe('Test create import video jobs', function () { 20describe('Test create import video jobs', function () {
34 this.timeout(60000) 21 this.timeout(60000)
35 22
36 let servers: ServerInfo[] = [] 23 let servers: PeerTubeServer[] = []
37 let video1UUID: string 24 let video1UUID: string
38 let video2UUID: string 25 let video2UUID: string
39 26
@@ -41,56 +28,61 @@ describe('Test create import video jobs', function () {
41 this.timeout(90000) 28 this.timeout(90000)
42 29
43 // Run server 2 to have transcoding enabled 30 // Run server 2 to have transcoding enabled
44 servers = await flushAndRunMultipleServers(2) 31 servers = await createMultipleServers(2)
45 await setAccessTokensToServers(servers) 32 await setAccessTokensToServers(servers)
46 33
47 await doubleFollow(servers[0], servers[1]) 34 await doubleFollow(servers[0], servers[1])
48 35
49 // Upload two videos for our needs 36 // Upload two videos for our needs
50 const res1 = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video1' }) 37 {
51 video1UUID = res1.body.video.uuid 38 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video1' } })
52 const res2 = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video2' }) 39 video1UUID = uuid
53 video2UUID = res2.body.video.uuid 40 }
41
42 {
43 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video2' } })
44 video2UUID = uuid
45 }
54 46
55 // Transcoding 47 // Transcoding
56 await waitJobs(servers) 48 await waitJobs(servers)
57 }) 49 })
58 50
59 it('Should run a import job on video 1 with a lower resolution', async function () { 51 it('Should run a import job on video 1 with a lower resolution', async function () {
60 const env = getEnvCli(servers[0]) 52 const command = `npm run create-import-video-file-job -- -v ${video1UUID} -i server/tests/fixtures/video_short-480.webm`
61 await execCLI(`${env} npm run create-import-video-file-job -- -v ${video1UUID} -i server/tests/fixtures/video_short-480.webm`) 53 await servers[0].cli.execWithEnv(command)
62 54
63 await waitJobs(servers) 55 await waitJobs(servers)
64 56
65 for (const server of servers) { 57 for (const server of servers) {
66 const { data: videos } = (await getVideosList(server.url)).body 58 const { data: videos } = await server.videos.list()
67 expect(videos).to.have.lengthOf(2) 59 expect(videos).to.have.lengthOf(2)
68 60
69 const video = videos.find(({ uuid }) => uuid === video1UUID) 61 const video = videos.find(({ uuid }) => uuid === video1UUID)
70 const videoDetail: VideoDetails = (await getVideo(server.url, video.uuid)).body 62 const videoDetails = await server.videos.get({ id: video.uuid })
71 63
72 expect(videoDetail.files).to.have.lengthOf(2) 64 expect(videoDetails.files).to.have.lengthOf(2)
73 const [ originalVideo, transcodedVideo ] = videoDetail.files 65 const [ originalVideo, transcodedVideo ] = videoDetails.files
74 assertVideoProperties(originalVideo, 720, 'webm', 218910) 66 assertVideoProperties(originalVideo, 720, 'webm', 218910)
75 assertVideoProperties(transcodedVideo, 480, 'webm', 69217) 67 assertVideoProperties(transcodedVideo, 480, 'webm', 69217)
76 } 68 }
77 }) 69 })
78 70
79 it('Should run a import job on video 2 with the same resolution and a different extension', async function () { 71 it('Should run a import job on video 2 with the same resolution and a different extension', async function () {
80 const env = getEnvCli(servers[1]) 72 const command = `npm run create-import-video-file-job -- -v ${video2UUID} -i server/tests/fixtures/video_short.ogv`
81 await execCLI(`${env} npm run create-import-video-file-job -- -v ${video2UUID} -i server/tests/fixtures/video_short.ogv`) 73 await servers[1].cli.execWithEnv(command)
82 74
83 await waitJobs(servers) 75 await waitJobs(servers)
84 76
85 for (const server of servers) { 77 for (const server of servers) {
86 const { data: videos } = (await getVideosList(server.url)).body 78 const { data: videos } = await server.videos.list()
87 expect(videos).to.have.lengthOf(2) 79 expect(videos).to.have.lengthOf(2)
88 80
89 const video = videos.find(({ uuid }) => uuid === video2UUID) 81 const video = videos.find(({ uuid }) => uuid === video2UUID)
90 const videoDetail: VideoDetails = (await getVideo(server.url, video.uuid)).body 82 const videoDetails = await server.videos.get({ id: video.uuid })
91 83
92 expect(videoDetail.files).to.have.lengthOf(4) 84 expect(videoDetails.files).to.have.lengthOf(4)
93 const [ originalVideo, transcodedVideo420, transcodedVideo320, transcodedVideo240 ] = videoDetail.files 85 const [ originalVideo, transcodedVideo420, transcodedVideo320, transcodedVideo240 ] = videoDetails.files
94 assertVideoProperties(originalVideo, 720, 'ogv', 140849) 86 assertVideoProperties(originalVideo, 720, 'ogv', 140849)
95 assertVideoProperties(transcodedVideo420, 480, 'mp4') 87 assertVideoProperties(transcodedVideo420, 480, 'mp4')
96 assertVideoProperties(transcodedVideo320, 360, 'mp4') 88 assertVideoProperties(transcodedVideo320, 360, 'mp4')
@@ -99,20 +91,20 @@ describe('Test create import video jobs', function () {
99 }) 91 })
100 92
101 it('Should run a import job on video 2 with the same resolution and the same extension', async function () { 93 it('Should run a import job on video 2 with the same resolution and the same extension', async function () {
102 const env = getEnvCli(servers[0]) 94 const command = `npm run create-import-video-file-job -- -v ${video1UUID} -i server/tests/fixtures/video_short2.webm`
103 await execCLI(`${env} npm run create-import-video-file-job -- -v ${video1UUID} -i server/tests/fixtures/video_short2.webm`) 95 await servers[0].cli.execWithEnv(command)
104 96
105 await waitJobs(servers) 97 await waitJobs(servers)
106 98
107 for (const server of servers) { 99 for (const server of servers) {
108 const { data: videos } = (await getVideosList(server.url)).body 100 const { data: videos } = await server.videos.list()
109 expect(videos).to.have.lengthOf(2) 101 expect(videos).to.have.lengthOf(2)
110 102
111 const video = videos.find(({ uuid }) => uuid === video1UUID) 103 const video = videos.find(({ uuid }) => uuid === video1UUID)
112 const videoDetail: VideoDetails = (await getVideo(server.url, video.uuid)).body 104 const videoDetails = await server.videos.get({ id: video.uuid })
113 105
114 expect(videoDetail.files).to.have.lengthOf(2) 106 expect(videoDetails.files).to.have.lengthOf(2)
115 const [ video720, video480 ] = videoDetail.files 107 const [ video720, video480 ] = videoDetails.files
116 assertVideoProperties(video720, 720, 'webm', 942961) 108 assertVideoProperties(video720, 720, 'webm', 942961)
117 assertVideoProperties(video480, 480, 'webm', 69217) 109 assertVideoProperties(video480, 480, 'webm', 69217)
118 } 110 }
diff --git a/server/tests/cli/create-transcoding-job.ts b/server/tests/cli/create-transcoding-job.ts
index 5bc1687cd..df787ccdc 100644
--- a/server/tests/cli/create-transcoding-job.ts
+++ b/server/tests/cli/create-transcoding-job.ts
@@ -2,26 +2,19 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoDetails } from '../../../shared/models/videos'
6import { 5import {
7 cleanupTests, 6 cleanupTests,
7 createMultipleServers,
8 doubleFollow, 8 doubleFollow,
9 execCLI, 9 PeerTubeServer,
10 flushAndRunMultipleServers,
11 getEnvCli,
12 getVideo,
13 getVideosList,
14 ServerInfo,
15 setAccessTokensToServers, 10 setAccessTokensToServers,
16 updateCustomSubConfig, 11 waitJobs
17 uploadVideo
18} from '../../../shared/extra-utils' 12} from '../../../shared/extra-utils'
19import { waitJobs } from '../../../shared/extra-utils/server/jobs'
20 13
21const expect = chai.expect 14const expect = chai.expect
22 15
23describe('Test create transcoding jobs', function () { 16describe('Test create transcoding jobs', function () {
24 let servers: ServerInfo[] = [] 17 let servers: PeerTubeServer[] = []
25 const videosUUID: string[] = [] 18 const videosUUID: string[] = []
26 19
27 const config = { 20 const config = {
@@ -46,16 +39,16 @@ describe('Test create transcoding jobs', function () {
46 this.timeout(60000) 39 this.timeout(60000)
47 40
48 // Run server 2 to have transcoding enabled 41 // Run server 2 to have transcoding enabled
49 servers = await flushAndRunMultipleServers(2) 42 servers = await createMultipleServers(2)
50 await setAccessTokensToServers(servers) 43 await setAccessTokensToServers(servers)
51 44
52 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 45 await servers[0].config.updateCustomSubConfig({ newConfig: config })
53 46
54 await doubleFollow(servers[0], servers[1]) 47 await doubleFollow(servers[0], servers[1])
55 48
56 for (let i = 1; i <= 5; i++) { 49 for (let i = 1; i <= 5; i++) {
57 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' + i }) 50 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video' + i } })
58 videosUUID.push(res.body.video.uuid) 51 videosUUID.push(uuid)
59 } 52 }
60 53
61 await waitJobs(servers) 54 await waitJobs(servers)
@@ -65,13 +58,11 @@ describe('Test create transcoding jobs', function () {
65 this.timeout(30000) 58 this.timeout(30000)
66 59
67 for (const server of servers) { 60 for (const server of servers) {
68 const res = await getVideosList(server.url) 61 const { data } = await server.videos.list()
69 const videos = res.body.data 62 expect(data).to.have.lengthOf(videosUUID.length)
70 expect(videos).to.have.lengthOf(videosUUID.length)
71 63
72 for (const video of videos) { 64 for (const video of data) {
73 const res2 = await getVideo(server.url, video.uuid) 65 const videoDetail = await server.videos.get({ id: video.uuid })
74 const videoDetail: VideoDetails = res2.body
75 expect(videoDetail.files).to.have.lengthOf(1) 66 expect(videoDetail.files).to.have.lengthOf(1)
76 expect(videoDetail.streamingPlaylists).to.have.lengthOf(0) 67 expect(videoDetail.streamingPlaylists).to.have.lengthOf(0)
77 } 68 }
@@ -81,20 +72,16 @@ describe('Test create transcoding jobs', function () {
81 it('Should run a transcoding job on video 2', async function () { 72 it('Should run a transcoding job on video 2', async function () {
82 this.timeout(60000) 73 this.timeout(60000)
83 74
84 const env = getEnvCli(servers[0]) 75 await servers[0].cli.execWithEnv(`npm run create-transcoding-job -- -v ${videosUUID[1]}`)
85 await execCLI(`${env} npm run create-transcoding-job -- -v ${videosUUID[1]}`)
86
87 await waitJobs(servers) 76 await waitJobs(servers)
88 77
89 for (const server of servers) { 78 for (const server of servers) {
90 const res = await getVideosList(server.url) 79 const { data } = await server.videos.list()
91 const videos = res.body.data
92 80
93 let infoHashes: { [id: number]: string } 81 let infoHashes: { [id: number]: string }
94 82
95 for (const video of videos) { 83 for (const video of data) {
96 const res2 = await getVideo(server.url, video.uuid) 84 const videoDetail = await server.videos.get({ id: video.uuid })
97 const videoDetail: VideoDetails = res2.body
98 85
99 if (video.uuid === videosUUID[1]) { 86 if (video.uuid === videosUUID[1]) {
100 expect(videoDetail.files).to.have.lengthOf(4) 87 expect(videoDetail.files).to.have.lengthOf(4)
@@ -123,43 +110,38 @@ describe('Test create transcoding jobs', function () {
123 it('Should run a transcoding job on video 1 with resolution', async function () { 110 it('Should run a transcoding job on video 1 with resolution', async function () {
124 this.timeout(60000) 111 this.timeout(60000)
125 112
126 const env = getEnvCli(servers[0]) 113 await servers[0].cli.execWithEnv(`npm run create-transcoding-job -- -v ${videosUUID[0]} -r 480`)
127 await execCLI(`${env} npm run create-transcoding-job -- -v ${videosUUID[0]} -r 480`)
128 114
129 await waitJobs(servers) 115 await waitJobs(servers)
130 116
131 for (const server of servers) { 117 for (const server of servers) {
132 const res = await getVideosList(server.url) 118 const { data } = await server.videos.list()
133 const videos = res.body.data 119 expect(data).to.have.lengthOf(videosUUID.length)
134 expect(videos).to.have.lengthOf(videosUUID.length)
135 120
136 const res2 = await getVideo(server.url, videosUUID[0]) 121 const videoDetails = await server.videos.get({ id: videosUUID[0] })
137 const videoDetail: VideoDetails = res2.body
138 122
139 expect(videoDetail.files).to.have.lengthOf(2) 123 expect(videoDetails.files).to.have.lengthOf(2)
140 expect(videoDetail.files[0].resolution.id).to.equal(720) 124 expect(videoDetails.files[0].resolution.id).to.equal(720)
141 expect(videoDetail.files[1].resolution.id).to.equal(480) 125 expect(videoDetails.files[1].resolution.id).to.equal(480)
142 126
143 expect(videoDetail.streamingPlaylists).to.have.lengthOf(0) 127 expect(videoDetails.streamingPlaylists).to.have.lengthOf(0)
144 } 128 }
145 }) 129 })
146 130
147 it('Should generate an HLS resolution', async function () { 131 it('Should generate an HLS resolution', async function () {
148 this.timeout(120000) 132 this.timeout(120000)
149 133
150 const env = getEnvCli(servers[0]) 134 await servers[0].cli.execWithEnv(`npm run create-transcoding-job -- -v ${videosUUID[2]} --generate-hls -r 480`)
151 await execCLI(`${env} npm run create-transcoding-job -- -v ${videosUUID[2]} --generate-hls -r 480`)
152 135
153 await waitJobs(servers) 136 await waitJobs(servers)
154 137
155 for (const server of servers) { 138 for (const server of servers) {
156 const res = await getVideo(server.url, videosUUID[2]) 139 const videoDetails = await server.videos.get({ id: videosUUID[2] })
157 const videoDetail: VideoDetails = res.body
158 140
159 expect(videoDetail.files).to.have.lengthOf(1) 141 expect(videoDetails.files).to.have.lengthOf(1)
160 expect(videoDetail.streamingPlaylists).to.have.lengthOf(1) 142 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
161 143
162 const files = videoDetail.streamingPlaylists[0].files 144 const files = videoDetails.streamingPlaylists[0].files
163 expect(files).to.have.lengthOf(1) 145 expect(files).to.have.lengthOf(1)
164 expect(files[0].resolution.id).to.equal(480) 146 expect(files[0].resolution.id).to.equal(480)
165 } 147 }
@@ -168,16 +150,14 @@ describe('Test create transcoding jobs', function () {
168 it('Should not duplicate an HLS resolution', async function () { 150 it('Should not duplicate an HLS resolution', async function () {
169 this.timeout(120000) 151 this.timeout(120000)
170 152
171 const env = getEnvCli(servers[0]) 153 await servers[0].cli.execWithEnv(`npm run create-transcoding-job -- -v ${videosUUID[2]} --generate-hls -r 480`)
172 await execCLI(`${env} npm run create-transcoding-job -- -v ${videosUUID[2]} --generate-hls -r 480`)
173 154
174 await waitJobs(servers) 155 await waitJobs(servers)
175 156
176 for (const server of servers) { 157 for (const server of servers) {
177 const res = await getVideo(server.url, videosUUID[2]) 158 const videoDetails = await server.videos.get({ id: videosUUID[2] })
178 const videoDetail: VideoDetails = res.body
179 159
180 const files = videoDetail.streamingPlaylists[0].files 160 const files = videoDetails.streamingPlaylists[0].files
181 expect(files).to.have.lengthOf(1) 161 expect(files).to.have.lengthOf(1)
182 expect(files[0].resolution.id).to.equal(480) 162 expect(files[0].resolution.id).to.equal(480)
183 } 163 }
@@ -186,19 +166,17 @@ describe('Test create transcoding jobs', function () {
186 it('Should generate all HLS resolutions', async function () { 166 it('Should generate all HLS resolutions', async function () {
187 this.timeout(120000) 167 this.timeout(120000)
188 168
189 const env = getEnvCli(servers[0]) 169 await servers[0].cli.execWithEnv(`npm run create-transcoding-job -- -v ${videosUUID[3]} --generate-hls`)
190 await execCLI(`${env} npm run create-transcoding-job -- -v ${videosUUID[3]} --generate-hls`)
191 170
192 await waitJobs(servers) 171 await waitJobs(servers)
193 172
194 for (const server of servers) { 173 for (const server of servers) {
195 const res = await getVideo(server.url, videosUUID[3]) 174 const videoDetails = await server.videos.get({ id: videosUUID[3] })
196 const videoDetail: VideoDetails = res.body
197 175
198 expect(videoDetail.files).to.have.lengthOf(1) 176 expect(videoDetails.files).to.have.lengthOf(1)
199 expect(videoDetail.streamingPlaylists).to.have.lengthOf(1) 177 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
200 178
201 const files = videoDetail.streamingPlaylists[0].files 179 const files = videoDetails.streamingPlaylists[0].files
202 expect(files).to.have.lengthOf(4) 180 expect(files).to.have.lengthOf(4)
203 } 181 }
204 }) 182 })
@@ -207,20 +185,18 @@ describe('Test create transcoding jobs', function () {
207 this.timeout(120000) 185 this.timeout(120000)
208 186
209 config.transcoding.hls.enabled = true 187 config.transcoding.hls.enabled = true
210 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 188 await servers[0].config.updateCustomSubConfig({ newConfig: config })
211 189
212 const env = getEnvCli(servers[0]) 190 await servers[0].cli.execWithEnv(`npm run create-transcoding-job -- -v ${videosUUID[4]}`)
213 await execCLI(`${env} npm run create-transcoding-job -- -v ${videosUUID[4]}`)
214 191
215 await waitJobs(servers) 192 await waitJobs(servers)
216 193
217 for (const server of servers) { 194 for (const server of servers) {
218 const res = await getVideo(server.url, videosUUID[4]) 195 const videoDetails = await server.videos.get({ id: videosUUID[4] })
219 const videoDetail: VideoDetails = res.body
220 196
221 expect(videoDetail.files).to.have.lengthOf(4) 197 expect(videoDetails.files).to.have.lengthOf(4)
222 expect(videoDetail.streamingPlaylists).to.have.lengthOf(1) 198 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
223 expect(videoDetail.streamingPlaylists[0].files).to.have.lengthOf(4) 199 expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(4)
224 } 200 }
225 }) 201 })
226 202
diff --git a/server/tests/cli/optimize-old-videos.ts b/server/tests/cli/optimize-old-videos.ts
index 91a1c9cc4..579b2e7d8 100644
--- a/server/tests/cli/optimize-old-videos.ts
+++ b/server/tests/cli/optimize-old-videos.ts
@@ -2,38 +2,30 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { join } from 'path'
6import { 5import {
7 buildServerDirectory,
8 cleanupTests, 6 cleanupTests,
7 createMultipleServers,
9 doubleFollow, 8 doubleFollow,
10 execCLI,
11 flushAndRunMultipleServers,
12 generateHighBitrateVideo, 9 generateHighBitrateVideo,
13 getEnvCli, 10 PeerTubeServer,
14 getVideo,
15 getVideosList,
16 ServerInfo,
17 setAccessTokensToServers, 11 setAccessTokensToServers,
18 uploadVideo, 12 wait,
19 viewVideo, 13 waitJobs
20 wait 14} from '@shared/extra-utils'
21} from '../../../shared/extra-utils' 15import { getMaxBitrate, VideoResolution } from '@shared/models'
22import { waitJobs } from '../../../shared/extra-utils/server/jobs'
23import { getMaxBitrate, Video, VideoDetails, VideoResolution } from '../../../shared/models/videos'
24import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../helpers/ffprobe-utils' 16import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../helpers/ffprobe-utils'
25import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants' 17import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants'
26 18
27const expect = chai.expect 19const expect = chai.expect
28 20
29describe('Test optimize old videos', function () { 21describe('Test optimize old videos', function () {
30 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
31 23
32 before(async function () { 24 before(async function () {
33 this.timeout(200000) 25 this.timeout(200000)
34 26
35 // Run server 2 to have transcoding enabled 27 // Run server 2 to have transcoding enabled
36 servers = await flushAndRunMultipleServers(2) 28 servers = await createMultipleServers(2)
37 await setAccessTokensToServers(servers) 29 await setAccessTokensToServers(servers)
38 30
39 await doubleFollow(servers[0], servers[1]) 31 await doubleFollow(servers[0], servers[1])
@@ -48,8 +40,8 @@ describe('Test optimize old videos', function () {
48 } 40 }
49 41
50 // Upload two videos for our needs 42 // Upload two videos for our needs
51 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video1', fixture: tempFixturePath }) 43 await servers[0].videos.upload({ attributes: { name: 'video1', fixture: tempFixturePath } })
52 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video2', fixture: tempFixturePath }) 44 await servers[0].videos.upload({ attributes: { name: 'video2', fixture: tempFixturePath } })
53 45
54 await waitJobs(servers) 46 await waitJobs(servers)
55 }) 47 })
@@ -58,14 +50,12 @@ describe('Test optimize old videos', function () {
58 this.timeout(30000) 50 this.timeout(30000)
59 51
60 for (const server of servers) { 52 for (const server of servers) {
61 const res = await getVideosList(server.url) 53 const { data } = await server.videos.list()
62 const videos = res.body.data 54 expect(data).to.have.lengthOf(2)
63 expect(videos).to.have.lengthOf(2) 55
64 56 for (const video of data) {
65 for (const video of videos) { 57 const videoDetails = await server.videos.get({ id: video.uuid })
66 const res2 = await getVideo(server.url, video.uuid) 58 expect(videoDetails.files).to.have.lengthOf(1)
67 const videoDetail: VideoDetails = res2.body
68 expect(videoDetail.files).to.have.lengthOf(1)
69 } 59 }
70 } 60 }
71 }) 61 })
@@ -73,34 +63,29 @@ describe('Test optimize old videos', function () {
73 it('Should run optimize script', async function () { 63 it('Should run optimize script', async function () {
74 this.timeout(200000) 64 this.timeout(200000)
75 65
76 const env = getEnvCli(servers[0]) 66 await servers[0].cli.execWithEnv('npm run optimize-old-videos')
77 await execCLI(`${env} npm run optimize-old-videos`)
78
79 await waitJobs(servers) 67 await waitJobs(servers)
80 68
81 for (const server of servers) { 69 for (const server of servers) {
82 const res = await getVideosList(server.url) 70 const { data } = await server.videos.list()
83 const videos: Video[] = res.body.data 71 expect(data).to.have.lengthOf(2)
84
85 expect(videos).to.have.lengthOf(2)
86 72
87 for (const video of videos) { 73 for (const video of data) {
88 await viewVideo(server.url, video.uuid) 74 await server.videos.view({ id: video.uuid })
89 75
90 // Refresh video 76 // Refresh video
91 await waitJobs(servers) 77 await waitJobs(servers)
92 await wait(5000) 78 await wait(5000)
93 await waitJobs(servers) 79 await waitJobs(servers)
94 80
95 const res2 = await getVideo(server.url, video.uuid) 81 const videoDetails = await server.videos.get({ id: video.uuid })
96 const videosDetails: VideoDetails = res2.body
97 82
98 expect(videosDetails.files).to.have.lengthOf(1) 83 expect(videoDetails.files).to.have.lengthOf(1)
99 const file = videosDetails.files[0] 84 const file = videoDetails.files[0]
100 85
101 expect(file.size).to.be.below(8000000) 86 expect(file.size).to.be.below(8000000)
102 87
103 const path = buildServerDirectory(servers[0], join('videos', video.uuid + '-' + file.resolution.id + '.mp4')) 88 const path = servers[0].servers.buildWebTorrentFilePath(file.fileUrl)
104 const bitrate = await getVideoFileBitrate(path) 89 const bitrate = await getVideoFileBitrate(path)
105 const fps = await getVideoFileFPS(path) 90 const fps = await getVideoFileFPS(path)
106 const resolution = await getVideoFileResolution(path) 91 const resolution = await getVideoFileResolution(path)
diff --git a/server/tests/cli/peertube.ts b/server/tests/cli/peertube.ts
index fcf7e2e2e..f2a984962 100644
--- a/server/tests/cli/peertube.ts
+++ b/server/tests/cli/peertube.ts
@@ -2,97 +2,91 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { Video, VideoDetails } from '../../../shared'
6import { 5import {
7 addVideoChannel,
8 areHttpImportTestsDisabled, 6 areHttpImportTestsDisabled,
9 buildAbsoluteFixturePath, 7 buildAbsoluteFixturePath,
10 cleanupTests, 8 cleanupTests,
11 createUser, 9 CLICommand,
10 createSingleServer,
12 doubleFollow, 11 doubleFollow,
13 execCLI, 12 FIXTURE_URLS,
14 flushAndRunServer, 13 PeerTubeServer,
15 getEnvCli,
16 getLocalIdByUUID,
17 getVideo,
18 getVideosList,
19 removeVideo,
20 ServerInfo,
21 setAccessTokensToServers, 14 setAccessTokensToServers,
22 testHelloWorldRegisteredSettings, 15 testHelloWorldRegisteredSettings,
23 uploadVideoAndGetId,
24 userLogin,
25 waitJobs 16 waitJobs
26} from '../../../shared/extra-utils' 17} from '../../../shared/extra-utils'
27import { getYoutubeVideoUrl } from '../../../shared/extra-utils/videos/video-imports'
28 18
29describe('Test CLI wrapper', function () { 19describe('Test CLI wrapper', function () {
30 let server: ServerInfo 20 let server: PeerTubeServer
31 let userAccessToken: string 21 let userAccessToken: string
32 22
23 let cliCommand: CLICommand
24
33 const cmd = 'node ./dist/server/tools/peertube.js' 25 const cmd = 'node ./dist/server/tools/peertube.js'
34 26
35 before(async function () { 27 before(async function () {
36 this.timeout(30000) 28 this.timeout(30000)
37 29
38 server = await flushAndRunServer(1) 30 server = await createSingleServer(1)
39 await setAccessTokensToServers([ server ]) 31 await setAccessTokensToServers([ server ])
40 32
41 await createUser({ url: server.url, accessToken: server.accessToken, username: 'user_1', password: 'super_password' }) 33 await server.users.create({ username: 'user_1', password: 'super_password' })
42 34
43 userAccessToken = await userLogin(server, { username: 'user_1', password: 'super_password' }) 35 userAccessToken = await server.login.getAccessToken({ username: 'user_1', password: 'super_password' })
44 36
45 { 37 {
46 const args = { name: 'user_channel', displayName: 'User channel', support: 'super support text' } 38 const attributes = { name: 'user_channel', displayName: 'User channel', support: 'super support text' }
47 await addVideoChannel(server.url, userAccessToken, args) 39 await server.channels.create({ token: userAccessToken, attributes })
48 } 40 }
41
42 cliCommand = server.cli
49 }) 43 })
50 44
51 describe('Authentication and instance selection', function () { 45 describe('Authentication and instance selection', function () {
52 46
47 it('Should get an access token', async function () {
48 const stdout = await cliCommand.execWithEnv(`${cmd} token --url ${server.url} --username user_1 --password super_password`)
49 const token = stdout.trim()
50
51 const body = await server.users.getMyInfo({ token })
52 expect(body.username).to.equal('user_1')
53 })
54
53 it('Should display no selected instance', async function () { 55 it('Should display no selected instance', async function () {
54 this.timeout(60000) 56 this.timeout(60000)
55 57
56 const env = getEnvCli(server) 58 const stdout = await cliCommand.execWithEnv(`${cmd} --help`)
57 const stdout = await execCLI(`${env} ${cmd} --help`)
58
59 expect(stdout).to.contain('no instance selected') 59 expect(stdout).to.contain('no instance selected')
60 }) 60 })
61 61
62 it('Should add a user', async function () { 62 it('Should add a user', async function () {
63 this.timeout(60000) 63 this.timeout(60000)
64 64
65 const env = getEnvCli(server) 65 await cliCommand.execWithEnv(`${cmd} auth add -u ${server.url} -U user_1 -p super_password`)
66 await execCLI(`${env} ${cmd} auth add -u ${server.url} -U user_1 -p super_password`)
67 }) 66 })
68 67
69 it('Should not fail to add a user if there is a slash at the end of the instance URL', async function () { 68 it('Should not fail to add a user if there is a slash at the end of the instance URL', async function () {
70 this.timeout(60000) 69 this.timeout(60000)
71 70
72 const env = getEnvCli(server) 71 let fullServerURL = server.url + '/'
73 let fullServerURL 72
74 fullServerURL = server.url + '/' 73 await cliCommand.execWithEnv(`${cmd} auth add -u ${fullServerURL} -U user_1 -p super_password`)
75 await execCLI(`${env} ${cmd} auth add -u ${fullServerURL} -U user_1 -p super_password`)
76 74
77 fullServerURL = server.url + '/asdfasdf' 75 fullServerURL = server.url + '/asdfasdf'
78 await execCLI(`${env} ${cmd} auth add -u ${fullServerURL} -U user_1 -p super_password`) 76 await cliCommand.execWithEnv(`${cmd} auth add -u ${fullServerURL} -U user_1 -p super_password`)
79 }) 77 })
80 78
81 it('Should default to this user', async function () { 79 it('Should default to this user', async function () {
82 this.timeout(60000) 80 this.timeout(60000)
83 81
84 const env = getEnvCli(server) 82 const stdout = await cliCommand.execWithEnv(`${cmd} --help`)
85 const stdout = await execCLI(`${env} ${cmd} --help`)
86
87 expect(stdout).to.contain(`instance ${server.url} selected`) 83 expect(stdout).to.contain(`instance ${server.url} selected`)
88 }) 84 })
89 85
90 it('Should remember the user', async function () { 86 it('Should remember the user', async function () {
91 this.timeout(60000) 87 this.timeout(60000)
92 88
93 const env = getEnvCli(server) 89 const stdout = await cliCommand.execWithEnv(`${cmd} auth list`)
94 const stdout = await execCLI(`${env} ${cmd} auth list`)
95
96 expect(stdout).to.contain(server.url) 90 expect(stdout).to.contain(server.url)
97 }) 91 })
98 }) 92 })
@@ -102,24 +96,17 @@ describe('Test CLI wrapper', function () {
102 it('Should upload a video', async function () { 96 it('Should upload a video', async function () {
103 this.timeout(60000) 97 this.timeout(60000)
104 98
105 const env = getEnvCli(server)
106
107 const fixture = buildAbsoluteFixturePath('60fps_720p_small.mp4') 99 const fixture = buildAbsoluteFixturePath('60fps_720p_small.mp4')
108
109 const params = `-f ${fixture} --video-name 'test upload' --channel-name user_channel --support 'support_text'` 100 const params = `-f ${fixture} --video-name 'test upload' --channel-name user_channel --support 'support_text'`
110 101
111 await execCLI(`${env} ${cmd} upload ${params}`) 102 await cliCommand.execWithEnv(`${cmd} upload ${params}`)
112 }) 103 })
113 104
114 it('Should have the video uploaded', async function () { 105 it('Should have the video uploaded', async function () {
115 const res = await getVideosList(server.url) 106 const { total, data } = await server.videos.list()
116 107 expect(total).to.equal(1)
117 expect(res.body.total).to.equal(1)
118
119 const videos: Video[] = res.body.data
120
121 const video: VideoDetails = (await getVideo(server.url, videos[0].uuid)).body
122 108
109 const video = await server.videos.get({ id: data[0].uuid })
123 expect(video.name).to.equal('test upload') 110 expect(video.name).to.equal('test upload')
124 expect(video.support).to.equal('support_text') 111 expect(video.support).to.equal('support_text')
125 expect(video.channel.name).to.equal('user_channel') 112 expect(video.channel.name).to.equal('user_channel')
@@ -130,11 +117,8 @@ describe('Test CLI wrapper', function () {
130 117
131 this.timeout(60000) 118 this.timeout(60000)
132 119
133 const env = getEnvCli(server) 120 const params = `--target-url ${FIXTURE_URLS.youtube} --channel-name user_channel`
134 121 await cliCommand.execWithEnv(`${cmd} import ${params}`)
135 const params = `--target-url ${getYoutubeVideoUrl()} --channel-name user_channel`
136
137 await execCLI(`${env} ${cmd} import ${params}`)
138 }) 122 })
139 123
140 it('Should have imported the video', async function () { 124 it('Should have imported the video', async function () {
@@ -144,21 +128,19 @@ describe('Test CLI wrapper', function () {
144 128
145 await waitJobs([ server ]) 129 await waitJobs([ server ])
146 130
147 const res = await getVideosList(server.url) 131 const { total, data } = await server.videos.list()
148 132 expect(total).to.equal(2)
149 expect(res.body.total).to.equal(2)
150 133
151 const videos: Video[] = res.body.data 134 const video = data.find(v => v.name === 'small video - youtube')
152 const video = videos.find(v => v.name === 'small video - youtube')
153 expect(video).to.not.be.undefined 135 expect(video).to.not.be.undefined
154 136
155 const videoDetails: VideoDetails = (await getVideo(server.url, video.id)).body 137 const videoDetails = await server.videos.get({ id: video.id })
156 expect(videoDetails.channel.name).to.equal('user_channel') 138 expect(videoDetails.channel.name).to.equal('user_channel')
157 expect(videoDetails.support).to.equal('super support text') 139 expect(videoDetails.support).to.equal('super support text')
158 expect(videoDetails.nsfw).to.be.false 140 expect(videoDetails.nsfw).to.be.false
159 141
160 // So we can reimport it 142 // So we can reimport it
161 await removeVideo(server.url, userAccessToken, video.id) 143 await server.videos.remove({ token: userAccessToken, id: video.id })
162 }) 144 })
163 145
164 it('Should import and override some imported attributes', async function () { 146 it('Should import and override some imported attributes', async function () {
@@ -166,23 +148,20 @@ describe('Test CLI wrapper', function () {
166 148
167 this.timeout(60000) 149 this.timeout(60000)
168 150
169 const env = getEnvCli(server) 151 const params = `--target-url ${FIXTURE_URLS.youtube} ` +
170 152 `--channel-name user_channel --video-name toto --nsfw --support support`
171 const params = `--target-url ${getYoutubeVideoUrl()} --channel-name user_channel --video-name toto --nsfw --support support` 153 await cliCommand.execWithEnv(`${cmd} import ${params}`)
172
173 await execCLI(`${env} ${cmd} import ${params}`)
174 154
175 await waitJobs([ server ]) 155 await waitJobs([ server ])
176 156
177 { 157 {
178 const res = await getVideosList(server.url) 158 const { total, data } = await server.videos.list()
179 expect(res.body.total).to.equal(2) 159 expect(total).to.equal(2)
180 160
181 const videos: Video[] = res.body.data 161 const video = data.find(v => v.name === 'toto')
182 const video = videos.find(v => v.name === 'toto')
183 expect(video).to.not.be.undefined 162 expect(video).to.not.be.undefined
184 163
185 const videoDetails: VideoDetails = (await getVideo(server.url, video.id)).body 164 const videoDetails = await server.videos.get({ id: video.id })
186 expect(videoDetails.channel.name).to.equal('user_channel') 165 expect(videoDetails.channel.name).to.equal('user_channel')
187 expect(videoDetails.support).to.equal('support') 166 expect(videoDetails.support).to.equal('support')
188 expect(videoDetails.nsfw).to.be.true 167 expect(videoDetails.nsfw).to.be.true
@@ -194,18 +173,14 @@ describe('Test CLI wrapper', function () {
194 describe('Admin auth', function () { 173 describe('Admin auth', function () {
195 174
196 it('Should remove the auth user', async function () { 175 it('Should remove the auth user', async function () {
197 const env = getEnvCli(server) 176 await cliCommand.execWithEnv(`${cmd} auth del ${server.url}`)
198
199 await execCLI(`${env} ${cmd} auth del ${server.url}`)
200
201 const stdout = await execCLI(`${env} ${cmd} --help`)
202 177
178 const stdout = await cliCommand.execWithEnv(`${cmd} --help`)
203 expect(stdout).to.contain('no instance selected') 179 expect(stdout).to.contain('no instance selected')
204 }) 180 })
205 181
206 it('Should add the admin user', async function () { 182 it('Should add the admin user', async function () {
207 const env = getEnvCli(server) 183 await cliCommand.execWithEnv(`${cmd} auth add -u ${server.url} -U root -p test${server.internalServerNumber}`)
208 await execCLI(`${env} ${cmd} auth add -u ${server.url} -U root -p test${server.internalServerNumber}`)
209 }) 184 })
210 }) 185 })
211 186
@@ -214,8 +189,7 @@ describe('Test CLI wrapper', function () {
214 it('Should install a plugin', async function () { 189 it('Should install a plugin', async function () {
215 this.timeout(60000) 190 this.timeout(60000)
216 191
217 const env = getEnvCli(server) 192 await cliCommand.execWithEnv(`${cmd} plugins install --npm-name peertube-plugin-hello-world`)
218 await execCLI(`${env} ${cmd} plugins install --npm-name peertube-plugin-hello-world`)
219 }) 193 })
220 194
221 it('Should have registered settings', async function () { 195 it('Should have registered settings', async function () {
@@ -223,29 +197,27 @@ describe('Test CLI wrapper', function () {
223 }) 197 })
224 198
225 it('Should list installed plugins', async function () { 199 it('Should list installed plugins', async function () {
226 const env = getEnvCli(server) 200 const res = await cliCommand.execWithEnv(`${cmd} plugins list`)
227 const res = await execCLI(`${env} ${cmd} plugins list`)
228 201
229 expect(res).to.contain('peertube-plugin-hello-world') 202 expect(res).to.contain('peertube-plugin-hello-world')
230 }) 203 })
231 204
232 it('Should uninstall the plugin', async function () { 205 it('Should uninstall the plugin', async function () {
233 const env = getEnvCli(server) 206 const res = await cliCommand.execWithEnv(`${cmd} plugins uninstall --npm-name peertube-plugin-hello-world`)
234 const res = await execCLI(`${env} ${cmd} plugins uninstall --npm-name peertube-plugin-hello-world`)
235 207
236 expect(res).to.not.contain('peertube-plugin-hello-world') 208 expect(res).to.not.contain('peertube-plugin-hello-world')
237 }) 209 })
238 }) 210 })
239 211
240 describe('Manage video redundancies', function () { 212 describe('Manage video redundancies', function () {
241 let anotherServer: ServerInfo 213 let anotherServer: PeerTubeServer
242 let video1Server2: number 214 let video1Server2: number
243 let servers: ServerInfo[] 215 let servers: PeerTubeServer[]
244 216
245 before(async function () { 217 before(async function () {
246 this.timeout(120000) 218 this.timeout(120000)
247 219
248 anotherServer = await flushAndRunServer(2) 220 anotherServer = await createSingleServer(2)
249 await setAccessTokensToServers([ anotherServer ]) 221 await setAccessTokensToServers([ anotherServer ])
250 222
251 await doubleFollow(server, anotherServer) 223 await doubleFollow(server, anotherServer)
@@ -253,20 +225,17 @@ describe('Test CLI wrapper', function () {
253 servers = [ server, anotherServer ] 225 servers = [ server, anotherServer ]
254 await waitJobs(servers) 226 await waitJobs(servers)
255 227
256 const uuid = (await uploadVideoAndGetId({ server: anotherServer, videoName: 'super video' })).uuid 228 const { uuid } = await anotherServer.videos.quickUpload({ name: 'super video' })
257 await waitJobs(servers) 229 await waitJobs(servers)
258 230
259 video1Server2 = await getLocalIdByUUID(server.url, uuid) 231 video1Server2 = await server.videos.getId({ uuid })
260 }) 232 })
261 233
262 it('Should add a redundancy', async function () { 234 it('Should add a redundancy', async function () {
263 this.timeout(60000) 235 this.timeout(60000)
264 236
265 const env = getEnvCli(server)
266
267 const params = `add --video ${video1Server2}` 237 const params = `add --video ${video1Server2}`
268 238 await cliCommand.execWithEnv(`${cmd} redundancy ${params}`)
269 await execCLI(`${env} ${cmd} redundancy ${params}`)
270 239
271 await waitJobs(servers) 240 await waitJobs(servers)
272 }) 241 })
@@ -275,10 +244,8 @@ describe('Test CLI wrapper', function () {
275 this.timeout(60000) 244 this.timeout(60000)
276 245
277 { 246 {
278 const env = getEnvCli(server)
279
280 const params = 'list-my-redundancies' 247 const params = 'list-my-redundancies'
281 const stdout = await execCLI(`${env} ${cmd} redundancy ${params}`) 248 const stdout = await cliCommand.execWithEnv(`${cmd} redundancy ${params}`)
282 249
283 expect(stdout).to.contain('super video') 250 expect(stdout).to.contain('super video')
284 expect(stdout).to.contain(`localhost:${server.port}`) 251 expect(stdout).to.contain(`localhost:${server.port}`)
@@ -288,18 +255,14 @@ describe('Test CLI wrapper', function () {
288 it('Should remove a redundancy', async function () { 255 it('Should remove a redundancy', async function () {
289 this.timeout(60000) 256 this.timeout(60000)
290 257
291 const env = getEnvCli(server)
292
293 const params = `remove --video ${video1Server2}` 258 const params = `remove --video ${video1Server2}`
294 259 await cliCommand.execWithEnv(`${cmd} redundancy ${params}`)
295 await execCLI(`${env} ${cmd} redundancy ${params}`)
296 260
297 await waitJobs(servers) 261 await waitJobs(servers)
298 262
299 { 263 {
300 const env = getEnvCli(server)
301 const params = 'list-my-redundancies' 264 const params = 'list-my-redundancies'
302 const stdout = await execCLI(`${env} ${cmd} redundancy ${params}`) 265 const stdout = await cliCommand.execWithEnv(`${cmd} redundancy ${params}`)
303 266
304 expect(stdout).to.not.contain('super video') 267 expect(stdout).to.not.contain('super video')
305 } 268 }
diff --git a/server/tests/cli/plugins.ts b/server/tests/cli/plugins.ts
index 7f19f14b7..07c78cc89 100644
--- a/server/tests/cli/plugins.ts
+++ b/server/tests/cli/plugins.ts
@@ -1,55 +1,47 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai'
4import { 5import {
5 cleanupTests, 6 cleanupTests,
6 execCLI, 7 createSingleServer,
7 flushAndRunServer,
8 getConfig,
9 getEnvCli,
10 getPluginTestPath,
11 killallServers, 8 killallServers,
12 reRunServer, 9 PeerTubeServer,
13 ServerInfo, 10 PluginsCommand,
14 setAccessTokensToServers 11 setAccessTokensToServers
15} from '../../../shared/extra-utils' 12} from '../../../shared/extra-utils'
16import { ServerConfig } from '../../../shared/models/server'
17import { expect } from 'chai'
18 13
19describe('Test plugin scripts', function () { 14describe('Test plugin scripts', function () {
20 let server: ServerInfo 15 let server: PeerTubeServer
21 16
22 before(async function () { 17 before(async function () {
23 this.timeout(30000) 18 this.timeout(30000)
24 19
25 server = await flushAndRunServer(1) 20 server = await createSingleServer(1)
26 await setAccessTokensToServers([ server ]) 21 await setAccessTokensToServers([ server ])
27 }) 22 })
28 23
29 it('Should install a plugin from stateless CLI', async function () { 24 it('Should install a plugin from stateless CLI', async function () {
30 this.timeout(60000) 25 this.timeout(60000)
31 26
32 const packagePath = getPluginTestPath() 27 const packagePath = PluginsCommand.getPluginTestPath()
33 28
34 const env = getEnvCli(server) 29 await server.cli.execWithEnv(`npm run plugin:install -- --plugin-path ${packagePath}`)
35 await execCLI(`${env} npm run plugin:install -- --plugin-path ${packagePath}`)
36 }) 30 })
37 31
38 it('Should install a theme from stateless CLI', async function () { 32 it('Should install a theme from stateless CLI', async function () {
39 this.timeout(60000) 33 this.timeout(60000)
40 34
41 const env = getEnvCli(server) 35 await server.cli.execWithEnv(`npm run plugin:install -- --npm-name peertube-theme-background-red`)
42 await execCLI(`${env} npm run plugin:install -- --npm-name peertube-theme-background-red`)
43 }) 36 })
44 37
45 it('Should have the theme and the plugin registered when we restart peertube', async function () { 38 it('Should have the theme and the plugin registered when we restart peertube', async function () {
46 this.timeout(30000) 39 this.timeout(30000)
47 40
48 killallServers([ server ]) 41 await killallServers([ server ])
49 await reRunServer(server) 42 await server.run()
50 43
51 const res = await getConfig(server.url) 44 const config = await server.config.getConfig()
52 const config: ServerConfig = res.body
53 45
54 const plugin = config.plugin.registered 46 const plugin = config.plugin.registered
55 .find(p => p.name === 'test') 47 .find(p => p.name === 'test')
@@ -63,18 +55,16 @@ describe('Test plugin scripts', function () {
63 it('Should uninstall a plugin from stateless CLI', async function () { 55 it('Should uninstall a plugin from stateless CLI', async function () {
64 this.timeout(60000) 56 this.timeout(60000)
65 57
66 const env = getEnvCli(server) 58 await server.cli.execWithEnv(`npm run plugin:uninstall -- --npm-name peertube-plugin-test`)
67 await execCLI(`${env} npm run plugin:uninstall -- --npm-name peertube-plugin-test`)
68 }) 59 })
69 60
70 it('Should have removed the plugin on another peertube restart', async function () { 61 it('Should have removed the plugin on another peertube restart', async function () {
71 this.timeout(30000) 62 this.timeout(30000)
72 63
73 killallServers([ server ]) 64 await killallServers([ server ])
74 await reRunServer(server) 65 await server.run()
75 66
76 const res = await getConfig(server.url) 67 const config = await server.config.getConfig()
77 const config: ServerConfig = res.body
78 68
79 const plugin = config.plugin.registered 69 const plugin = config.plugin.registered
80 .find(p => p.name === 'test') 70 .find(p => p.name === 'test')
diff --git a/server/tests/cli/print-transcode-command.ts b/server/tests/cli/print-transcode-command.ts
index 2d7255db7..3a7969e68 100644
--- a/server/tests/cli/print-transcode-command.ts
+++ b/server/tests/cli/print-transcode-command.ts
@@ -2,14 +2,15 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { execCLI } from '../../../shared/extra-utils' 5import { getVideoFileBitrate, getVideoFileFPS } from '@server/helpers/ffprobe-utils'
6import { CLICommand } from '@shared/extra-utils'
6import { getTargetBitrate, VideoResolution } from '../../../shared/models/videos' 7import { getTargetBitrate, VideoResolution } from '../../../shared/models/videos'
7import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants' 8import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants'
8import { getVideoFileBitrate, getVideoFileFPS } from '@server/helpers/ffprobe-utils'
9 9
10const expect = chai.expect 10const expect = chai.expect
11 11
12describe('Test create transcoding jobs', function () { 12describe('Test create transcoding jobs', function () {
13
13 it('Should print the correct command for each resolution', async function () { 14 it('Should print the correct command for each resolution', async function () {
14 const fixturePath = 'server/tests/fixtures/video_short.webm' 15 const fixturePath = 'server/tests/fixtures/video_short.webm'
15 const fps = await getVideoFileFPS(fixturePath) 16 const fps = await getVideoFileFPS(fixturePath)
@@ -19,7 +20,7 @@ describe('Test create transcoding jobs', function () {
19 VideoResolution.H_720P, 20 VideoResolution.H_720P,
20 VideoResolution.H_1080P 21 VideoResolution.H_1080P
21 ]) { 22 ]) {
22 const command = await execCLI(`npm run print-transcode-command -- ${fixturePath} -r ${resolution}`) 23 const command = await CLICommand.exec(`npm run print-transcode-command -- ${fixturePath} -r ${resolution}`)
23 const targetBitrate = Math.min(getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS), bitrate) 24 const targetBitrate = Math.min(getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS), bitrate)
24 25
25 expect(command).to.includes(`-vf scale=w=-2:h=${resolution}`) 26 expect(command).to.includes(`-vf scale=w=-2:h=${resolution}`)
diff --git a/server/tests/cli/prune-storage.ts b/server/tests/cli/prune-storage.ts
index a0af09de8..2d4c02da7 100644
--- a/server/tests/cli/prune-storage.ts
+++ b/server/tests/cli/prune-storage.ts
@@ -5,87 +5,89 @@ import * as chai from 'chai'
5import { createFile, readdir } from 'fs-extra' 5import { createFile, readdir } from 'fs-extra'
6import { join } from 'path' 6import { join } from 'path'
7import { buildUUID } from '@server/helpers/uuid' 7import { buildUUID } from '@server/helpers/uuid'
8import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
9import { 8import {
10 buildServerDirectory,
11 cleanupTests, 9 cleanupTests,
12 createVideoPlaylist, 10 CLICommand,
11 createMultipleServers,
13 doubleFollow, 12 doubleFollow,
14 execCLI,
15 flushAndRunMultipleServers,
16 getAccount,
17 getEnvCli,
18 killallServers, 13 killallServers,
19 makeGetRequest, 14 makeGetRequest,
20 ServerInfo, 15 PeerTubeServer,
21 setAccessTokensToServers, 16 setAccessTokensToServers,
22 setDefaultVideoChannel, 17 setDefaultVideoChannel,
23 updateMyAvatar, 18 wait,
24 uploadVideo, 19 waitJobs
25 wait 20} from '@shared/extra-utils'
26} from '../../../shared/extra-utils' 21import { HttpStatusCode, VideoPlaylistPrivacy } from '@shared/models'
27import { waitJobs } from '../../../shared/extra-utils/server/jobs'
28import { Account, VideoPlaylistPrivacy } from '../../../shared/models'
29 22
30const expect = chai.expect 23const expect = chai.expect
31 24
32async function countFiles (internalServerNumber: number, directory: string) { 25async function countFiles (server: PeerTubeServer, directory: string) {
33 const files = await readdir(buildServerDirectory({ internalServerNumber }, directory)) 26 const files = await readdir(server.servers.buildDirectory(directory))
34 27
35 return files.length 28 return files.length
36} 29}
37 30
38async function assertNotExists (internalServerNumber: number, directory: string, substring: string) { 31async function assertNotExists (server: PeerTubeServer, directory: string, substring: string) {
39 const files = await readdir(buildServerDirectory({ internalServerNumber }, directory)) 32 const files = await readdir(server.servers.buildDirectory(directory))
40 33
41 for (const f of files) { 34 for (const f of files) {
42 expect(f).to.not.contain(substring) 35 expect(f).to.not.contain(substring)
43 } 36 }
44} 37}
45 38
46async function assertCountAreOkay (servers: ServerInfo[]) { 39async function assertCountAreOkay (servers: PeerTubeServer[], videoServer2UUID: string) {
47 for (const server of servers) { 40 for (const server of servers) {
48 const videosCount = await countFiles(server.internalServerNumber, 'videos') 41 const videosCount = await countFiles(server, 'videos')
49 expect(videosCount).to.equal(8) 42 expect(videosCount).to.equal(8)
50 43
51 const torrentsCount = await countFiles(server.internalServerNumber, 'torrents') 44 const torrentsCount = await countFiles(server, 'torrents')
52 expect(torrentsCount).to.equal(16) 45 expect(torrentsCount).to.equal(16)
53 46
54 const previewsCount = await countFiles(server.internalServerNumber, 'previews') 47 const previewsCount = await countFiles(server, 'previews')
55 expect(previewsCount).to.equal(2) 48 expect(previewsCount).to.equal(2)
56 49
57 const thumbnailsCount = await countFiles(server.internalServerNumber, 'thumbnails') 50 const thumbnailsCount = await countFiles(server, 'thumbnails')
58 expect(thumbnailsCount).to.equal(6) 51 expect(thumbnailsCount).to.equal(6)
59 52
60 const avatarsCount = await countFiles(server.internalServerNumber, 'avatars') 53 const avatarsCount = await countFiles(server, 'avatars')
61 expect(avatarsCount).to.equal(2) 54 expect(avatarsCount).to.equal(2)
62 } 55 }
56
57 // When we'll prune HLS directories too
58 // const hlsRootCount = await countFiles(servers[1], 'streaming-playlists/hls/')
59 // expect(hlsRootCount).to.equal(2)
60
61 // const hlsCount = await countFiles(servers[1], 'streaming-playlists/hls/' + videoServer2UUID)
62 // expect(hlsCount).to.equal(10)
63} 63}
64 64
65describe('Test prune storage scripts', function () { 65describe('Test prune storage scripts', function () {
66 let servers: ServerInfo[] 66 let servers: PeerTubeServer[]
67 const badNames: { [directory: string]: string[] } = {} 67 const badNames: { [directory: string]: string[] } = {}
68 68
69 let videoServer2UUID: string
70
69 before(async function () { 71 before(async function () {
70 this.timeout(120000) 72 this.timeout(120000)
71 73
72 servers = await flushAndRunMultipleServers(2, { transcoding: { enabled: true } }) 74 servers = await createMultipleServers(2, { transcoding: { enabled: true } })
73 await setAccessTokensToServers(servers) 75 await setAccessTokensToServers(servers)
74 await setDefaultVideoChannel(servers) 76 await setDefaultVideoChannel(servers)
75 77
76 for (const server of servers) { 78 for (const server of servers) {
77 await uploadVideo(server.url, server.accessToken, { name: 'video 1' }) 79 await server.videos.upload({ attributes: { name: 'video 1' } })
78 await uploadVideo(server.url, server.accessToken, { name: 'video 2' })
79 80
80 await updateMyAvatar({ url: server.url, accessToken: server.accessToken, fixture: 'avatar.png' }) 81 const { uuid } = await server.videos.upload({ attributes: { name: 'video 2' } })
82 if (server.serverNumber === 2) videoServer2UUID = uuid
81 83
82 await createVideoPlaylist({ 84 await server.users.updateMyAvatar({ fixture: 'avatar.png' })
83 url: server.url, 85
84 token: server.accessToken, 86 await server.playlists.create({
85 playlistAttrs: { 87 attributes: {
86 displayName: 'playlist', 88 displayName: 'playlist',
87 privacy: VideoPlaylistPrivacy.PUBLIC, 89 privacy: VideoPlaylistPrivacy.PUBLIC,
88 videoChannelId: server.videoChannel.id, 90 videoChannelId: server.store.channel.id,
89 thumbnailfile: 'thumbnail.jpg' 91 thumbnailfile: 'thumbnail.jpg'
90 } 92 }
91 }) 93 })
@@ -95,41 +97,39 @@ describe('Test prune storage scripts', function () {
95 97
96 // Lazy load the remote avatar 98 // Lazy load the remote avatar
97 { 99 {
98 const res = await getAccount(servers[0].url, 'root@localhost:' + servers[1].port) 100 const account = await servers[0].accounts.get({ accountName: 'root@localhost:' + servers[1].port })
99 const account: Account = res.body
100 await makeGetRequest({ 101 await makeGetRequest({
101 url: servers[0].url, 102 url: servers[0].url,
102 path: account.avatar.path, 103 path: account.avatar.path,
103 statusCodeExpected: HttpStatusCode.OK_200 104 expectedStatus: HttpStatusCode.OK_200
104 }) 105 })
105 } 106 }
106 107
107 { 108 {
108 const res = await getAccount(servers[1].url, 'root@localhost:' + servers[0].port) 109 const account = await servers[1].accounts.get({ accountName: 'root@localhost:' + servers[0].port })
109 const account: Account = res.body
110 await makeGetRequest({ 110 await makeGetRequest({
111 url: servers[1].url, 111 url: servers[1].url,
112 path: account.avatar.path, 112 path: account.avatar.path,
113 statusCodeExpected: HttpStatusCode.OK_200 113 expectedStatus: HttpStatusCode.OK_200
114 }) 114 })
115 } 115 }
116 116
117 await wait(1000) 117 await wait(1000)
118 118
119 await waitJobs(servers) 119 await waitJobs(servers)
120 killallServers(servers) 120 await killallServers(servers)
121 121
122 await wait(1000) 122 await wait(1000)
123 }) 123 })
124 124
125 it('Should have the files on the disk', async function () { 125 it('Should have the files on the disk', async function () {
126 await assertCountAreOkay(servers) 126 await assertCountAreOkay(servers, videoServer2UUID)
127 }) 127 })
128 128
129 it('Should create some dirty files', async function () { 129 it('Should create some dirty files', async function () {
130 for (let i = 0; i < 2; i++) { 130 for (let i = 0; i < 2; i++) {
131 { 131 {
132 const base = buildServerDirectory(servers[0], 'videos') 132 const base = servers[0].servers.buildDirectory('videos')
133 133
134 const n1 = buildUUID() + '.mp4' 134 const n1 = buildUUID() + '.mp4'
135 const n2 = buildUUID() + '.webm' 135 const n2 = buildUUID() + '.webm'
@@ -141,7 +141,7 @@ describe('Test prune storage scripts', function () {
141 } 141 }
142 142
143 { 143 {
144 const base = buildServerDirectory(servers[0], 'torrents') 144 const base = servers[0].servers.buildDirectory('torrents')
145 145
146 const n1 = buildUUID() + '-240.torrent' 146 const n1 = buildUUID() + '-240.torrent'
147 const n2 = buildUUID() + '-480.torrent' 147 const n2 = buildUUID() + '-480.torrent'
@@ -153,7 +153,7 @@ describe('Test prune storage scripts', function () {
153 } 153 }
154 154
155 { 155 {
156 const base = buildServerDirectory(servers[0], 'thumbnails') 156 const base = servers[0].servers.buildDirectory('thumbnails')
157 157
158 const n1 = buildUUID() + '.jpg' 158 const n1 = buildUUID() + '.jpg'
159 const n2 = buildUUID() + '.jpg' 159 const n2 = buildUUID() + '.jpg'
@@ -165,7 +165,7 @@ describe('Test prune storage scripts', function () {
165 } 165 }
166 166
167 { 167 {
168 const base = buildServerDirectory(servers[0], 'previews') 168 const base = servers[0].servers.buildDirectory('previews')
169 169
170 const n1 = buildUUID() + '.jpg' 170 const n1 = buildUUID() + '.jpg'
171 const n2 = buildUUID() + '.jpg' 171 const n2 = buildUUID() + '.jpg'
@@ -177,7 +177,7 @@ describe('Test prune storage scripts', function () {
177 } 177 }
178 178
179 { 179 {
180 const base = buildServerDirectory(servers[0], 'avatars') 180 const base = servers[0].servers.buildDirectory('avatars')
181 181
182 const n1 = buildUUID() + '.png' 182 const n1 = buildUUID() + '.png'
183 const n2 = buildUUID() + '.jpg' 183 const n2 = buildUUID() + '.jpg'
@@ -187,22 +187,44 @@ describe('Test prune storage scripts', function () {
187 187
188 badNames['avatars'] = [ n1, n2 ] 188 badNames['avatars'] = [ n1, n2 ]
189 } 189 }
190
191 // When we'll prune HLS directories too
192 // {
193 // const directory = join('streaming-playlists', 'hls')
194 // const base = servers[1].servers.buildDirectory(directory)
195
196 // const n1 = buildUUID()
197 // await createFile(join(base, n1))
198 // badNames[directory] = [ n1 ]
199 // }
200
201 // {
202 // const directory = join('streaming-playlists', 'hls', videoServer2UUID)
203 // const base = servers[1].servers.buildDirectory(directory)
204 // const n1 = buildUUID() + '-240-fragmented-.mp4'
205 // const n2 = buildUUID() + '-master.m3u8'
206
207 // await createFile(join(base, n1))
208 // await createFile(join(base, n2))
209
210 // badNames[directory] = [ n1, n2 ]
211 // }
190 } 212 }
191 }) 213 })
192 214
193 it('Should run prune storage', async function () { 215 it('Should run prune storage', async function () {
194 this.timeout(30000) 216 this.timeout(30000)
195 217
196 const env = getEnvCli(servers[0]) 218 const env = servers[0].cli.getEnv()
197 await execCLI(`echo y | ${env} npm run prune-storage`) 219 await CLICommand.exec(`echo y | ${env} npm run prune-storage`)
198 }) 220 })
199 221
200 it('Should have removed files', async function () { 222 it('Should have removed files', async function () {
201 await assertCountAreOkay(servers) 223 await assertCountAreOkay(servers, videoServer2UUID)
202 224
203 for (const directory of Object.keys(badNames)) { 225 for (const directory of Object.keys(badNames)) {
204 for (const name of badNames[directory]) { 226 for (const name of badNames[directory]) {
205 await assertNotExists(servers[0].internalServerNumber, directory, name) 227 await assertNotExists(servers[0], directory, name)
206 } 228 }
207 } 229 }
208 }) 230 })
diff --git a/server/tests/cli/regenerate-thumbnails.ts b/server/tests/cli/regenerate-thumbnails.ts
index 8acb9f263..780c9b4bd 100644
--- a/server/tests/cli/regenerate-thumbnails.ts
+++ b/server/tests/cli/regenerate-thumbnails.ts
@@ -2,36 +2,33 @@ import 'mocha'
2import { expect } from 'chai' 2import { expect } from 'chai'
3import { writeFile } from 'fs-extra' 3import { writeFile } from 'fs-extra'
4import { basename, join } from 'path' 4import { basename, join } from 'path'
5import { Video, VideoDetails } from '@shared/models' 5import { HttpStatusCode, Video } from '@shared/models'
6import { 6import {
7 buildServerDirectory,
8 cleanupTests, 7 cleanupTests,
8 createMultipleServers,
9 doubleFollow, 9 doubleFollow,
10 execCLI,
11 flushAndRunMultipleServers,
12 getEnvCli,
13 getVideo,
14 makeRawRequest, 10 makeRawRequest,
15 ServerInfo, 11 PeerTubeServer,
16 setAccessTokensToServers, 12 setAccessTokensToServers,
17 uploadVideoAndGetId,
18 waitJobs 13 waitJobs
19} from '../../../shared/extra-utils' 14} from '../../../shared/extra-utils'
20import { HttpStatusCode } from '@shared/core-utils'
21 15
22async function testThumbnail (server: ServerInfo, videoId: number | string) { 16async function testThumbnail (server: PeerTubeServer, videoId: number | string) {
23 const res = await getVideo(server.url, videoId) 17 const video = await server.videos.get({ id: videoId })
24 const video: VideoDetails = res.body
25 18
26 const res1 = await makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200) 19 const requests = [
27 expect(res1.body).to.not.have.lengthOf(0) 20 makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200),
21 makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200)
22 ]
28 23
29 const res2 = await makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200) 24 for (const req of requests) {
30 expect(res2.body).to.not.have.lengthOf(0) 25 const res = await req
26 expect(res.body).to.not.have.lengthOf(0)
27 }
31} 28}
32 29
33describe('Test regenerate thumbnails script', function () { 30describe('Test regenerate thumbnails script', function () {
34 let servers: ServerInfo[] 31 let servers: PeerTubeServer[]
35 32
36 let video1: Video 33 let video1: Video
37 let video2: Video 34 let video2: Video
@@ -43,28 +40,28 @@ describe('Test regenerate thumbnails script', function () {
43 before(async function () { 40 before(async function () {
44 this.timeout(60000) 41 this.timeout(60000)
45 42
46 servers = await flushAndRunMultipleServers(2) 43 servers = await createMultipleServers(2)
47 await setAccessTokensToServers(servers) 44 await setAccessTokensToServers(servers)
48 45
49 await doubleFollow(servers[0], servers[1]) 46 await doubleFollow(servers[0], servers[1])
50 47
51 { 48 {
52 const videoUUID1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 1' })).uuid 49 const videoUUID1 = (await servers[0].videos.quickUpload({ name: 'video 1' })).uuid
53 video1 = await (getVideo(servers[0].url, videoUUID1).then(res => res.body)) 50 video1 = await servers[0].videos.get({ id: videoUUID1 })
54 51
55 thumbnail1Path = join(buildServerDirectory(servers[0], 'thumbnails'), basename(video1.thumbnailPath)) 52 thumbnail1Path = join(servers[0].servers.buildDirectory('thumbnails'), basename(video1.thumbnailPath))
56 53
57 const videoUUID2 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 2' })).uuid 54 const videoUUID2 = (await servers[0].videos.quickUpload({ name: 'video 2' })).uuid
58 video2 = await (getVideo(servers[0].url, videoUUID2).then(res => res.body)) 55 video2 = await servers[0].videos.get({ id: videoUUID2 })
59 } 56 }
60 57
61 { 58 {
62 const videoUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 3' })).uuid 59 const videoUUID = (await servers[1].videos.quickUpload({ name: 'video 3' })).uuid
63 await waitJobs(servers) 60 await waitJobs(servers)
64 61
65 remoteVideo = await (getVideo(servers[0].url, videoUUID).then(res => res.body)) 62 remoteVideo = await servers[0].videos.get({ id: videoUUID })
66 63
67 thumbnailRemotePath = join(buildServerDirectory(servers[0], 'thumbnails'), basename(remoteVideo.thumbnailPath)) 64 thumbnailRemotePath = join(servers[0].servers.buildDirectory('thumbnails'), basename(remoteVideo.thumbnailPath))
68 } 65 }
69 66
70 await writeFile(thumbnail1Path, '') 67 await writeFile(thumbnail1Path, '')
@@ -91,8 +88,7 @@ describe('Test regenerate thumbnails script', function () {
91 it('Should regenerate local thumbnails from the CLI', async function () { 88 it('Should regenerate local thumbnails from the CLI', async function () {
92 this.timeout(15000) 89 this.timeout(15000)
93 90
94 const env = getEnvCli(servers[0]) 91 await servers[0].cli.execWithEnv(`npm run regenerate-thumbnails`)
95 await execCLI(`${env} npm run regenerate-thumbnails`)
96 }) 92 })
97 93
98 it('Should have generated new thumbnail files', async function () { 94 it('Should have generated new thumbnail files', async function () {
diff --git a/server/tests/cli/reset-password.ts b/server/tests/cli/reset-password.ts
index a84463b33..4a02db35d 100644
--- a/server/tests/cli/reset-password.ts
+++ b/server/tests/cli/reset-password.ts
@@ -1,35 +1,24 @@
1import 'mocha' 1import 'mocha'
2 2import { cleanupTests, CLICommand, createSingleServer, PeerTubeServer, setAccessTokensToServers } from '../../../shared/extra-utils'
3import {
4 cleanupTests,
5 createUser,
6 execCLI,
7 flushAndRunServer,
8 getEnvCli,
9 login,
10 ServerInfo,
11 setAccessTokensToServers
12} from '../../../shared/extra-utils'
13import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
14 3
15describe('Test reset password scripts', function () { 4describe('Test reset password scripts', function () {
16 let server: ServerInfo 5 let server: PeerTubeServer
17 6
18 before(async function () { 7 before(async function () {
19 this.timeout(30000) 8 this.timeout(30000)
20 server = await flushAndRunServer(1) 9 server = await createSingleServer(1)
21 await setAccessTokensToServers([ server ]) 10 await setAccessTokensToServers([ server ])
22 11
23 await createUser({ url: server.url, accessToken: server.accessToken, username: 'user_1', password: 'super password' }) 12 await server.users.create({ username: 'user_1', password: 'super password' })
24 }) 13 })
25 14
26 it('Should change the user password from CLI', async function () { 15 it('Should change the user password from CLI', async function () {
27 this.timeout(60000) 16 this.timeout(60000)
28 17
29 const env = getEnvCli(server) 18 const env = server.cli.getEnv()
30 await execCLI(`echo coucou | ${env} npm run reset-password -- -u user_1`) 19 await CLICommand.exec(`echo coucou | ${env} npm run reset-password -- -u user_1`)
31 20
32 await login(server.url, server.client, { username: 'user_1', password: 'coucou' }, HttpStatusCode.OK_200) 21 await server.login.login({ user: { username: 'user_1', password: 'coucou' } })
33 }) 22 })
34 23
35 after(async function () { 24 after(async function () {
diff --git a/server/tests/cli/update-host.ts b/server/tests/cli/update-host.ts
index 2070f16f5..43fbaec30 100644
--- a/server/tests/cli/update-host.ts
+++ b/server/tests/cli/update-host.ts
@@ -1,33 +1,20 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import { expect } from 'chai'
5import { VideoDetails } from '../../../shared/models/videos'
6import { waitJobs } from '../../../shared/extra-utils/server/jobs'
7import { addVideoCommentThread } from '../../../shared/extra-utils/videos/video-comments'
8import { 5import {
9 addVideoChannel,
10 cleanupTests, 6 cleanupTests,
11 createUser, 7 createSingleServer,
12 execCLI,
13 flushAndRunServer,
14 getEnvCli,
15 getVideo,
16 getVideoChannelsList,
17 getVideosList,
18 killallServers, 8 killallServers,
19 makeActivityPubGetRequest, 9 makeActivityPubGetRequest,
20 parseTorrentVideo, reRunServer, 10 parseTorrentVideo,
21 ServerInfo, 11 PeerTubeServer,
22 setAccessTokensToServers, 12 setAccessTokensToServers,
23 uploadVideo 13 waitJobs
24} from '../../../shared/extra-utils' 14} from '@shared/extra-utils'
25import { getAccountsList } from '../../../shared/extra-utils/users/accounts'
26
27const expect = chai.expect
28 15
29describe('Test update host scripts', function () { 16describe('Test update host scripts', function () {
30 let server: ServerInfo 17 let server: PeerTubeServer
31 18
32 before(async function () { 19 before(async function () {
33 this.timeout(60000) 20 this.timeout(60000)
@@ -38,17 +25,15 @@ describe('Test update host scripts', function () {
38 } 25 }
39 } 26 }
40 // Run server 2 to have transcoding enabled 27 // Run server 2 to have transcoding enabled
41 server = await flushAndRunServer(2, overrideConfig) 28 server = await createSingleServer(2, overrideConfig)
42 await setAccessTokensToServers([ server ]) 29 await setAccessTokensToServers([ server ])
43 30
44 // Upload two videos for our needs 31 // Upload two videos for our needs
45 const videoAttributes = {} 32 const { uuid: video1UUID } = await server.videos.upload()
46 const resVideo1 = await uploadVideo(server.url, server.accessToken, videoAttributes) 33 await server.videos.upload()
47 const video1UUID = resVideo1.body.video.uuid
48 await uploadVideo(server.url, server.accessToken, videoAttributes)
49 34
50 // Create a user 35 // Create a user
51 await createUser({ url: server.url, accessToken: server.accessToken, username: 'toto', password: 'coucou' }) 36 await server.users.create({ username: 'toto', password: 'coucou' })
52 37
53 // Create channel 38 // Create channel
54 const videoChannel = { 39 const videoChannel = {
@@ -56,11 +41,11 @@ describe('Test update host scripts', function () {
56 displayName: 'second video channel', 41 displayName: 'second video channel',
57 description: 'super video channel description' 42 description: 'super video channel description'
58 } 43 }
59 await addVideoChannel(server.url, server.accessToken, videoChannel) 44 await server.channels.create({ attributes: videoChannel })
60 45
61 // Create comments 46 // Create comments
62 const text = 'my super first comment' 47 const text = 'my super first comment'
63 await addVideoCommentThread(server.url, server.accessToken, video1UUID, text) 48 await server.comments.createThread({ videoId: video1UUID, text })
64 49
65 await waitJobs(server) 50 await waitJobs(server)
66 }) 51 })
@@ -68,25 +53,23 @@ describe('Test update host scripts', function () {
68 it('Should run update host', async function () { 53 it('Should run update host', async function () {
69 this.timeout(30000) 54 this.timeout(30000)
70 55
71 killallServers([ server ]) 56 await killallServers([ server ])
72 // Run server with standard configuration 57 // Run server with standard configuration
73 await reRunServer(server) 58 await server.run()
74 59
75 const env = getEnvCli(server) 60 await server.cli.execWithEnv(`npm run update-host`)
76 await execCLI(`${env} npm run update-host`)
77 }) 61 })
78 62
79 it('Should have updated videos url', async function () { 63 it('Should have updated videos url', async function () {
80 const res = await getVideosList(server.url) 64 const { total, data } = await server.videos.list()
81 expect(res.body.total).to.equal(2) 65 expect(total).to.equal(2)
82 66
83 for (const video of res.body.data) { 67 for (const video of data) {
84 const { body } = await makeActivityPubGetRequest(server.url, '/videos/watch/' + video.uuid) 68 const { body } = await makeActivityPubGetRequest(server.url, '/videos/watch/' + video.uuid)
85 69
86 expect(body.id).to.equal('http://localhost:9002/videos/watch/' + video.uuid) 70 expect(body.id).to.equal('http://localhost:9002/videos/watch/' + video.uuid)
87 71
88 const res = await getVideo(server.url, video.uuid) 72 const videoDetails = await server.videos.get({ id: video.uuid })
89 const videoDetails: VideoDetails = res.body
90 73
91 expect(videoDetails.trackerUrls[0]).to.include(server.host) 74 expect(videoDetails.trackerUrls[0]).to.include(server.host)
92 expect(videoDetails.streamingPlaylists[0].playlistUrl).to.include(server.host) 75 expect(videoDetails.streamingPlaylists[0].playlistUrl).to.include(server.host)
@@ -95,10 +78,10 @@ describe('Test update host scripts', function () {
95 }) 78 })
96 79
97 it('Should have updated video channels url', async function () { 80 it('Should have updated video channels url', async function () {
98 const res = await getVideoChannelsList(server.url, 0, 5, '-name') 81 const { data, total } = await server.channels.list({ sort: '-name' })
99 expect(res.body.total).to.equal(3) 82 expect(total).to.equal(3)
100 83
101 for (const channel of res.body.data) { 84 for (const channel of data) {
102 const { body } = await makeActivityPubGetRequest(server.url, '/video-channels/' + channel.name) 85 const { body } = await makeActivityPubGetRequest(server.url, '/video-channels/' + channel.name)
103 86
104 expect(body.id).to.equal('http://localhost:9002/video-channels/' + channel.name) 87 expect(body.id).to.equal('http://localhost:9002/video-channels/' + channel.name)
@@ -106,10 +89,10 @@ describe('Test update host scripts', function () {
106 }) 89 })
107 90
108 it('Should have updated accounts url', async function () { 91 it('Should have updated accounts url', async function () {
109 const res = await getAccountsList(server.url) 92 const body = await server.accounts.list()
110 expect(res.body.total).to.equal(3) 93 expect(body.total).to.equal(3)
111 94
112 for (const account of res.body.data) { 95 for (const account of body.data) {
113 const usernameWithDomain = account.name 96 const usernameWithDomain = account.name
114 const { body } = await makeActivityPubGetRequest(server.url, '/accounts/' + usernameWithDomain) 97 const { body } = await makeActivityPubGetRequest(server.url, '/accounts/' + usernameWithDomain)
115 98
@@ -120,28 +103,27 @@ describe('Test update host scripts', function () {
120 it('Should have updated torrent hosts', async function () { 103 it('Should have updated torrent hosts', async function () {
121 this.timeout(30000) 104 this.timeout(30000)
122 105
123 const res = await getVideosList(server.url) 106 const { data } = await server.videos.list()
124 const videos = res.body.data 107 expect(data).to.have.lengthOf(2)
125 expect(videos).to.have.lengthOf(2)
126 108
127 for (const video of videos) { 109 for (const video of data) {
128 const res2 = await getVideo(server.url, video.id) 110 const videoDetails = await server.videos.get({ id: video.id })
129 const videoDetails: VideoDetails = res2.body 111 const files = videoDetails.files.concat(videoDetails.streamingPlaylists[0].files)
130 112
131 expect(videoDetails.files).to.have.lengthOf(4) 113 expect(files).to.have.lengthOf(8)
132 114
133 for (const file of videoDetails.files) { 115 for (const file of files) {
134 expect(file.magnetUri).to.contain('localhost%3A9002%2Ftracker%2Fsocket') 116 expect(file.magnetUri).to.contain('localhost%3A9002%2Ftracker%2Fsocket')
135 expect(file.magnetUri).to.contain('localhost%3A9002%2Fstatic%2Fwebseed%2F') 117 expect(file.magnetUri).to.contain('localhost%3A9002%2Fstatic%2F')
136 118
137 const torrent = await parseTorrentVideo(server, videoDetails.uuid, file.resolution.id) 119 const torrent = await parseTorrentVideo(server, file)
138 const announceWS = torrent.announce.find(a => a === 'ws://localhost:9002/tracker/socket') 120 const announceWS = torrent.announce.find(a => a === 'ws://localhost:9002/tracker/socket')
139 expect(announceWS).to.not.be.undefined 121 expect(announceWS).to.not.be.undefined
140 122
141 const announceHttp = torrent.announce.find(a => a === 'http://localhost:9002/tracker/announce') 123 const announceHttp = torrent.announce.find(a => a === 'http://localhost:9002/tracker/announce')
142 expect(announceHttp).to.not.be.undefined 124 expect(announceHttp).to.not.be.undefined
143 125
144 expect(torrent.urlList[0]).to.contain('http://localhost:9002/static/webseed') 126 expect(torrent.urlList[0]).to.contain('http://localhost:9002/static/')
145 } 127 }
146 } 128 }
147 }) 129 })
diff --git a/server/tests/client.ts b/server/tests/client.ts
index 7c4fb4e46..4cbdb2cb3 100644
--- a/server/tests/client.ts
+++ b/server/tests/client.ts
@@ -3,28 +3,16 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { omit } from 'lodash' 5import { omit } from 'lodash'
6import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 6import { Account, HTMLServerConfig, HttpStatusCode, ServerConfig, VideoPlaylistCreateResult, VideoPlaylistPrivacy } from '@shared/models'
7import { Account, CustomConfig, HTMLServerConfig, ServerConfig, VideoPlaylistCreateResult, VideoPlaylistPrivacy } from '@shared/models'
8import { 7import {
9 addVideoInPlaylist,
10 cleanupTests, 8 cleanupTests,
11 createVideoPlaylist, 9 createMultipleServers,
12 doubleFollow, 10 doubleFollow,
13 flushAndRunMultipleServers,
14 getAccount,
15 getConfig,
16 getCustomConfig,
17 getVideosList,
18 makeGetRequest, 11 makeGetRequest,
19 makeHTMLRequest, 12 makeHTMLRequest,
20 ServerInfo, 13 PeerTubeServer,
21 setAccessTokensToServers, 14 setAccessTokensToServers,
22 setDefaultVideoChannel, 15 setDefaultVideoChannel,
23 updateCustomConfig,
24 updateCustomSubConfig,
25 updateMyUser,
26 updateVideoChannel,
27 uploadVideo,
28 waitJobs 16 waitJobs
29} from '../../shared/extra-utils' 17} from '../../shared/extra-utils'
30 18
@@ -40,7 +28,7 @@ function checkIndexTags (html: string, title: string, description: string, css:
40} 28}
41 29
42describe('Test a client controllers', function () { 30describe('Test a client controllers', function () {
43 let servers: ServerInfo[] = [] 31 let servers: PeerTubeServer[] = []
44 let account: Account 32 let account: Account
45 33
46 const videoName = 'my super name for server 1' 34 const videoName = 'my super name for server 1'
@@ -62,7 +50,7 @@ describe('Test a client controllers', function () {
62 before(async function () { 50 before(async function () {
63 this.timeout(120000) 51 this.timeout(120000)
64 52
65 servers = await flushAndRunMultipleServers(2) 53 servers = await createMultipleServers(2)
66 54
67 await setAccessTokensToServers(servers) 55 await setAccessTokensToServers(servers)
68 56
@@ -70,47 +58,48 @@ describe('Test a client controllers', function () {
70 58
71 await setDefaultVideoChannel(servers) 59 await setDefaultVideoChannel(servers)
72 60
73 await updateVideoChannel(servers[0].url, servers[0].accessToken, servers[0].videoChannel.name, { description: channelDescription }) 61 await servers[0].channels.update({
62 channelName: servers[0].store.channel.name,
63 attributes: { description: channelDescription }
64 })
74 65
75 // Video 66 // Video
76 67
77 const videoAttributes = { name: videoName, description: videoDescription } 68 {
78 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 69 const attributes = { name: videoName, description: videoDescription }
70 await servers[0].videos.upload({ attributes })
79 71
80 const resVideosRequest = await getVideosList(servers[0].url) 72 const { data } = await servers[0].videos.list()
81 const videos = resVideosRequest.body.data 73 expect(data.length).to.equal(1)
82 expect(videos.length).to.equal(1)
83 74
84 const video = videos[0] 75 const video = data[0]
85 servers[0].video = video 76 servers[0].store.video = video
86 videoIds = [ video.id, video.uuid, video.shortUUID ] 77 videoIds = [ video.id, video.uuid, video.shortUUID ]
78 }
87 79
88 // Playlist 80 // Playlist
89 81
90 const playlistAttrs = { 82 {
91 displayName: playlistName, 83 const attributes = {
92 description: playlistDescription, 84 displayName: playlistName,
93 privacy: VideoPlaylistPrivacy.PUBLIC, 85 description: playlistDescription,
94 videoChannelId: servers[0].videoChannel.id 86 privacy: VideoPlaylistPrivacy.PUBLIC,
95 } 87 videoChannelId: servers[0].store.channel.id
88 }
96 89
97 const resVideoPlaylistRequest = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs }) 90 playlist = await servers[0].playlists.create({ attributes })
98 playlist = resVideoPlaylistRequest.body.videoPlaylist 91 playlistIds = [ playlist.id, playlist.shortUUID, playlist.uuid ]
99 playlistIds = [ playlist.id, playlist.shortUUID, playlist.uuid ]
100 92
101 await addVideoInPlaylist({ 93 await servers[0].playlists.addElement({ playlistId: playlist.shortUUID, attributes: { videoId: servers[0].store.video.id } })
102 url: servers[0].url, 94 }
103 token: servers[0].accessToken,
104 playlistId: playlist.shortUUID,
105 elementAttrs: { videoId: video.id }
106 })
107 95
108 // Account 96 // Account
109 97
110 await updateMyUser({ url: servers[0].url, accessToken: servers[0].accessToken, description: 'my account description' }) 98 {
99 await servers[0].users.updateMe({ description: 'my account description' })
111 100
112 const resAccountRequest = await getAccount(servers[0].url, `${servers[0].user.username}@${servers[0].host}`) 101 account = await servers[0].accounts.get({ accountName: `${servers[0].store.user.username}@${servers[0].host}` })
113 account = resAccountRequest.body 102 }
114 103
115 await waitJobs(servers) 104 await waitJobs(servers)
116 }) 105 })
@@ -124,14 +113,14 @@ describe('Test a client controllers', function () {
124 url: servers[0].url, 113 url: servers[0].url,
125 path: basePath + id, 114 path: basePath + id,
126 accept: 'text/html', 115 accept: 'text/html',
127 statusCodeExpected: HttpStatusCode.OK_200 116 expectedStatus: HttpStatusCode.OK_200
128 }) 117 })
129 118
130 const port = servers[0].port 119 const port = servers[0].port
131 120
132 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' + 121 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
133 `url=http%3A%2F%2Flocalhost%3A${port}%2Fw%2F${servers[0].video.uuid}" ` + 122 `url=http%3A%2F%2Flocalhost%3A${port}%2Fw%2F${servers[0].store.video.shortUUID}" ` +
134 `title="${servers[0].video.name}" />` 123 `title="${servers[0].store.video.name}" />`
135 124
136 expect(res.text).to.contain(expectedLink) 125 expect(res.text).to.contain(expectedLink)
137 } 126 }
@@ -145,13 +134,13 @@ describe('Test a client controllers', function () {
145 url: servers[0].url, 134 url: servers[0].url,
146 path: basePath + id, 135 path: basePath + id,
147 accept: 'text/html', 136 accept: 'text/html',
148 statusCodeExpected: HttpStatusCode.OK_200 137 expectedStatus: HttpStatusCode.OK_200
149 }) 138 })
150 139
151 const port = servers[0].port 140 const port = servers[0].port
152 141
153 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' + 142 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
154 `url=http%3A%2F%2Flocalhost%3A${port}%2Fw%2Fp%2F${playlist.uuid}" ` + 143 `url=http%3A%2F%2Flocalhost%3A${port}%2Fw%2Fp%2F${playlist.shortUUID}" ` +
155 `title="${playlistName}" />` 144 `title="${playlistName}" />`
156 145
157 expect(res.text).to.contain(expectedLink) 146 expect(res.text).to.contain(expectedLink)
@@ -163,55 +152,55 @@ describe('Test a client controllers', function () {
163 describe('Open Graph', function () { 152 describe('Open Graph', function () {
164 153
165 async function accountPageTest (path: string) { 154 async function accountPageTest (path: string) {
166 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 155 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
167 const text = res.text 156 const text = res.text
168 157
169 expect(text).to.contain(`<meta property="og:title" content="${account.displayName}" />`) 158 expect(text).to.contain(`<meta property="og:title" content="${account.displayName}" />`)
170 expect(text).to.contain(`<meta property="og:description" content="${account.description}" />`) 159 expect(text).to.contain(`<meta property="og:description" content="${account.description}" />`)
171 expect(text).to.contain('<meta property="og:type" content="website" />') 160 expect(text).to.contain('<meta property="og:type" content="website" />')
172 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/accounts/${servers[0].user.username}" />`) 161 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/accounts/${servers[0].store.user.username}" />`)
173 } 162 }
174 163
175 async function channelPageTest (path: string) { 164 async function channelPageTest (path: string) {
176 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 165 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
177 const text = res.text 166 const text = res.text
178 167
179 expect(text).to.contain(`<meta property="og:title" content="${servers[0].videoChannel.displayName}" />`) 168 expect(text).to.contain(`<meta property="og:title" content="${servers[0].store.channel.displayName}" />`)
180 expect(text).to.contain(`<meta property="og:description" content="${channelDescription}" />`) 169 expect(text).to.contain(`<meta property="og:description" content="${channelDescription}" />`)
181 expect(text).to.contain('<meta property="og:type" content="website" />') 170 expect(text).to.contain('<meta property="og:type" content="website" />')
182 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/video-channels/${servers[0].videoChannel.name}" />`) 171 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/video-channels/${servers[0].store.channel.name}" />`)
183 } 172 }
184 173
185 async function watchVideoPageTest (path: string) { 174 async function watchVideoPageTest (path: string) {
186 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 175 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
187 const text = res.text 176 const text = res.text
188 177
189 expect(text).to.contain(`<meta property="og:title" content="${videoName}" />`) 178 expect(text).to.contain(`<meta property="og:title" content="${videoName}" />`)
190 expect(text).to.contain(`<meta property="og:description" content="${videoDescriptionPlainText}" />`) 179 expect(text).to.contain(`<meta property="og:description" content="${videoDescriptionPlainText}" />`)
191 expect(text).to.contain('<meta property="og:type" content="video" />') 180 expect(text).to.contain('<meta property="og:type" content="video" />')
192 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/${servers[0].video.uuid}" />`) 181 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/${servers[0].store.video.shortUUID}" />`)
193 } 182 }
194 183
195 async function watchPlaylistPageTest (path: string) { 184 async function watchPlaylistPageTest (path: string) {
196 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 185 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
197 const text = res.text 186 const text = res.text
198 187
199 expect(text).to.contain(`<meta property="og:title" content="${playlistName}" />`) 188 expect(text).to.contain(`<meta property="og:title" content="${playlistName}" />`)
200 expect(text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`) 189 expect(text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`)
201 expect(text).to.contain('<meta property="og:type" content="video" />') 190 expect(text).to.contain('<meta property="og:type" content="video" />')
202 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/p/${playlist.uuid}" />`) 191 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/p/${playlist.shortUUID}" />`)
203 } 192 }
204 193
205 it('Should have valid Open Graph tags on the account page', async function () { 194 it('Should have valid Open Graph tags on the account page', async function () {
206 await accountPageTest('/accounts/' + servers[0].user.username) 195 await accountPageTest('/accounts/' + servers[0].store.user.username)
207 await accountPageTest('/a/' + servers[0].user.username) 196 await accountPageTest('/a/' + servers[0].store.user.username)
208 await accountPageTest('/@' + servers[0].user.username) 197 await accountPageTest('/@' + servers[0].store.user.username)
209 }) 198 })
210 199
211 it('Should have valid Open Graph tags on the channel page', async function () { 200 it('Should have valid Open Graph tags on the channel page', async function () {
212 await channelPageTest('/video-channels/' + servers[0].videoChannel.name) 201 await channelPageTest('/video-channels/' + servers[0].store.channel.name)
213 await channelPageTest('/c/' + servers[0].videoChannel.name) 202 await channelPageTest('/c/' + servers[0].store.channel.name)
214 await channelPageTest('/@' + servers[0].videoChannel.name) 203 await channelPageTest('/@' + servers[0].store.channel.name)
215 }) 204 })
216 205
217 it('Should have valid Open Graph tags on the watch page', async function () { 206 it('Should have valid Open Graph tags on the watch page', async function () {
@@ -236,7 +225,7 @@ describe('Test a client controllers', function () {
236 describe('Not whitelisted', function () { 225 describe('Not whitelisted', function () {
237 226
238 async function accountPageTest (path: string) { 227 async function accountPageTest (path: string) {
239 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 228 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
240 const text = res.text 229 const text = res.text
241 230
242 expect(text).to.contain('<meta property="twitter:card" content="summary" />') 231 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
@@ -246,17 +235,17 @@ describe('Test a client controllers', function () {
246 } 235 }
247 236
248 async function channelPageTest (path: string) { 237 async function channelPageTest (path: string) {
249 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 238 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
250 const text = res.text 239 const text = res.text
251 240
252 expect(text).to.contain('<meta property="twitter:card" content="summary" />') 241 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
253 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />') 242 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
254 expect(text).to.contain(`<meta property="twitter:title" content="${servers[0].videoChannel.displayName}" />`) 243 expect(text).to.contain(`<meta property="twitter:title" content="${servers[0].store.channel.displayName}" />`)
255 expect(text).to.contain(`<meta property="twitter:description" content="${channelDescription}" />`) 244 expect(text).to.contain(`<meta property="twitter:description" content="${channelDescription}" />`)
256 } 245 }
257 246
258 async function watchVideoPageTest (path: string) { 247 async function watchVideoPageTest (path: string) {
259 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 248 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
260 const text = res.text 249 const text = res.text
261 250
262 expect(text).to.contain('<meta property="twitter:card" content="summary_large_image" />') 251 expect(text).to.contain('<meta property="twitter:card" content="summary_large_image" />')
@@ -266,7 +255,7 @@ describe('Test a client controllers', function () {
266 } 255 }
267 256
268 async function watchPlaylistPageTest (path: string) { 257 async function watchPlaylistPageTest (path: string) {
269 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 258 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
270 const text = res.text 259 const text = res.text
271 260
272 expect(text).to.contain('<meta property="twitter:card" content="summary" />') 261 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
@@ -298,27 +287,26 @@ describe('Test a client controllers', function () {
298 }) 287 })
299 288
300 it('Should have valid twitter card on the channel page', async function () { 289 it('Should have valid twitter card on the channel page', async function () {
301 await channelPageTest('/video-channels/' + servers[0].videoChannel.name) 290 await channelPageTest('/video-channels/' + servers[0].store.channel.name)
302 await channelPageTest('/c/' + servers[0].videoChannel.name) 291 await channelPageTest('/c/' + servers[0].store.channel.name)
303 await channelPageTest('/@' + servers[0].videoChannel.name) 292 await channelPageTest('/@' + servers[0].store.channel.name)
304 }) 293 })
305 }) 294 })
306 295
307 describe('Whitelisted', function () { 296 describe('Whitelisted', function () {
308 297
309 before(async function () { 298 before(async function () {
310 const res = await getCustomConfig(servers[0].url, servers[0].accessToken) 299 const config = await servers[0].config.getCustomConfig()
311 const config = res.body as CustomConfig
312 config.services.twitter = { 300 config.services.twitter = {
313 username: '@Kuja', 301 username: '@Kuja',
314 whitelisted: true 302 whitelisted: true
315 } 303 }
316 304
317 await updateCustomConfig(servers[0].url, servers[0].accessToken, config) 305 await servers[0].config.updateCustomConfig({ newCustomConfig: config })
318 }) 306 })
319 307
320 async function accountPageTest (path: string) { 308 async function accountPageTest (path: string) {
321 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 309 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
322 const text = res.text 310 const text = res.text
323 311
324 expect(text).to.contain('<meta property="twitter:card" content="summary" />') 312 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
@@ -326,7 +314,7 @@ describe('Test a client controllers', function () {
326 } 314 }
327 315
328 async function channelPageTest (path: string) { 316 async function channelPageTest (path: string) {
329 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 317 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
330 const text = res.text 318 const text = res.text
331 319
332 expect(text).to.contain('<meta property="twitter:card" content="summary" />') 320 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
@@ -334,7 +322,7 @@ describe('Test a client controllers', function () {
334 } 322 }
335 323
336 async function watchVideoPageTest (path: string) { 324 async function watchVideoPageTest (path: string) {
337 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 325 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
338 const text = res.text 326 const text = res.text
339 327
340 expect(text).to.contain('<meta property="twitter:card" content="player" />') 328 expect(text).to.contain('<meta property="twitter:card" content="player" />')
@@ -342,7 +330,7 @@ describe('Test a client controllers', function () {
342 } 330 }
343 331
344 async function watchPlaylistPageTest (path: string) { 332 async function watchPlaylistPageTest (path: string) {
345 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 333 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
346 const text = res.text 334 const text = res.text
347 335
348 expect(text).to.contain('<meta property="twitter:card" content="player" />') 336 expect(text).to.contain('<meta property="twitter:card" content="player" />')
@@ -372,9 +360,9 @@ describe('Test a client controllers', function () {
372 }) 360 })
373 361
374 it('Should have valid twitter card on the channel page', async function () { 362 it('Should have valid twitter card on the channel page', async function () {
375 await channelPageTest('/video-channels/' + servers[0].videoChannel.name) 363 await channelPageTest('/video-channels/' + servers[0].store.channel.name)
376 await channelPageTest('/c/' + servers[0].videoChannel.name) 364 await channelPageTest('/c/' + servers[0].store.channel.name)
377 await channelPageTest('/@' + servers[0].videoChannel.name) 365 await channelPageTest('/@' + servers[0].store.channel.name)
378 }) 366 })
379 }) 367 })
380 }) 368 })
@@ -382,53 +370,55 @@ describe('Test a client controllers', function () {
382 describe('Index HTML', function () { 370 describe('Index HTML', function () {
383 371
384 it('Should have valid index html tags (title, description...)', async function () { 372 it('Should have valid index html tags (title, description...)', async function () {
385 const resConfig = await getConfig(servers[0].url) 373 const config = await servers[0].config.getConfig()
386 const res = await makeHTMLRequest(servers[0].url, '/videos/trending') 374 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
387 375
388 const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.' 376 const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
389 checkIndexTags(res.text, 'PeerTube', description, '', resConfig.body) 377 checkIndexTags(res.text, 'PeerTube', description, '', config)
390 }) 378 })
391 379
392 it('Should update the customized configuration and have the correct index html tags', async function () { 380 it('Should update the customized configuration and have the correct index html tags', async function () {
393 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 381 await servers[0].config.updateCustomSubConfig({
394 instance: { 382 newConfig: {
395 name: 'PeerTube updated', 383 instance: {
396 shortDescription: 'my short description', 384 name: 'PeerTube updated',
397 description: 'my super description', 385 shortDescription: 'my short description',
398 terms: 'my super terms', 386 description: 'my super description',
399 defaultNSFWPolicy: 'blur', 387 terms: 'my super terms',
400 defaultClientRoute: '/videos/recently-added', 388 defaultNSFWPolicy: 'blur',
401 customizations: { 389 defaultClientRoute: '/videos/recently-added',
402 javascript: 'alert("coucou")', 390 customizations: {
403 css: 'body { background-color: red; }' 391 javascript: 'alert("coucou")',
392 css: 'body { background-color: red; }'
393 }
404 } 394 }
405 } 395 }
406 }) 396 })
407 397
408 const resConfig = await getConfig(servers[0].url) 398 const config = await servers[0].config.getConfig()
409 const res = await makeHTMLRequest(servers[0].url, '/videos/trending') 399 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
410 400
411 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body) 401 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
412 }) 402 })
413 403
414 it('Should have valid index html updated tags (title, description...)', async function () { 404 it('Should have valid index html updated tags (title, description...)', async function () {
415 const resConfig = await getConfig(servers[0].url) 405 const config = await servers[0].config.getConfig()
416 const res = await makeHTMLRequest(servers[0].url, '/videos/trending') 406 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
417 407
418 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body) 408 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
419 }) 409 })
420 410
421 it('Should use the original video URL for the canonical tag', async function () { 411 it('Should use the original video URL for the canonical tag', async function () {
422 for (const basePath of watchVideoBasePaths) { 412 for (const basePath of watchVideoBasePaths) {
423 for (const id of videoIds) { 413 for (const id of videoIds) {
424 const res = await makeHTMLRequest(servers[1].url, basePath + id) 414 const res = await makeHTMLRequest(servers[1].url, basePath + id)
425 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`) 415 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/videos/watch/${servers[0].store.video.uuid}" />`)
426 } 416 }
427 } 417 }
428 }) 418 })
429 419
430 it('Should use the original account URL for the canonical tag', async function () { 420 it('Should use the original account URL for the canonical tag', async function () {
431 const accountURLtest = (res) => { 421 const accountURLtest = res => {
432 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/accounts/root" />`) 422 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/accounts/root" />`)
433 } 423 }
434 424
@@ -438,7 +428,7 @@ describe('Test a client controllers', function () {
438 }) 428 })
439 429
440 it('Should use the original channel URL for the canonical tag', async function () { 430 it('Should use the original channel URL for the canonical tag', async function () {
441 const channelURLtests = (res) => { 431 const channelURLtests = res => {
442 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-channels/root_channel" />`) 432 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-channels/root_channel" />`)
443 } 433 }
444 434
@@ -460,10 +450,10 @@ describe('Test a client controllers', function () {
460 describe('Embed HTML', function () { 450 describe('Embed HTML', function () {
461 451
462 it('Should have the correct embed html tags', async function () { 452 it('Should have the correct embed html tags', async function () {
463 const resConfig = await getConfig(servers[0].url) 453 const config = await servers[0].config.getConfig()
464 const res = await makeHTMLRequest(servers[0].url, servers[0].video.embedPath) 454 const res = await makeHTMLRequest(servers[0].url, servers[0].store.video.embedPath)
465 455
466 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body) 456 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
467 }) 457 })
468 }) 458 })
469 459
diff --git a/server/tests/external-plugins/auth-ldap.ts b/server/tests/external-plugins/auth-ldap.ts
index e4eae7e8c..acec69df5 100644
--- a/server/tests/external-plugins/auth-ldap.ts
+++ b/server/tests/external-plugins/auth-ldap.ts
@@ -2,46 +2,29 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { User } from '@shared/models/users/user.model' 5import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
6import { 6import { HttpStatusCode } from '@shared/models'
7 blockUser,
8 getMyUserInformation,
9 installPlugin,
10 setAccessTokensToServers,
11 unblockUser,
12 uninstallPlugin,
13 updatePluginSettings,
14 uploadVideo,
15 userLogin
16} from '../../../shared/extra-utils'
17import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers'
18 7
19describe('Official plugin auth-ldap', function () { 8describe('Official plugin auth-ldap', function () {
20 let server: ServerInfo 9 let server: PeerTubeServer
21 let accessToken: string 10 let accessToken: string
22 let userId: number 11 let userId: number
23 12
24 before(async function () { 13 before(async function () {
25 this.timeout(30000) 14 this.timeout(30000)
26 15
27 server = await flushAndRunServer(1) 16 server = await createSingleServer(1)
28 await setAccessTokensToServers([ server ]) 17 await setAccessTokensToServers([ server ])
29 18
30 await installPlugin({ 19 await server.plugins.install({ npmName: 'peertube-plugin-auth-ldap' })
31 url: server.url,
32 accessToken: server.accessToken,
33 npmName: 'peertube-plugin-auth-ldap'
34 })
35 }) 20 })
36 21
37 it('Should not login with without LDAP settings', async function () { 22 it('Should not login with without LDAP settings', async function () {
38 await userLogin(server, { username: 'fry', password: 'fry' }, 400) 23 await server.login.login({ user: { username: 'fry', password: 'fry' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
39 }) 24 })
40 25
41 it('Should not login with bad LDAP settings', async function () { 26 it('Should not login with bad LDAP settings', async function () {
42 await updatePluginSettings({ 27 await server.plugins.updateSettings({
43 url: server.url,
44 accessToken: server.accessToken,
45 npmName: 'peertube-plugin-auth-ldap', 28 npmName: 'peertube-plugin-auth-ldap',
46 settings: { 29 settings: {
47 'bind-credentials': 'GoodNewsEveryone', 30 'bind-credentials': 'GoodNewsEveryone',
@@ -55,13 +38,11 @@ describe('Official plugin auth-ldap', function () {
55 } 38 }
56 }) 39 })
57 40
58 await userLogin(server, { username: 'fry', password: 'fry' }, 400) 41 await server.login.login({ user: { username: 'fry', password: 'fry' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
59 }) 42 })
60 43
61 it('Should not login with good LDAP settings but wrong username/password', async function () { 44 it('Should not login with good LDAP settings but wrong username/password', async function () {
62 await updatePluginSettings({ 45 await server.plugins.updateSettings({
63 url: server.url,
64 accessToken: server.accessToken,
65 npmName: 'peertube-plugin-auth-ldap', 46 npmName: 'peertube-plugin-auth-ldap',
66 settings: { 47 settings: {
67 'bind-credentials': 'GoodNewsEveryone', 48 'bind-credentials': 'GoodNewsEveryone',
@@ -75,22 +56,20 @@ describe('Official plugin auth-ldap', function () {
75 } 56 }
76 }) 57 })
77 58
78 await userLogin(server, { username: 'fry', password: 'bad password' }, 400) 59 await server.login.login({ user: { username: 'fry', password: 'bad password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
79 await userLogin(server, { username: 'fryr', password: 'fry' }, 400) 60 await server.login.login({ user: { username: 'fryr', password: 'fry' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
80 }) 61 })
81 62
82 it('Should login with the appropriate username/password', async function () { 63 it('Should login with the appropriate username/password', async function () {
83 accessToken = await userLogin(server, { username: 'fry', password: 'fry' }) 64 accessToken = await server.login.getAccessToken({ username: 'fry', password: 'fry' })
84 }) 65 })
85 66
86 it('Should login with the appropriate email/password', async function () { 67 it('Should login with the appropriate email/password', async function () {
87 accessToken = await userLogin(server, { username: 'fry@planetexpress.com', password: 'fry' }) 68 accessToken = await server.login.getAccessToken({ username: 'fry@planetexpress.com', password: 'fry' })
88 }) 69 })
89 70
90 it('Should login get my profile', async function () { 71 it('Should login get my profile', async function () {
91 const res = await getMyUserInformation(server.url, accessToken) 72 const body = await server.users.getMyInfo({ token: accessToken })
92 const body: User = res.body
93
94 expect(body.username).to.equal('fry') 73 expect(body.username).to.equal('fry')
95 expect(body.email).to.equal('fry@planetexpress.com') 74 expect(body.email).to.equal('fry@planetexpress.com')
96 75
@@ -98,25 +77,31 @@ describe('Official plugin auth-ldap', function () {
98 }) 77 })
99 78
100 it('Should upload a video', async function () { 79 it('Should upload a video', async function () {
101 await uploadVideo(server.url, accessToken, { name: 'my super video' }) 80 await server.videos.upload({ token: accessToken, attributes: { name: 'my super video' } })
102 }) 81 })
103 82
104 it('Should not be able to login if the user is banned', async function () { 83 it('Should not be able to login if the user is banned', async function () {
105 await blockUser(server.url, userId, server.accessToken) 84 await server.users.banUser({ userId })
106 85
107 await userLogin(server, { username: 'fry@planetexpress.com', password: 'fry' }, 400) 86 await server.login.login({
87 user: { username: 'fry@planetexpress.com', password: 'fry' },
88 expectedStatus: HttpStatusCode.BAD_REQUEST_400
89 })
108 }) 90 })
109 91
110 it('Should be able to login if the user is unbanned', async function () { 92 it('Should be able to login if the user is unbanned', async function () {
111 await unblockUser(server.url, userId, server.accessToken) 93 await server.users.unbanUser({ userId })
112 94
113 await userLogin(server, { username: 'fry@planetexpress.com', password: 'fry' }) 95 await server.login.login({ user: { username: 'fry@planetexpress.com', password: 'fry' } })
114 }) 96 })
115 97
116 it('Should not login if the plugin is uninstalled', async function () { 98 it('Should not login if the plugin is uninstalled', async function () {
117 await uninstallPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-plugin-auth-ldap' }) 99 await server.plugins.uninstall({ npmName: 'peertube-plugin-auth-ldap' })
118 100
119 await userLogin(server, { username: 'fry@planetexpress.com', password: 'fry' }, 400) 101 await server.login.login({
102 user: { username: 'fry@planetexpress.com', password: 'fry' },
103 expectedStatus: HttpStatusCode.BAD_REQUEST_400
104 })
120 }) 105 })
121 106
122 after(async function () { 107 after(async function () {
diff --git a/server/tests/external-plugins/auto-block-videos.ts b/server/tests/external-plugins/auto-block-videos.ts
index 18ea17d78..0eb4bda9a 100644
--- a/server/tests/external-plugins/auto-block-videos.ts
+++ b/server/tests/external-plugins/auto-block-videos.ts
@@ -2,41 +2,29 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { Video, VideoBlacklist } from '@shared/models'
6import { 5import {
6 cleanupTests,
7 createMultipleServers,
7 doubleFollow, 8 doubleFollow,
8 getBlacklistedVideosList, 9 killallServers,
9 getVideosList,
10 installPlugin,
11 MockBlocklist, 10 MockBlocklist,
12 removeVideoFromBlacklist, 11 PeerTubeServer,
13 setAccessTokensToServers, 12 setAccessTokensToServers,
14 updatePluginSettings,
15 uploadVideoAndGetId,
16 wait 13 wait
17} from '../../../shared/extra-utils' 14} from '@shared/extra-utils'
18import { 15import { Video } from '@shared/models'
19 cleanupTests,
20 flushAndRunMultipleServers,
21 killallServers,
22 reRunServer,
23 ServerInfo
24} from '../../../shared/extra-utils/server/servers'
25 16
26async function check (server: ServerInfo, videoUUID: string, exists = true) { 17async function check (server: PeerTubeServer, videoUUID: string, exists = true) {
27 const res = await getVideosList(server.url) 18 const { data } = await server.videos.list()
28 19
29 const video = res.body.data.find(v => v.uuid === videoUUID) 20 const video = data.find(v => v.uuid === videoUUID)
30 21
31 if (exists) { 22 if (exists) expect(video).to.not.be.undefined
32 expect(video).to.not.be.undefined 23 else expect(video).to.be.undefined
33 } else {
34 expect(video).to.be.undefined
35 }
36} 24}
37 25
38describe('Official plugin auto-block videos', function () { 26describe('Official plugin auto-block videos', function () {
39 let servers: ServerInfo[] 27 let servers: PeerTubeServer[]
40 let blocklistServer: MockBlocklist 28 let blocklistServer: MockBlocklist
41 let server1Videos: Video[] = [] 29 let server1Videos: Video[] = []
42 let server2Videos: Video[] = [] 30 let server2Videos: Video[] = []
@@ -45,42 +33,36 @@ describe('Official plugin auto-block videos', function () {
45 before(async function () { 33 before(async function () {
46 this.timeout(60000) 34 this.timeout(60000)
47 35
48 servers = await flushAndRunMultipleServers(2) 36 servers = await createMultipleServers(2)
49 await setAccessTokensToServers(servers) 37 await setAccessTokensToServers(servers)
50 38
51 for (const server of servers) { 39 for (const server of servers) {
52 await installPlugin({ 40 await server.plugins.install({ npmName: 'peertube-plugin-auto-block-videos' })
53 url: server.url,
54 accessToken: server.accessToken,
55 npmName: 'peertube-plugin-auto-block-videos'
56 })
57 } 41 }
58 42
59 blocklistServer = new MockBlocklist() 43 blocklistServer = new MockBlocklist()
60 port = await blocklistServer.initialize() 44 port = await blocklistServer.initialize()
61 45
62 await uploadVideoAndGetId({ server: servers[0], videoName: 'video server 1' }) 46 await servers[0].videos.quickUpload({ name: 'video server 1' })
63 await uploadVideoAndGetId({ server: servers[1], videoName: 'video server 2' }) 47 await servers[1].videos.quickUpload({ name: 'video server 2' })
64 await uploadVideoAndGetId({ server: servers[1], videoName: 'video 2 server 2' }) 48 await servers[1].videos.quickUpload({ name: 'video 2 server 2' })
65 await uploadVideoAndGetId({ server: servers[1], videoName: 'video 3 server 2' }) 49 await servers[1].videos.quickUpload({ name: 'video 3 server 2' })
66 50
67 { 51 {
68 const res = await getVideosList(servers[0].url) 52 const { data } = await servers[0].videos.list()
69 server1Videos = res.body.data.map(v => Object.assign(v, { url: servers[0].url + '/videos/watch/' + v.uuid })) 53 server1Videos = data.map(v => Object.assign(v, { url: servers[0].url + '/videos/watch/' + v.uuid }))
70 } 54 }
71 55
72 { 56 {
73 const res = await getVideosList(servers[1].url) 57 const { data } = await servers[1].videos.list()
74 server2Videos = res.body.data.map(v => Object.assign(v, { url: servers[1].url + '/videos/watch/' + v.uuid })) 58 server2Videos = data.map(v => Object.assign(v, { url: servers[1].url + '/videos/watch/' + v.uuid }))
75 } 59 }
76 60
77 await doubleFollow(servers[0], servers[1]) 61 await doubleFollow(servers[0], servers[1])
78 }) 62 })
79 63
80 it('Should update plugin settings', async function () { 64 it('Should update plugin settings', async function () {
81 await updatePluginSettings({ 65 await servers[0].plugins.updateSettings({
82 url: servers[0].url,
83 accessToken: servers[0].accessToken,
84 npmName: 'peertube-plugin-auto-block-videos', 66 npmName: 'peertube-plugin-auto-block-videos',
85 settings: { 67 settings: {
86 'blocklist-urls': `http://localhost:${port}/blocklist`, 68 'blocklist-urls': `http://localhost:${port}/blocklist`,
@@ -108,10 +90,9 @@ describe('Official plugin auto-block videos', function () {
108 }) 90 })
109 91
110 it('Should have video in blacklists', async function () { 92 it('Should have video in blacklists', async function () {
111 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken }) 93 const body = await servers[0].blacklist.list()
112
113 const videoBlacklists = res.body.data as VideoBlacklist[]
114 94
95 const videoBlacklists = body.data
115 expect(videoBlacklists).to.have.lengthOf(1) 96 expect(videoBlacklists).to.have.lengthOf(1)
116 expect(videoBlacklists[0].reason).to.contains('Automatically blocked from auto block plugin') 97 expect(videoBlacklists[0].reason).to.contains('Automatically blocked from auto block plugin')
117 expect(videoBlacklists[0].video.name).to.equal(server2Videos[0].name) 98 expect(videoBlacklists[0].video.name).to.equal(server2Videos[0].name)
@@ -174,12 +155,12 @@ describe('Official plugin auto-block videos', function () {
174 155
175 await check(servers[0], video.uuid, false) 156 await check(servers[0], video.uuid, false)
176 157
177 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, video.uuid) 158 await servers[0].blacklist.remove({ videoId: video.uuid })
178 159
179 await check(servers[0], video.uuid, true) 160 await check(servers[0], video.uuid, true)
180 161
181 killallServers([ servers[0] ]) 162 await killallServers([ servers[0] ])
182 await reRunServer(servers[0]) 163 await servers[0].run()
183 await wait(2000) 164 await wait(2000)
184 165
185 await check(servers[0], video.uuid, true) 166 await check(servers[0], video.uuid, true)
diff --git a/server/tests/external-plugins/auto-mute.ts b/server/tests/external-plugins/auto-mute.ts
index 09355d932..271779dd4 100644
--- a/server/tests/external-plugins/auto-mute.ts
+++ b/server/tests/external-plugins/auto-mute.ts
@@ -3,63 +3,45 @@
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { 5import {
6 addAccountToServerBlocklist, 6 cleanupTests,
7 addServerToAccountBlocklist, 7 createMultipleServers,
8 removeAccountFromServerBlocklist
9} from '@shared/extra-utils/users/blocklist'
10import {
11 doubleFollow, 8 doubleFollow,
12 getVideosList, 9 killallServers,
13 installPlugin,
14 makeGetRequest, 10 makeGetRequest,
15 MockBlocklist, 11 MockBlocklist,
12 PeerTubeServer,
16 setAccessTokensToServers, 13 setAccessTokensToServers,
17 updatePluginSettings,
18 uploadVideoAndGetId,
19 wait 14 wait
20} from '../../../shared/extra-utils' 15} from '@shared/extra-utils'
21import { 16import { HttpStatusCode } from '@shared/models'
22 cleanupTests,
23 flushAndRunMultipleServers,
24 killallServers,
25 reRunServer,
26 ServerInfo
27} from '../../../shared/extra-utils/server/servers'
28import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
29 17
30describe('Official plugin auto-mute', function () { 18describe('Official plugin auto-mute', function () {
31 const autoMuteListPath = '/plugins/auto-mute/router/api/v1/mute-list' 19 const autoMuteListPath = '/plugins/auto-mute/router/api/v1/mute-list'
32 let servers: ServerInfo[] 20 let servers: PeerTubeServer[]
33 let blocklistServer: MockBlocklist 21 let blocklistServer: MockBlocklist
34 let port: number 22 let port: number
35 23
36 before(async function () { 24 before(async function () {
37 this.timeout(30000) 25 this.timeout(30000)
38 26
39 servers = await flushAndRunMultipleServers(2) 27 servers = await createMultipleServers(2)
40 await setAccessTokensToServers(servers) 28 await setAccessTokensToServers(servers)
41 29
42 for (const server of servers) { 30 for (const server of servers) {
43 await installPlugin({ 31 await server.plugins.install({ npmName: 'peertube-plugin-auto-mute' })
44 url: server.url,
45 accessToken: server.accessToken,
46 npmName: 'peertube-plugin-auto-mute'
47 })
48 } 32 }
49 33
50 blocklistServer = new MockBlocklist() 34 blocklistServer = new MockBlocklist()
51 port = await blocklistServer.initialize() 35 port = await blocklistServer.initialize()
52 36
53 await uploadVideoAndGetId({ server: servers[0], videoName: 'video server 1' }) 37 await servers[0].videos.quickUpload({ name: 'video server 1' })
54 await uploadVideoAndGetId({ server: servers[1], videoName: 'video server 2' }) 38 await servers[1].videos.quickUpload({ name: 'video server 2' })
55 39
56 await doubleFollow(servers[0], servers[1]) 40 await doubleFollow(servers[0], servers[1])
57 }) 41 })
58 42
59 it('Should update plugin settings', async function () { 43 it('Should update plugin settings', async function () {
60 await updatePluginSettings({ 44 await servers[0].plugins.updateSettings({
61 url: servers[0].url,
62 accessToken: servers[0].accessToken,
63 npmName: 'peertube-plugin-auto-mute', 45 npmName: 'peertube-plugin-auto-mute',
64 settings: { 46 settings: {
65 'blocklist-urls': `http://localhost:${port}/blocklist`, 47 'blocklist-urls': `http://localhost:${port}/blocklist`,
@@ -81,8 +63,8 @@ describe('Official plugin auto-mute', function () {
81 63
82 await wait(2000) 64 await wait(2000)
83 65
84 const res = await getVideosList(servers[0].url) 66 const { total } = await servers[0].videos.list()
85 expect(res.body.total).to.equal(1) 67 expect(total).to.equal(1)
86 }) 68 })
87 69
88 it('Should remove a server blocklist', async function () { 70 it('Should remove a server blocklist', async function () {
@@ -99,8 +81,8 @@ describe('Official plugin auto-mute', function () {
99 81
100 await wait(2000) 82 await wait(2000)
101 83
102 const res = await getVideosList(servers[0].url) 84 const { total } = await servers[0].videos.list()
103 expect(res.body.total).to.equal(2) 85 expect(total).to.equal(2)
104 }) 86 })
105 87
106 it('Should add an account blocklist', async function () { 88 it('Should add an account blocklist', async function () {
@@ -116,8 +98,8 @@ describe('Official plugin auto-mute', function () {
116 98
117 await wait(2000) 99 await wait(2000)
118 100
119 const res = await getVideosList(servers[0].url) 101 const { total } = await servers[0].videos.list()
120 expect(res.body.total).to.equal(1) 102 expect(total).to.equal(1)
121 }) 103 })
122 104
123 it('Should remove an account blocklist', async function () { 105 it('Should remove an account blocklist', async function () {
@@ -134,8 +116,8 @@ describe('Official plugin auto-mute', function () {
134 116
135 await wait(2000) 117 await wait(2000)
136 118
137 const res = await getVideosList(servers[0].url) 119 const { total } = await servers[0].videos.list()
138 expect(res.body.total).to.equal(2) 120 expect(total).to.equal(2)
139 }) 121 })
140 122
141 it('Should auto mute an account, manually unmute it and do not remute it automatically', async function () { 123 it('Should auto mute an account, manually unmute it and do not remute it automatically', async function () {
@@ -155,24 +137,24 @@ describe('Official plugin auto-mute', function () {
155 await wait(2000) 137 await wait(2000)
156 138
157 { 139 {
158 const res = await getVideosList(servers[0].url) 140 const { total } = await servers[0].videos.list()
159 expect(res.body.total).to.equal(1) 141 expect(total).to.equal(1)
160 } 142 }
161 143
162 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, account) 144 await servers[0].blocklist.removeFromServerBlocklist({ account })
163 145
164 { 146 {
165 const res = await getVideosList(servers[0].url) 147 const { total } = await servers[0].videos.list()
166 expect(res.body.total).to.equal(2) 148 expect(total).to.equal(2)
167 } 149 }
168 150
169 killallServers([ servers[0] ]) 151 await killallServers([ servers[0] ])
170 await reRunServer(servers[0]) 152 await servers[0].run()
171 await wait(2000) 153 await wait(2000)
172 154
173 { 155 {
174 const res = await getVideosList(servers[0].url) 156 const { total } = await servers[0].videos.list()
175 expect(res.body.total).to.equal(2) 157 expect(total).to.equal(2)
176 } 158 }
177 }) 159 })
178 160
@@ -180,14 +162,12 @@ describe('Official plugin auto-mute', function () {
180 await makeGetRequest({ 162 await makeGetRequest({
181 url: servers[0].url, 163 url: servers[0].url,
182 path: '/plugins/auto-mute/router/api/v1/mute-list', 164 path: '/plugins/auto-mute/router/api/v1/mute-list',
183 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 165 expectedStatus: HttpStatusCode.FORBIDDEN_403
184 }) 166 })
185 }) 167 })
186 168
187 it('Should enable auto mute list', async function () { 169 it('Should enable auto mute list', async function () {
188 await updatePluginSettings({ 170 await servers[0].plugins.updateSettings({
189 url: servers[0].url,
190 accessToken: servers[0].accessToken,
191 npmName: 'peertube-plugin-auto-mute', 171 npmName: 'peertube-plugin-auto-mute',
192 settings: { 172 settings: {
193 'blocklist-urls': '', 173 'blocklist-urls': '',
@@ -199,16 +179,14 @@ describe('Official plugin auto-mute', function () {
199 await makeGetRequest({ 179 await makeGetRequest({
200 url: servers[0].url, 180 url: servers[0].url,
201 path: '/plugins/auto-mute/router/api/v1/mute-list', 181 path: '/plugins/auto-mute/router/api/v1/mute-list',
202 statusCodeExpected: HttpStatusCode.OK_200 182 expectedStatus: HttpStatusCode.OK_200
203 }) 183 })
204 }) 184 })
205 185
206 it('Should mute an account on server 1, and server 2 auto mutes it', async function () { 186 it('Should mute an account on server 1, and server 2 auto mutes it', async function () {
207 this.timeout(20000) 187 this.timeout(20000)
208 188
209 await updatePluginSettings({ 189 await servers[1].plugins.updateSettings({
210 url: servers[1].url,
211 accessToken: servers[1].accessToken,
212 npmName: 'peertube-plugin-auto-mute', 190 npmName: 'peertube-plugin-auto-mute',
213 settings: { 191 settings: {
214 'blocklist-urls': 'http://localhost:' + servers[0].port + autoMuteListPath, 192 'blocklist-urls': 'http://localhost:' + servers[0].port + autoMuteListPath,
@@ -217,13 +195,13 @@ describe('Official plugin auto-mute', function () {
217 } 195 }
218 }) 196 })
219 197
220 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port) 198 await servers[0].blocklist.addToServerBlocklist({ account: 'root@localhost:' + servers[1].port })
221 await addServerToAccountBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port) 199 await servers[0].blocklist.addToMyBlocklist({ server: 'localhost:' + servers[1].port })
222 200
223 const res = await makeGetRequest({ 201 const res = await makeGetRequest({
224 url: servers[0].url, 202 url: servers[0].url,
225 path: '/plugins/auto-mute/router/api/v1/mute-list', 203 path: '/plugins/auto-mute/router/api/v1/mute-list',
226 statusCodeExpected: HttpStatusCode.OK_200 204 expectedStatus: HttpStatusCode.OK_200
227 }) 205 })
228 206
229 const data = res.body.data 207 const data = res.body.data
@@ -234,8 +212,8 @@ describe('Official plugin auto-mute', function () {
234 await wait(2000) 212 await wait(2000)
235 213
236 for (const server of servers) { 214 for (const server of servers) {
237 const res = await getVideosList(server.url) 215 const { total } = await server.videos.list()
238 expect(res.body.total).to.equal(1) 216 expect(total).to.equal(1)
239 } 217 }
240 }) 218 })
241 219
diff --git a/server/tests/feeds/feeds.ts b/server/tests/feeds/feeds.ts
index 7bad81751..55b434846 100644
--- a/server/tests/feeds/feeds.ts
+++ b/server/tests/feeds/feeds.ts
@@ -4,34 +4,16 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import * as xmlParser from 'fast-xml-parser' 5import * as xmlParser from 'fast-xml-parser'
6import { 6import {
7 addAccountToAccountBlocklist,
8 addAccountToServerBlocklist,
9 removeAccountFromServerBlocklist
10} from '@shared/extra-utils/users/blocklist'
11import { addUserSubscription, listUserSubscriptionVideos } from '@shared/extra-utils/users/user-subscriptions'
12import { VideoPrivacy } from '@shared/models'
13import { ScopedToken } from '@shared/models/users/user-scoped-token'
14import {
15 cleanupTests, 7 cleanupTests,
16 createUser, 8 createMultipleServers,
9 createSingleServer,
17 doubleFollow, 10 doubleFollow,
18 flushAndRunMultipleServers, 11 makeGetRequest,
19 flushAndRunServer, 12 PeerTubeServer,
20 getJSONfeed,
21 getMyUserInformation,
22 getUserScopedTokens,
23 getXMLfeed,
24 renewUserScopedTokens,
25 ServerInfo,
26 setAccessTokensToServers, 13 setAccessTokensToServers,
27 uploadVideo, 14 waitJobs
28 uploadVideoAndGetId, 15} from '@shared/extra-utils'
29 userLogin 16import { HttpStatusCode, VideoPrivacy } from '@shared/models'
30} from '../../../shared/extra-utils'
31import { waitJobs } from '../../../shared/extra-utils/server/jobs'
32import { addVideoCommentThread } from '../../../shared/extra-utils/videos/video-comments'
33import { User } from '../../../shared/models/users'
34import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
35 17
36chai.use(require('chai-xml')) 18chai.use(require('chai-xml'))
37chai.use(require('chai-json-schema')) 19chai.use(require('chai-json-schema'))
@@ -39,8 +21,8 @@ chai.config.includeStack = true
39const expect = chai.expect 21const expect = chai.expect
40 22
41describe('Test syndication feeds', () => { 23describe('Test syndication feeds', () => {
42 let servers: ServerInfo[] = [] 24 let servers: PeerTubeServer[] = []
43 let serverHLSOnly: ServerInfo 25 let serverHLSOnly: PeerTubeServer
44 let userAccessToken: string 26 let userAccessToken: string
45 let rootAccountId: number 27 let rootAccountId: number
46 let rootChannelId: number 28 let rootChannelId: number
@@ -52,8 +34,8 @@ describe('Test syndication feeds', () => {
52 this.timeout(120000) 34 this.timeout(120000)
53 35
54 // Run servers 36 // Run servers
55 servers = await flushAndRunMultipleServers(2) 37 servers = await createMultipleServers(2)
56 serverHLSOnly = await flushAndRunServer(3, { 38 serverHLSOnly = await createSingleServer(3, {
57 transcoding: { 39 transcoding: {
58 enabled: true, 40 enabled: true,
59 webtorrent: { enabled: false }, 41 webtorrent: { enabled: false },
@@ -65,50 +47,43 @@ describe('Test syndication feeds', () => {
65 await doubleFollow(servers[0], servers[1]) 47 await doubleFollow(servers[0], servers[1])
66 48
67 { 49 {
68 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) 50 const user = await servers[0].users.getMyInfo()
69 const user: User = res.body
70 rootAccountId = user.account.id 51 rootAccountId = user.account.id
71 rootChannelId = user.videoChannels[0].id 52 rootChannelId = user.videoChannels[0].id
72 } 53 }
73 54
74 { 55 {
75 const attr = { username: 'john', password: 'password' } 56 userAccessToken = await servers[0].users.generateUserAndToken('john')
76 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: attr.username, password: attr.password })
77 userAccessToken = await userLogin(servers[0], attr)
78 57
79 const res = await getMyUserInformation(servers[0].url, userAccessToken) 58 const user = await servers[0].users.getMyInfo({ token: userAccessToken })
80 const user: User = res.body
81 userAccountId = user.account.id 59 userAccountId = user.account.id
82 userChannelId = user.videoChannels[0].id 60 userChannelId = user.videoChannels[0].id
83 61
84 const res2 = await getUserScopedTokens(servers[0].url, userAccessToken) 62 const token = await servers[0].users.getMyScopedTokens({ token: userAccessToken })
85 const token: ScopedToken = res2.body
86 userFeedToken = token.feedToken 63 userFeedToken = token.feedToken
87 } 64 }
88 65
89 { 66 {
90 await uploadVideo(servers[0].url, userAccessToken, { name: 'user video' }) 67 await servers[0].videos.upload({ token: userAccessToken, attributes: { name: 'user video' } })
91 } 68 }
92 69
93 { 70 {
94 const videoAttributes = { 71 const attributes = {
95 name: 'my super name for server 1', 72 name: 'my super name for server 1',
96 description: 'my super description for server 1', 73 description: 'my super description for server 1',
97 fixture: 'video_short.webm' 74 fixture: 'video_short.webm'
98 } 75 }
99 const res = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 76 const { id } = await servers[0].videos.upload({ attributes })
100 const videoId = res.body.video.id
101 77
102 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoId, 'super comment 1') 78 await servers[0].comments.createThread({ videoId: id, text: 'super comment 1' })
103 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoId, 'super comment 2') 79 await servers[0].comments.createThread({ videoId: id, text: 'super comment 2' })
104 } 80 }
105 81
106 { 82 {
107 const videoAttributes = { name: 'unlisted video', privacy: VideoPrivacy.UNLISTED } 83 const attributes = { name: 'unlisted video', privacy: VideoPrivacy.UNLISTED }
108 const res = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 84 const { id } = await servers[0].videos.upload({ attributes })
109 const videoId = res.body.video.id
110 85
111 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoId, 'comment on unlisted video') 86 await servers[0].comments.createThread({ videoId: id, text: 'comment on unlisted video' })
112 } 87 }
113 88
114 await waitJobs(servers) 89 await waitJobs(servers)
@@ -118,30 +93,65 @@ describe('Test syndication feeds', () => {
118 93
119 it('Should be well formed XML (covers RSS 2.0 and ATOM 1.0 endpoints)', async function () { 94 it('Should be well formed XML (covers RSS 2.0 and ATOM 1.0 endpoints)', async function () {
120 for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) { 95 for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) {
121 const rss = await getXMLfeed(servers[0].url, feed) 96 const rss = await servers[0].feed.getXML({ feed })
122 expect(rss.text).xml.to.be.valid() 97 expect(rss).xml.to.be.valid()
123 98
124 const atom = await getXMLfeed(servers[0].url, feed, 'atom') 99 const atom = await servers[0].feed.getXML({ feed, format: 'atom' })
125 expect(atom.text).xml.to.be.valid() 100 expect(atom).xml.to.be.valid()
126 } 101 }
127 }) 102 })
128 103
129 it('Should be well formed JSON (covers JSON feed 1.0 endpoint)', async function () { 104 it('Should be well formed JSON (covers JSON feed 1.0 endpoint)', async function () {
130 for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) { 105 for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) {
131 const json = await getJSONfeed(servers[0].url, feed) 106 const jsonText = await servers[0].feed.getJSON({ feed })
132 expect(JSON.parse(json.text)).to.be.jsonSchema({ type: 'object' }) 107 expect(JSON.parse(jsonText)).to.be.jsonSchema({ type: 'object' })
133 } 108 }
134 }) 109 })
110
111 it('Should serve the endpoint with a classic request', async function () {
112 await makeGetRequest({
113 url: servers[0].url,
114 path: '/feeds/videos.xml',
115 accept: 'application/xml',
116 expectedStatus: HttpStatusCode.OK_200
117 })
118 })
119
120 it('Should serve the endpoint as a cached request', async function () {
121 const res = await makeGetRequest({
122 url: servers[0].url,
123 path: '/feeds/videos.xml',
124 accept: 'application/xml',
125 expectedStatus: HttpStatusCode.OK_200
126 })
127
128 expect(res.headers['x-api-cache-cached']).to.equal('true')
129 })
130
131 it('Should not serve the endpoint as a cached request', async function () {
132 const res = await makeGetRequest({
133 url: servers[0].url,
134 path: '/feeds/videos.xml?v=186',
135 accept: 'application/xml',
136 expectedStatus: HttpStatusCode.OK_200
137 })
138
139 expect(res.headers['x-api-cache-cached']).to.not.exist
140 })
141
142 it('Should refuse to serve the endpoint without accept header', async function () {
143 await makeGetRequest({ url: servers[0].url, path: '/feeds/videos.xml', expectedStatus: HttpStatusCode.NOT_ACCEPTABLE_406 })
144 })
135 }) 145 })
136 146
137 describe('Videos feed', function () { 147 describe('Videos feed', function () {
138 148
139 it('Should contain a valid enclosure (covers RSS 2.0 endpoint)', async function () { 149 it('Should contain a valid enclosure (covers RSS 2.0 endpoint)', async function () {
140 for (const server of servers) { 150 for (const server of servers) {
141 const rss = await getXMLfeed(server.url, 'videos') 151 const rss = await server.feed.getXML({ feed: 'videos' })
142 expect(xmlParser.validate(rss.text)).to.be.true 152 expect(xmlParser.validate(rss)).to.be.true
143 153
144 const xmlDoc = xmlParser.parse(rss.text, { parseAttributeValue: true, ignoreAttributes: false }) 154 const xmlDoc = xmlParser.parse(rss, { parseAttributeValue: true, ignoreAttributes: false })
145 155
146 const enclosure = xmlDoc.rss.channel.item[0].enclosure 156 const enclosure = xmlDoc.rss.channel.item[0].enclosure
147 expect(enclosure).to.exist 157 expect(enclosure).to.exist
@@ -153,8 +163,8 @@ describe('Test syndication feeds', () => {
153 163
154 it('Should contain a valid \'attachments\' object (covers JSON feed 1.0 endpoint)', async function () { 164 it('Should contain a valid \'attachments\' object (covers JSON feed 1.0 endpoint)', async function () {
155 for (const server of servers) { 165 for (const server of servers) {
156 const json = await getJSONfeed(server.url, 'videos') 166 const json = await server.feed.getJSON({ feed: 'videos' })
157 const jsonObj = JSON.parse(json.text) 167 const jsonObj = JSON.parse(json)
158 expect(jsonObj.items.length).to.be.equal(2) 168 expect(jsonObj.items.length).to.be.equal(2)
159 expect(jsonObj.items[0].attachments).to.exist 169 expect(jsonObj.items[0].attachments).to.exist
160 expect(jsonObj.items[0].attachments.length).to.be.eq(1) 170 expect(jsonObj.items[0].attachments.length).to.be.eq(1)
@@ -166,16 +176,16 @@ describe('Test syndication feeds', () => {
166 176
167 it('Should filter by account', async function () { 177 it('Should filter by account', async function () {
168 { 178 {
169 const json = await getJSONfeed(servers[0].url, 'videos', { accountId: rootAccountId }) 179 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { accountId: rootAccountId } })
170 const jsonObj = JSON.parse(json.text) 180 const jsonObj = JSON.parse(json)
171 expect(jsonObj.items.length).to.be.equal(1) 181 expect(jsonObj.items.length).to.be.equal(1)
172 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 182 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
173 expect(jsonObj.items[0].author.name).to.equal('root') 183 expect(jsonObj.items[0].author.name).to.equal('root')
174 } 184 }
175 185
176 { 186 {
177 const json = await getJSONfeed(servers[0].url, 'videos', { accountId: userAccountId }) 187 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { accountId: userAccountId } })
178 const jsonObj = JSON.parse(json.text) 188 const jsonObj = JSON.parse(json)
179 expect(jsonObj.items.length).to.be.equal(1) 189 expect(jsonObj.items.length).to.be.equal(1)
180 expect(jsonObj.items[0].title).to.equal('user video') 190 expect(jsonObj.items[0].title).to.equal('user video')
181 expect(jsonObj.items[0].author.name).to.equal('john') 191 expect(jsonObj.items[0].author.name).to.equal('john')
@@ -183,15 +193,15 @@ describe('Test syndication feeds', () => {
183 193
184 for (const server of servers) { 194 for (const server of servers) {
185 { 195 {
186 const json = await getJSONfeed(server.url, 'videos', { accountName: 'root@localhost:' + servers[0].port }) 196 const json = await server.feed.getJSON({ feed: 'videos', query: { accountName: 'root@localhost:' + servers[0].port } })
187 const jsonObj = JSON.parse(json.text) 197 const jsonObj = JSON.parse(json)
188 expect(jsonObj.items.length).to.be.equal(1) 198 expect(jsonObj.items.length).to.be.equal(1)
189 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 199 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
190 } 200 }
191 201
192 { 202 {
193 const json = await getJSONfeed(server.url, 'videos', { accountName: 'john@localhost:' + servers[0].port }) 203 const json = await server.feed.getJSON({ feed: 'videos', query: { accountName: 'john@localhost:' + servers[0].port } })
194 const jsonObj = JSON.parse(json.text) 204 const jsonObj = JSON.parse(json)
195 expect(jsonObj.items.length).to.be.equal(1) 205 expect(jsonObj.items.length).to.be.equal(1)
196 expect(jsonObj.items[0].title).to.equal('user video') 206 expect(jsonObj.items[0].title).to.equal('user video')
197 } 207 }
@@ -200,16 +210,16 @@ describe('Test syndication feeds', () => {
200 210
201 it('Should filter by video channel', async function () { 211 it('Should filter by video channel', async function () {
202 { 212 {
203 const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelId: rootChannelId }) 213 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { videoChannelId: rootChannelId } })
204 const jsonObj = JSON.parse(json.text) 214 const jsonObj = JSON.parse(json)
205 expect(jsonObj.items.length).to.be.equal(1) 215 expect(jsonObj.items.length).to.be.equal(1)
206 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 216 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
207 expect(jsonObj.items[0].author.name).to.equal('root') 217 expect(jsonObj.items[0].author.name).to.equal('root')
208 } 218 }
209 219
210 { 220 {
211 const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelId: userChannelId }) 221 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { videoChannelId: userChannelId } })
212 const jsonObj = JSON.parse(json.text) 222 const jsonObj = JSON.parse(json)
213 expect(jsonObj.items.length).to.be.equal(1) 223 expect(jsonObj.items.length).to.be.equal(1)
214 expect(jsonObj.items[0].title).to.equal('user video') 224 expect(jsonObj.items[0].title).to.equal('user video')
215 expect(jsonObj.items[0].author.name).to.equal('john') 225 expect(jsonObj.items[0].author.name).to.equal('john')
@@ -217,15 +227,17 @@ describe('Test syndication feeds', () => {
217 227
218 for (const server of servers) { 228 for (const server of servers) {
219 { 229 {
220 const json = await getJSONfeed(server.url, 'videos', { videoChannelName: 'root_channel@localhost:' + servers[0].port }) 230 const query = { videoChannelName: 'root_channel@localhost:' + servers[0].port }
221 const jsonObj = JSON.parse(json.text) 231 const json = await server.feed.getJSON({ feed: 'videos', query })
232 const jsonObj = JSON.parse(json)
222 expect(jsonObj.items.length).to.be.equal(1) 233 expect(jsonObj.items.length).to.be.equal(1)
223 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 234 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
224 } 235 }
225 236
226 { 237 {
227 const json = await getJSONfeed(server.url, 'videos', { videoChannelName: 'john_channel@localhost:' + servers[0].port }) 238 const query = { videoChannelName: 'john_channel@localhost:' + servers[0].port }
228 const jsonObj = JSON.parse(json.text) 239 const json = await server.feed.getJSON({ feed: 'videos', query })
240 const jsonObj = JSON.parse(json)
229 expect(jsonObj.items.length).to.be.equal(1) 241 expect(jsonObj.items.length).to.be.equal(1)
230 expect(jsonObj.items[0].title).to.equal('user video') 242 expect(jsonObj.items[0].title).to.equal('user video')
231 } 243 }
@@ -235,12 +247,12 @@ describe('Test syndication feeds', () => {
235 it('Should correctly have videos feed with HLS only', async function () { 247 it('Should correctly have videos feed with HLS only', async function () {
236 this.timeout(120000) 248 this.timeout(120000)
237 249
238 await uploadVideo(serverHLSOnly.url, serverHLSOnly.accessToken, { name: 'hls only video' }) 250 await serverHLSOnly.videos.upload({ attributes: { name: 'hls only video' } })
239 251
240 await waitJobs([ serverHLSOnly ]) 252 await waitJobs([ serverHLSOnly ])
241 253
242 const json = await getJSONfeed(serverHLSOnly.url, 'videos') 254 const json = await serverHLSOnly.feed.getJSON({ feed: 'videos' })
243 const jsonObj = JSON.parse(json.text) 255 const jsonObj = JSON.parse(json)
244 expect(jsonObj.items.length).to.be.equal(1) 256 expect(jsonObj.items.length).to.be.equal(1)
245 expect(jsonObj.items[0].attachments).to.exist 257 expect(jsonObj.items[0].attachments).to.exist
246 expect(jsonObj.items[0].attachments.length).to.be.eq(4) 258 expect(jsonObj.items[0].attachments.length).to.be.eq(4)
@@ -257,9 +269,9 @@ describe('Test syndication feeds', () => {
257 269
258 it('Should contain valid comments (covers JSON feed 1.0 endpoint) and not from unlisted videos', async function () { 270 it('Should contain valid comments (covers JSON feed 1.0 endpoint) and not from unlisted videos', async function () {
259 for (const server of servers) { 271 for (const server of servers) {
260 const json = await getJSONfeed(server.url, 'video-comments') 272 const json = await server.feed.getJSON({ feed: 'video-comments' })
261 273
262 const jsonObj = JSON.parse(json.text) 274 const jsonObj = JSON.parse(json)
263 expect(jsonObj.items.length).to.be.equal(2) 275 expect(jsonObj.items.length).to.be.equal(2)
264 expect(jsonObj.items[0].html_content).to.equal('super comment 2') 276 expect(jsonObj.items[0].html_content).to.equal('super comment 2')
265 expect(jsonObj.items[1].html_content).to.equal('super comment 1') 277 expect(jsonObj.items[1].html_content).to.equal('super comment 1')
@@ -271,32 +283,32 @@ describe('Test syndication feeds', () => {
271 283
272 const remoteHandle = 'root@localhost:' + servers[0].port 284 const remoteHandle = 'root@localhost:' + servers[0].port
273 285
274 await addAccountToServerBlocklist(servers[1].url, servers[1].accessToken, remoteHandle) 286 await servers[1].blocklist.addToServerBlocklist({ account: remoteHandle })
275 287
276 { 288 {
277 const json = await getJSONfeed(servers[1].url, 'video-comments', { version: 2 }) 289 const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 2 } })
278 const jsonObj = JSON.parse(json.text) 290 const jsonObj = JSON.parse(json)
279 expect(jsonObj.items.length).to.be.equal(0) 291 expect(jsonObj.items.length).to.be.equal(0)
280 } 292 }
281 293
282 await removeAccountFromServerBlocklist(servers[1].url, servers[1].accessToken, remoteHandle) 294 await servers[1].blocklist.removeFromServerBlocklist({ account: remoteHandle })
283 295
284 { 296 {
285 const videoUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'server 2' })).uuid 297 const videoUUID = (await servers[1].videos.quickUpload({ name: 'server 2' })).uuid
286 await waitJobs(servers) 298 await waitJobs(servers)
287 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'super comment') 299 await servers[0].comments.createThread({ videoId: videoUUID, text: 'super comment' })
288 await waitJobs(servers) 300 await waitJobs(servers)
289 301
290 const json = await getJSONfeed(servers[1].url, 'video-comments', { version: 3 }) 302 const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 3 } })
291 const jsonObj = JSON.parse(json.text) 303 const jsonObj = JSON.parse(json)
292 expect(jsonObj.items.length).to.be.equal(3) 304 expect(jsonObj.items.length).to.be.equal(3)
293 } 305 }
294 306
295 await addAccountToAccountBlocklist(servers[1].url, servers[1].accessToken, remoteHandle) 307 await servers[1].blocklist.addToMyBlocklist({ account: remoteHandle })
296 308
297 { 309 {
298 const json = await getJSONfeed(servers[1].url, 'video-comments', { version: 4 }) 310 const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 4 } })
299 const jsonObj = JSON.parse(json.text) 311 const jsonObj = JSON.parse(json)
300 expect(jsonObj.items.length).to.be.equal(2) 312 expect(jsonObj.items.length).to.be.equal(2)
301 } 313 }
302 }) 314 })
@@ -308,66 +320,64 @@ describe('Test syndication feeds', () => {
308 320
309 it('Should list no videos for a user with no videos and no subscriptions', async function () { 321 it('Should list no videos for a user with no videos and no subscriptions', async function () {
310 const attr = { username: 'feeduser', password: 'password' } 322 const attr = { username: 'feeduser', password: 'password' }
311 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: attr.username, password: attr.password }) 323 await servers[0].users.create({ username: attr.username, password: attr.password })
312 const feeduserAccessToken = await userLogin(servers[0], attr) 324 const feeduserAccessToken = await servers[0].login.getAccessToken(attr)
313 325
314 { 326 {
315 const res = await getMyUserInformation(servers[0].url, feeduserAccessToken) 327 const user = await servers[0].users.getMyInfo({ token: feeduserAccessToken })
316 const user: User = res.body
317 feeduserAccountId = user.account.id 328 feeduserAccountId = user.account.id
318 } 329 }
319 330
320 { 331 {
321 const res = await getUserScopedTokens(servers[0].url, feeduserAccessToken) 332 const token = await servers[0].users.getMyScopedTokens({ token: feeduserAccessToken })
322 const token: ScopedToken = res.body
323 feeduserFeedToken = token.feedToken 333 feeduserFeedToken = token.feedToken
324 } 334 }
325 335
326 { 336 {
327 const res = await listUserSubscriptionVideos(servers[0].url, feeduserAccessToken) 337 const body = await servers[0].subscriptions.listVideos({ token: feeduserAccessToken })
328 expect(res.body.total).to.equal(0) 338 expect(body.total).to.equal(0)
329 339
330 const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: feeduserAccountId, token: feeduserFeedToken }) 340 const query = { accountId: feeduserAccountId, token: feeduserFeedToken }
331 const jsonObj = JSON.parse(json.text) 341 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query })
342 const jsonObj = JSON.parse(json)
332 expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos 343 expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos
333 } 344 }
334 }) 345 })
335 346
336 it('Should fail with an invalid token', async function () { 347 it('Should fail with an invalid token', async function () {
337 await getJSONfeed(servers[0].url, 'subscriptions', { accountId: feeduserAccountId, token: 'toto' }, HttpStatusCode.FORBIDDEN_403) 348 const query = { accountId: feeduserAccountId, token: 'toto' }
349 await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
338 }) 350 })
339 351
340 it('Should fail with a token of another user', async function () { 352 it('Should fail with a token of another user', async function () {
341 await getJSONfeed( 353 const query = { accountId: feeduserAccountId, token: userFeedToken }
342 servers[0].url, 354 await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
343 'subscriptions',
344 { accountId: feeduserAccountId, token: userFeedToken },
345 HttpStatusCode.FORBIDDEN_403
346 )
347 }) 355 })
348 356
349 it('Should list no videos for a user with videos but no subscriptions', async function () { 357 it('Should list no videos for a user with videos but no subscriptions', async function () {
350 const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken) 358 const body = await servers[0].subscriptions.listVideos({ token: userAccessToken })
351 expect(res.body.total).to.equal(0) 359 expect(body.total).to.equal(0)
352 360
353 const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken }) 361 const query = { accountId: userAccountId, token: userFeedToken }
354 const jsonObj = JSON.parse(json.text) 362 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query })
363 const jsonObj = JSON.parse(json)
355 expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos 364 expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos
356 }) 365 })
357 366
358 it('Should list self videos for a user with a subscription to themselves', async function () { 367 it('Should list self videos for a user with a subscription to themselves', async function () {
359 this.timeout(30000) 368 this.timeout(30000)
360 369
361 await addUserSubscription(servers[0].url, userAccessToken, 'john_channel@localhost:' + servers[0].port) 370 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'john_channel@localhost:' + servers[0].port })
362 await waitJobs(servers) 371 await waitJobs(servers)
363 372
364 { 373 {
365 const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken) 374 const body = await servers[0].subscriptions.listVideos({ token: userAccessToken })
366 expect(res.body.total).to.equal(1) 375 expect(body.total).to.equal(1)
367 expect(res.body.data[0].name).to.equal('user video') 376 expect(body.data[0].name).to.equal('user video')
368 377
369 const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 1 }) 378 const query = { accountId: userAccountId, token: userFeedToken, version: 1 }
370 const jsonObj = JSON.parse(json.text) 379 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query })
380 const jsonObj = JSON.parse(json)
371 expect(jsonObj.items.length).to.be.equal(1) // subscribed to self, it should not list the instance's videos but list john's 381 expect(jsonObj.items.length).to.be.equal(1) // subscribed to self, it should not list the instance's videos but list john's
372 } 382 }
373 }) 383 })
@@ -375,36 +385,33 @@ describe('Test syndication feeds', () => {
375 it('Should list videos of a user\'s subscription', async function () { 385 it('Should list videos of a user\'s subscription', async function () {
376 this.timeout(30000) 386 this.timeout(30000)
377 387
378 await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:' + servers[0].port) 388 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@localhost:' + servers[0].port })
379 await waitJobs(servers) 389 await waitJobs(servers)
380 390
381 { 391 {
382 const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken) 392 const body = await servers[0].subscriptions.listVideos({ token: userAccessToken })
383 expect(res.body.total).to.equal(2, "there should be 2 videos part of the subscription") 393 expect(body.total).to.equal(2, "there should be 2 videos part of the subscription")
384 394
385 const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 2 }) 395 const query = { accountId: userAccountId, token: userFeedToken, version: 2 }
386 const jsonObj = JSON.parse(json.text) 396 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query })
397 const jsonObj = JSON.parse(json)
387 expect(jsonObj.items.length).to.be.equal(2) // subscribed to root, it should not list the instance's videos but list root/john's 398 expect(jsonObj.items.length).to.be.equal(2) // subscribed to root, it should not list the instance's videos but list root/john's
388 } 399 }
389 }) 400 })
390 401
391 it('Should renew the token, and so have an invalid old token', async function () { 402 it('Should renew the token, and so have an invalid old token', async function () {
392 await renewUserScopedTokens(servers[0].url, userAccessToken) 403 await servers[0].users.renewMyScopedTokens({ token: userAccessToken })
393 404
394 await getJSONfeed( 405 const query = { accountId: userAccountId, token: userFeedToken, version: 3 }
395 servers[0].url, 406 await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
396 'subscriptions',
397 { accountId: userAccountId, token: userFeedToken, version: 3 },
398 HttpStatusCode.FORBIDDEN_403
399 )
400 }) 407 })
401 408
402 it('Should succeed with the new token', async function () { 409 it('Should succeed with the new token', async function () {
403 const res2 = await getUserScopedTokens(servers[0].url, userAccessToken) 410 const token = await servers[0].users.getMyScopedTokens({ token: userAccessToken })
404 const token: ScopedToken = res2.body
405 userFeedToken = token.feedToken 411 userFeedToken = token.feedToken
406 412
407 await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 4 }) 413 const query = { accountId: userAccountId, token: userFeedToken, version: 4 }
414 await servers[0].feed.getJSON({ feed: 'subscriptions', query })
408 }) 415 })
409 416
410 }) 417 })
diff --git a/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js b/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js
index 59b136947..c4ae777f5 100644
--- a/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js
+++ b/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js
@@ -18,12 +18,12 @@ async function register ({ transcodingManager }) {
18 const builder = (options) => { 18 const builder = (options) => {
19 return { 19 return {
20 outputOptions: [ 20 outputOptions: [
21 '-r:' + options.streamNum + ' 5' 21 '-r:' + options.streamNum + ' 50'
22 ] 22 ]
23 } 23 }
24 } 24 }
25 25
26 transcodingManager.addLiveProfile('libx264', 'low-live', builder) 26 transcodingManager.addLiveProfile('libx264', 'high-live', builder)
27 } 27 }
28 } 28 }
29 29
@@ -45,7 +45,7 @@ async function register ({ transcodingManager }) {
45 const builder = () => { 45 const builder = () => {
46 return { 46 return {
47 inputOptions: [ 47 inputOptions: [
48 '-r 5' 48 '-r 50'
49 ] 49 ]
50 } 50 }
51 } 51 }
@@ -82,7 +82,6 @@ async function register ({ transcodingManager }) {
82 } 82 }
83} 83}
84 84
85
86async function unregister () { 85async function unregister () {
87 return 86 return
88} 87}
diff --git a/server/tests/fixtures/peertube-plugin-test-video-constants/main.js b/server/tests/fixtures/peertube-plugin-test-video-constants/main.js
index 3e650e0a1..06527bd35 100644
--- a/server/tests/fixtures/peertube-plugin-test-video-constants/main.js
+++ b/server/tests/fixtures/peertube-plugin-test-video-constants/main.js
@@ -1,46 +1,46 @@
1async function register ({ 1async function register ({
2 registerHook,
3 registerSetting,
4 settingsManager,
5 storageManager,
6 videoCategoryManager, 2 videoCategoryManager,
7 videoLicenceManager, 3 videoLicenceManager,
8 videoLanguageManager, 4 videoLanguageManager,
9 videoPrivacyManager, 5 videoPrivacyManager,
10 playlistPrivacyManager 6 playlistPrivacyManager,
7 getRouter
11}) { 8}) {
12 videoLanguageManager.addLanguage('al_bhed', 'Al Bhed') 9 videoLanguageManager.addConstant('al_bhed', 'Al Bhed')
13 videoLanguageManager.addLanguage('al_bhed2', 'Al Bhed 2') 10 videoLanguageManager.addLanguage('al_bhed2', 'Al Bhed 2')
14 videoLanguageManager.addLanguage('al_bhed3', 'Al Bhed 3') 11 videoLanguageManager.addConstant('al_bhed3', 'Al Bhed 3')
15 videoLanguageManager.deleteLanguage('en') 12 videoLanguageManager.deleteConstant('en')
16 videoLanguageManager.deleteLanguage('fr') 13 videoLanguageManager.deleteLanguage('fr')
17 videoLanguageManager.deleteLanguage('al_bhed3') 14 videoLanguageManager.deleteConstant('al_bhed3')
18 15
19 videoCategoryManager.addCategory(42, 'Best category') 16 videoCategoryManager.addCategory(42, 'Best category')
20 videoCategoryManager.addCategory(43, 'High best category') 17 videoCategoryManager.addConstant(43, 'High best category')
21 videoCategoryManager.deleteCategory(1) // Music 18 videoCategoryManager.deleteConstant(1) // Music
22 videoCategoryManager.deleteCategory(2) // Films 19 videoCategoryManager.deleteCategory(2) // Films
23 20
24 videoLicenceManager.addLicence(42, 'Best licence') 21 videoLicenceManager.addLicence(42, 'Best licence')
25 videoLicenceManager.addLicence(43, 'High best licence') 22 videoLicenceManager.addConstant(43, 'High best licence')
26 videoLicenceManager.deleteLicence(1) // Attribution 23 videoLicenceManager.deleteConstant(1) // Attribution
27 videoLicenceManager.deleteLicence(7) // Public domain 24 videoLicenceManager.deleteConstant(7) // Public domain
28 25
26 videoPrivacyManager.deleteConstant(2)
29 videoPrivacyManager.deletePrivacy(2) 27 videoPrivacyManager.deletePrivacy(2)
28 playlistPrivacyManager.deleteConstant(3)
30 playlistPrivacyManager.deletePlaylistPrivacy(3) 29 playlistPrivacyManager.deletePlaylistPrivacy(3)
31}
32 30
33async function unregister () { 31 {
34 return 32 const router = getRouter()
33 router.get('/reset-categories', (req, res) => {
34 videoCategoryManager.resetConstants()
35
36 res.sendStatus(204)
37 })
38 }
35} 39}
36 40
41async function unregister () {}
42
37module.exports = { 43module.exports = {
38 register, 44 register,
39 unregister 45 unregister
40} 46}
41
42// ############################################################################
43
44function addToCount (obj) {
45 return Object.assign({}, obj, { count: obj.count + 1 })
46}
diff --git a/server/tests/fixtures/peertube-plugin-test/main.js b/server/tests/fixtures/peertube-plugin-test/main.js
index f8e6f0b98..db405ff31 100644
--- a/server/tests/fixtures/peertube-plugin-test/main.js
+++ b/server/tests/fixtures/peertube-plugin-test/main.js
@@ -234,7 +234,7 @@ async function register ({ registerHook, registerSetting, settingsManager, stora
234 }) 234 })
235 235
236 { 236 {
237 const searchHooks = [ 237 const filterHooks = [
238 'filter:api.search.videos.local.list.params', 238 'filter:api.search.videos.local.list.params',
239 'filter:api.search.videos.local.list.result', 239 'filter:api.search.videos.local.list.result',
240 'filter:api.search.videos.index.list.params', 240 'filter:api.search.videos.index.list.params',
@@ -246,10 +246,13 @@ async function register ({ registerHook, registerSetting, settingsManager, stora
246 'filter:api.search.video-playlists.local.list.params', 246 'filter:api.search.video-playlists.local.list.params',
247 'filter:api.search.video-playlists.local.list.result', 247 'filter:api.search.video-playlists.local.list.result',
248 'filter:api.search.video-playlists.index.list.params', 248 'filter:api.search.video-playlists.index.list.params',
249 'filter:api.search.video-playlists.index.list.result' 249 'filter:api.search.video-playlists.index.list.result',
250
251 'filter:api.overviews.videos.list.params',
252 'filter:api.overviews.videos.list.result'
250 ] 253 ]
251 254
252 for (const h of searchHooks) { 255 for (const h of filterHooks) {
253 registerHook({ 256 registerHook({
254 target: h, 257 target: h,
255 handler: (obj) => { 258 handler: (obj) => {
diff --git a/server/tests/fixtures/video_very_short_240p.mp4 b/server/tests/fixtures/video_very_short_240p.mp4
new file mode 100644
index 000000000..95b6be92a
--- /dev/null
+++ b/server/tests/fixtures/video_very_short_240p.mp4
Binary files differ
diff --git a/server/tests/helpers/comment-model.ts b/server/tests/helpers/comment-model.ts
index 4c51b7000..31dc6ec72 100644
--- a/server/tests/helpers/comment-model.ts
+++ b/server/tests/helpers/comment-model.ts
@@ -1,7 +1,7 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { VideoCommentModel } from '../../models/video/video-comment' 5import { VideoCommentModel } from '../../models/video/video-comment'
6 6
7const expect = chai.expect 7const expect = chai.expect
diff --git a/server/tests/helpers/core-utils.ts b/server/tests/helpers/core-utils.ts
index c028b316d..d5cac51a3 100644
--- a/server/tests/helpers/core-utils.ts
+++ b/server/tests/helpers/core-utils.ts
@@ -1,10 +1,10 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { snakeCase } from 'lodash' 5import { snakeCase } from 'lodash'
6import { objectConverter, parseBytes } from '../../helpers/core-utils'
7import validator from 'validator' 6import validator from 'validator'
7import { objectConverter, parseBytes } from '../../helpers/core-utils'
8 8
9const expect = chai.expect 9const expect = chai.expect
10 10
diff --git a/server/tests/helpers/image.ts b/server/tests/helpers/image.ts
index 54911697f..9fe9aa4cb 100644
--- a/server/tests/helpers/image.ts
+++ b/server/tests/helpers/image.ts
@@ -1,11 +1,11 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai'
4import { readFile, remove } from 'fs-extra' 5import { readFile, remove } from 'fs-extra'
5import { join } from 'path' 6import { join } from 'path'
6import { processImage } from '../../../server/helpers/image-utils' 7import { processImage } from '../../../server/helpers/image-utils'
7import { buildAbsoluteFixturePath, root } from '../../../shared/extra-utils' 8import { buildAbsoluteFixturePath, root } from '../../../shared/extra-utils'
8import { expect } from 'chai'
9 9
10async function checkBuffers (path1: string, path2: string, equals: boolean) { 10async function checkBuffers (path1: string, path2: string, equals: boolean) {
11 const [ buf1, buf2 ] = await Promise.all([ 11 const [ buf1, buf2 ] = await Promise.all([
diff --git a/server/tests/helpers/request.ts b/server/tests/helpers/request.ts
index 5e77f129e..7f7873df3 100644
--- a/server/tests/helpers/request.ts
+++ b/server/tests/helpers/request.ts
@@ -4,7 +4,7 @@ import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { pathExists, remove } from 'fs-extra' 5import { pathExists, remove } from 'fs-extra'
6import { join } from 'path' 6import { join } from 'path'
7import { get4KFileUrl, root, wait } from '../../../shared/extra-utils' 7import { FIXTURE_URLS, root, wait } from '../../../shared/extra-utils'
8import { doRequest, doRequestAndSaveToFile } from '../../helpers/requests' 8import { doRequest, doRequestAndSaveToFile } from '../../helpers/requests'
9 9
10describe('Request helpers', function () { 10describe('Request helpers', function () {
@@ -13,7 +13,7 @@ describe('Request helpers', function () {
13 13
14 it('Should throw an error when the bytes limit is exceeded for request', async function () { 14 it('Should throw an error when the bytes limit is exceeded for request', async function () {
15 try { 15 try {
16 await doRequest(get4KFileUrl(), { bodyKBLimit: 3 }) 16 await doRequest(FIXTURE_URLS.video4K, { bodyKBLimit: 3 })
17 } catch { 17 } catch {
18 return 18 return
19 } 19 }
@@ -23,7 +23,7 @@ describe('Request helpers', function () {
23 23
24 it('Should throw an error when the bytes limit is exceeded for request and save file', async function () { 24 it('Should throw an error when the bytes limit is exceeded for request and save file', async function () {
25 try { 25 try {
26 await doRequestAndSaveToFile(get4KFileUrl(), destPath1, { bodyKBLimit: 3 }) 26 await doRequestAndSaveToFile(FIXTURE_URLS.video4K, destPath1, { bodyKBLimit: 3 })
27 } catch { 27 } catch {
28 28
29 await wait(500) 29 await wait(500)
@@ -35,8 +35,8 @@ describe('Request helpers', function () {
35 }) 35 })
36 36
37 it('Should succeed if the file is below the limit', async function () { 37 it('Should succeed if the file is below the limit', async function () {
38 await doRequest(get4KFileUrl(), { bodyKBLimit: 5 }) 38 await doRequest(FIXTURE_URLS.video4K, { bodyKBLimit: 5 })
39 await doRequestAndSaveToFile(get4KFileUrl(), destPath2, { bodyKBLimit: 5 }) 39 await doRequestAndSaveToFile(FIXTURE_URLS.video4K, destPath2, { bodyKBLimit: 5 })
40 40
41 expect(await pathExists(destPath2)).to.be.true 41 expect(await pathExists(destPath2)).to.be.true
42 }) 42 })
diff --git a/server/tests/index.ts b/server/tests/index.ts
index 3fbd0ebbd..1718ac424 100644
--- a/server/tests/index.ts
+++ b/server/tests/index.ts
@@ -6,3 +6,4 @@ import './cli/'
6import './api/' 6import './api/'
7import './plugins/' 7import './plugins/'
8import './helpers/' 8import './helpers/'
9import './lib/'
diff --git a/server/tests/lib/index.ts b/server/tests/lib/index.ts
new file mode 100644
index 000000000..a40df35fd
--- /dev/null
+++ b/server/tests/lib/index.ts
@@ -0,0 +1 @@
export * from './video-constant-registry-factory'
diff --git a/server/tests/lib/video-constant-registry-factory.ts b/server/tests/lib/video-constant-registry-factory.ts
new file mode 100644
index 000000000..e26b286e1
--- /dev/null
+++ b/server/tests/lib/video-constant-registry-factory.ts
@@ -0,0 +1,155 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions */
2import 'mocha'
3import { expect } from 'chai'
4import { VideoConstantManagerFactory } from '@server/lib/plugins/video-constant-manager-factory'
5import {
6 VIDEO_CATEGORIES,
7 VIDEO_LANGUAGES,
8 VIDEO_LICENCES,
9 VIDEO_PLAYLIST_PRIVACIES,
10 VIDEO_PRIVACIES
11} from '@server/initializers/constants'
12import {
13 VideoPlaylistPrivacy,
14 VideoPrivacy
15} from '@shared/models'
16
17describe('VideoConstantManagerFactory', function () {
18 const factory = new VideoConstantManagerFactory('peertube-plugin-constants')
19
20 afterEach(() => {
21 factory.resetVideoConstants('peertube-plugin-constants')
22 })
23
24 describe('VideoCategoryManager', () => {
25 const videoCategoryManager = factory.createVideoConstantManager<number>('category')
26
27 it('Should be able to list all video category constants', () => {
28 const constants = videoCategoryManager.getConstants()
29 expect(constants).to.deep.equal(VIDEO_CATEGORIES)
30 })
31
32 it('Should be able to delete a video category constant', () => {
33 const successfullyDeleted = videoCategoryManager.deleteConstant(1)
34 expect(successfullyDeleted).to.be.true
35 expect(videoCategoryManager.getConstantValue(1)).to.be.undefined
36 })
37
38 it('Should be able to add a video category constant', () => {
39 const successfullyAdded = videoCategoryManager.addConstant(42, 'The meaning of life')
40 expect(successfullyAdded).to.be.true
41 expect(videoCategoryManager.getConstantValue(42)).to.equal('The meaning of life')
42 })
43
44 it('Should be able to reset video category constants', () => {
45 videoCategoryManager.deleteConstant(1)
46 videoCategoryManager.resetConstants()
47 expect(videoCategoryManager.getConstantValue(1)).not.be.undefined
48 })
49 })
50
51 describe('VideoLicenceManager', () => {
52 const videoLicenceManager = factory.createVideoConstantManager<number>('licence')
53 it('Should be able to list all video licence constants', () => {
54 const constants = videoLicenceManager.getConstants()
55 expect(constants).to.deep.equal(VIDEO_LICENCES)
56 })
57
58 it('Should be able to delete a video licence constant', () => {
59 const successfullyDeleted = videoLicenceManager.deleteConstant(1)
60 expect(successfullyDeleted).to.be.true
61 expect(videoLicenceManager.getConstantValue(1)).to.be.undefined
62 })
63
64 it('Should be able to add a video licence constant', () => {
65 const successfullyAdded = videoLicenceManager.addConstant(42, 'European Union Public Licence')
66 expect(successfullyAdded).to.be.true
67 expect(videoLicenceManager.getConstantValue(42)).to.equal('European Union Public Licence')
68 })
69
70 it('Should be able to reset video licence constants', () => {
71 videoLicenceManager.deleteConstant(1)
72 videoLicenceManager.resetConstants()
73 expect(videoLicenceManager.getConstantValue(1)).not.be.undefined
74 })
75 })
76
77 describe('PlaylistPrivacyManager', () => {
78 const playlistPrivacyManager = factory.createVideoConstantManager<VideoPlaylistPrivacy>('playlistPrivacy')
79 it('Should be able to list all video playlist privacy constants', () => {
80 const constants = playlistPrivacyManager.getConstants()
81 expect(constants).to.deep.equal(VIDEO_PLAYLIST_PRIVACIES)
82 })
83
84 it('Should be able to delete a video playlist privacy constant', () => {
85 const successfullyDeleted = playlistPrivacyManager.deleteConstant(1)
86 expect(successfullyDeleted).to.be.true
87 expect(playlistPrivacyManager.getConstantValue(1)).to.be.undefined
88 })
89
90 it('Should be able to add a video playlist privacy constant', () => {
91 const successfullyAdded = playlistPrivacyManager.addConstant(42, 'Friends only')
92 expect(successfullyAdded).to.be.true
93 expect(playlistPrivacyManager.getConstantValue(42)).to.equal('Friends only')
94 })
95
96 it('Should be able to reset video playlist privacy constants', () => {
97 playlistPrivacyManager.deleteConstant(1)
98 playlistPrivacyManager.resetConstants()
99 expect(playlistPrivacyManager.getConstantValue(1)).not.be.undefined
100 })
101 })
102
103 describe('VideoPrivacyManager', () => {
104 const videoPrivacyManager = factory.createVideoConstantManager<VideoPrivacy>('privacy')
105 it('Should be able to list all video privacy constants', () => {
106 const constants = videoPrivacyManager.getConstants()
107 expect(constants).to.deep.equal(VIDEO_PRIVACIES)
108 })
109
110 it('Should be able to delete a video privacy constant', () => {
111 const successfullyDeleted = videoPrivacyManager.deleteConstant(1)
112 expect(successfullyDeleted).to.be.true
113 expect(videoPrivacyManager.getConstantValue(1)).to.be.undefined
114 })
115
116 it('Should be able to add a video privacy constant', () => {
117 const successfullyAdded = videoPrivacyManager.addConstant(42, 'Friends only')
118 expect(successfullyAdded).to.be.true
119 expect(videoPrivacyManager.getConstantValue(42)).to.equal('Friends only')
120 })
121
122 it('Should be able to reset video privacy constants', () => {
123 videoPrivacyManager.deleteConstant(1)
124 videoPrivacyManager.resetConstants()
125 expect(videoPrivacyManager.getConstantValue(1)).not.be.undefined
126 })
127 })
128
129 describe('VideoLanguageManager', () => {
130 const videoLanguageManager = factory.createVideoConstantManager<string>('language')
131 it('Should be able to list all video language constants', () => {
132 const constants = videoLanguageManager.getConstants()
133 expect(constants).to.deep.equal(VIDEO_LANGUAGES)
134 })
135
136 it('Should be able to add a video language constant', () => {
137 const successfullyAdded = videoLanguageManager.addConstant('fr', 'Fr occitan')
138 expect(successfullyAdded).to.be.true
139 expect(videoLanguageManager.getConstantValue('fr')).to.equal('Fr occitan')
140 })
141
142 it('Should be able to delete a video language constant', () => {
143 videoLanguageManager.addConstant('fr', 'Fr occitan')
144 const successfullyDeleted = videoLanguageManager.deleteConstant('fr')
145 expect(successfullyDeleted).to.be.true
146 expect(videoLanguageManager.getConstantValue('fr')).to.be.undefined
147 })
148
149 it('Should be able to reset video language constants', () => {
150 videoLanguageManager.addConstant('fr', 'Fr occitan')
151 videoLanguageManager.resetConstants()
152 expect(videoLanguageManager.getConstantValue('fr')).to.be.undefined
153 })
154 })
155})
diff --git a/server/tests/misc-endpoints.ts b/server/tests/misc-endpoints.ts
index 09e5afcf9..4968eef08 100644
--- a/server/tests/misc-endpoints.ts
+++ b/server/tests/misc-endpoints.ts
@@ -2,28 +2,18 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import { cleanupTests, createSingleServer, makeGetRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
6 addVideoChannel, 6import { HttpStatusCode, VideoPrivacy } from '@shared/models'
7 cleanupTests,
8 createUser,
9 flushAndRunServer,
10 makeGetRequest,
11 ServerInfo,
12 setAccessTokensToServers,
13 uploadVideo
14} from '../../shared/extra-utils'
15import { VideoPrivacy } from '../../shared/models/videos'
16import { HttpStatusCode } from '@shared/core-utils'
17 7
18const expect = chai.expect 8const expect = chai.expect
19 9
20describe('Test misc endpoints', function () { 10describe('Test misc endpoints', function () {
21 let server: ServerInfo 11 let server: PeerTubeServer
22 12
23 before(async function () { 13 before(async function () {
24 this.timeout(120000) 14 this.timeout(120000)
25 15
26 server = await flushAndRunServer(1) 16 server = await createSingleServer(1)
27 await setAccessTokensToServers([ server ]) 17 await setAccessTokensToServers([ server ])
28 }) 18 })
29 19
@@ -33,7 +23,7 @@ describe('Test misc endpoints', function () {
33 const res = await makeGetRequest({ 23 const res = await makeGetRequest({
34 url: server.url, 24 url: server.url,
35 path: '/.well-known/security.txt', 25 path: '/.well-known/security.txt',
36 statusCodeExpected: HttpStatusCode.OK_200 26 expectedStatus: HttpStatusCode.OK_200
37 }) 27 })
38 28
39 expect(res.text).to.contain('security issue') 29 expect(res.text).to.contain('security issue')
@@ -43,7 +33,7 @@ describe('Test misc endpoints', function () {
43 const res = await makeGetRequest({ 33 const res = await makeGetRequest({
44 url: server.url, 34 url: server.url,
45 path: '/.well-known/nodeinfo', 35 path: '/.well-known/nodeinfo',
46 statusCodeExpected: HttpStatusCode.OK_200 36 expectedStatus: HttpStatusCode.OK_200
47 }) 37 })
48 38
49 expect(res.body.links).to.be.an('array') 39 expect(res.body.links).to.be.an('array')
@@ -55,7 +45,7 @@ describe('Test misc endpoints', function () {
55 const res = await makeGetRequest({ 45 const res = await makeGetRequest({
56 url: server.url, 46 url: server.url,
57 path: '/.well-known/dnt-policy.txt', 47 path: '/.well-known/dnt-policy.txt',
58 statusCodeExpected: HttpStatusCode.OK_200 48 expectedStatus: HttpStatusCode.OK_200
59 }) 49 })
60 50
61 expect(res.text).to.contain('http://www.w3.org/TR/tracking-dnt') 51 expect(res.text).to.contain('http://www.w3.org/TR/tracking-dnt')
@@ -65,7 +55,7 @@ describe('Test misc endpoints', function () {
65 const res = await makeGetRequest({ 55 const res = await makeGetRequest({
66 url: server.url, 56 url: server.url,
67 path: '/.well-known/dnt', 57 path: '/.well-known/dnt',
68 statusCodeExpected: HttpStatusCode.OK_200 58 expectedStatus: HttpStatusCode.OK_200
69 }) 59 })
70 60
71 expect(res.body.tracking).to.equal('N') 61 expect(res.body.tracking).to.equal('N')
@@ -75,7 +65,7 @@ describe('Test misc endpoints', function () {
75 const res = await makeGetRequest({ 65 const res = await makeGetRequest({
76 url: server.url, 66 url: server.url,
77 path: '/.well-known/change-password', 67 path: '/.well-known/change-password',
78 statusCodeExpected: HttpStatusCode.FOUND_302 68 expectedStatus: HttpStatusCode.FOUND_302
79 }) 69 })
80 70
81 expect(res.header.location).to.equal('/my-account/settings') 71 expect(res.header.location).to.equal('/my-account/settings')
@@ -88,7 +78,7 @@ describe('Test misc endpoints', function () {
88 const res = await makeGetRequest({ 78 const res = await makeGetRequest({
89 url: server.url, 79 url: server.url,
90 path: '/.well-known/webfinger?resource=' + resource, 80 path: '/.well-known/webfinger?resource=' + resource,
91 statusCodeExpected: HttpStatusCode.OK_200 81 expectedStatus: HttpStatusCode.OK_200
92 }) 82 })
93 83
94 const data = res.body 84 const data = res.body
@@ -113,7 +103,7 @@ describe('Test misc endpoints', function () {
113 const res = await makeGetRequest({ 103 const res = await makeGetRequest({
114 url: server.url, 104 url: server.url,
115 path: '/robots.txt', 105 path: '/robots.txt',
116 statusCodeExpected: HttpStatusCode.OK_200 106 expectedStatus: HttpStatusCode.OK_200
117 }) 107 })
118 108
119 expect(res.text).to.contain('User-agent') 109 expect(res.text).to.contain('User-agent')
@@ -123,7 +113,7 @@ describe('Test misc endpoints', function () {
123 await makeGetRequest({ 113 await makeGetRequest({
124 url: server.url, 114 url: server.url,
125 path: '/security.txt', 115 path: '/security.txt',
126 statusCodeExpected: HttpStatusCode.MOVED_PERMANENTLY_301 116 expectedStatus: HttpStatusCode.MOVED_PERMANENTLY_301
127 }) 117 })
128 }) 118 })
129 119
@@ -131,7 +121,7 @@ describe('Test misc endpoints', function () {
131 const res = await makeGetRequest({ 121 const res = await makeGetRequest({
132 url: server.url, 122 url: server.url,
133 path: '/nodeinfo/2.0.json', 123 path: '/nodeinfo/2.0.json',
134 statusCodeExpected: HttpStatusCode.OK_200 124 expectedStatus: HttpStatusCode.OK_200
135 }) 125 })
136 126
137 expect(res.body.software.name).to.equal('peertube') 127 expect(res.body.software.name).to.equal('peertube')
@@ -146,7 +136,7 @@ describe('Test misc endpoints', function () {
146 const res = await makeGetRequest({ 136 const res = await makeGetRequest({
147 url: server.url, 137 url: server.url,
148 path: '/sitemap.xml', 138 path: '/sitemap.xml',
149 statusCodeExpected: HttpStatusCode.OK_200 139 expectedStatus: HttpStatusCode.OK_200
150 }) 140 })
151 141
152 expect(res.text).to.contain('xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"') 142 expect(res.text).to.contain('xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"')
@@ -157,7 +147,7 @@ describe('Test misc endpoints', function () {
157 const res = await makeGetRequest({ 147 const res = await makeGetRequest({
158 url: server.url, 148 url: server.url,
159 path: '/sitemap.xml', 149 path: '/sitemap.xml',
160 statusCodeExpected: HttpStatusCode.OK_200 150 expectedStatus: HttpStatusCode.OK_200
161 }) 151 })
162 152
163 expect(res.text).to.contain('xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"') 153 expect(res.text).to.contain('xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"')
@@ -167,20 +157,20 @@ describe('Test misc endpoints', function () {
167 it('Should add videos, channel and accounts and get sitemap', async function () { 157 it('Should add videos, channel and accounts and get sitemap', async function () {
168 this.timeout(35000) 158 this.timeout(35000)
169 159
170 await uploadVideo(server.url, server.accessToken, { name: 'video 1', nsfw: false }) 160 await server.videos.upload({ attributes: { name: 'video 1', nsfw: false } })
171 await uploadVideo(server.url, server.accessToken, { name: 'video 2', nsfw: false }) 161 await server.videos.upload({ attributes: { name: 'video 2', nsfw: false } })
172 await uploadVideo(server.url, server.accessToken, { name: 'video 3', privacy: VideoPrivacy.PRIVATE }) 162 await server.videos.upload({ attributes: { name: 'video 3', privacy: VideoPrivacy.PRIVATE } })
173 163
174 await addVideoChannel(server.url, server.accessToken, { name: 'channel1', displayName: 'channel 1' }) 164 await server.channels.create({ attributes: { name: 'channel1', displayName: 'channel 1' } })
175 await addVideoChannel(server.url, server.accessToken, { name: 'channel2', displayName: 'channel 2' }) 165 await server.channels.create({ attributes: { name: 'channel2', displayName: 'channel 2' } })
176 166
177 await createUser({ url: server.url, accessToken: server.accessToken, username: 'user1', password: 'password' }) 167 await server.users.create({ username: 'user1', password: 'password' })
178 await createUser({ url: server.url, accessToken: server.accessToken, username: 'user2', password: 'password' }) 168 await server.users.create({ username: 'user2', password: 'password' })
179 169
180 const res = await makeGetRequest({ 170 const res = await makeGetRequest({
181 url: server.url, 171 url: server.url,
182 path: '/sitemap.xml?t=1', // avoid using cache 172 path: '/sitemap.xml?t=1', // avoid using cache
183 statusCodeExpected: HttpStatusCode.OK_200 173 expectedStatus: HttpStatusCode.OK_200
184 }) 174 })
185 175
186 expect(res.text).to.contain('xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"') 176 expect(res.text).to.contain('xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"')
diff --git a/server/tests/plugins/action-hooks.ts b/server/tests/plugins/action-hooks.ts
index 0f57ef7fe..4c1bc7d06 100644
--- a/server/tests/plugins/action-hooks.ts
+++ b/server/tests/plugins/action-hooks.ts
@@ -1,63 +1,38 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { ServerHookName, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
5import {
6 addVideoCommentReply,
7 addVideoCommentThread,
8 addVideoInPlaylist,
9 blockUser,
10 createLive,
11 createUser,
12 createVideoPlaylist,
13 deleteVideoComment,
14 getPluginTestPath,
15 installPlugin,
16 registerUser,
17 removeUser,
18 setAccessTokensToServers,
19 setDefaultVideoChannel,
20 unblockUser,
21 updateUser,
22 updateVideo,
23 uploadVideo,
24 userLogin,
25 viewVideo
26} from '../../../shared/extra-utils'
27import { 4import {
28 cleanupTests, 5 cleanupTests,
29 flushAndRunMultipleServers, 6 createMultipleServers,
30 killallServers, 7 killallServers,
31 reRunServer, 8 PeerTubeServer,
32 ServerInfo, 9 PluginsCommand,
33 waitUntilLog 10 setAccessTokensToServers,
34} from '../../../shared/extra-utils/server/servers' 11 setDefaultVideoChannel
12} from '@shared/extra-utils'
13import { ServerHookName, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
35 14
36describe('Test plugin action hooks', function () { 15describe('Test plugin action hooks', function () {
37 let servers: ServerInfo[] 16 let servers: PeerTubeServer[]
38 let videoUUID: string 17 let videoUUID: string
39 let threadId: number 18 let threadId: number
40 19
41 function checkHook (hook: ServerHookName) { 20 function checkHook (hook: ServerHookName) {
42 return waitUntilLog(servers[0], 'Run hook ' + hook) 21 return servers[0].servers.waitUntilLog('Run hook ' + hook)
43 } 22 }
44 23
45 before(async function () { 24 before(async function () {
46 this.timeout(30000) 25 this.timeout(30000)
47 26
48 servers = await flushAndRunMultipleServers(2) 27 servers = await createMultipleServers(2)
49 await setAccessTokensToServers(servers) 28 await setAccessTokensToServers(servers)
50 await setDefaultVideoChannel(servers) 29 await setDefaultVideoChannel(servers)
51 30
52 await installPlugin({ 31 await servers[0].plugins.install({ path: PluginsCommand.getPluginTestPath() })
53 url: servers[0].url,
54 accessToken: servers[0].accessToken,
55 path: getPluginTestPath()
56 })
57 32
58 killallServers([ servers[0] ]) 33 await killallServers([ servers[0] ])
59 34
60 await reRunServer(servers[0], { 35 await servers[0].run({
61 live: { 36 live: {
62 enabled: true 37 enabled: true
63 } 38 }
@@ -73,20 +48,20 @@ describe('Test plugin action hooks', function () {
73 describe('Videos hooks', function () { 48 describe('Videos hooks', function () {
74 49
75 it('Should run action:api.video.uploaded', async function () { 50 it('Should run action:api.video.uploaded', async function () {
76 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' }) 51 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video' } })
77 videoUUID = res.body.video.uuid 52 videoUUID = uuid
78 53
79 await checkHook('action:api.video.uploaded') 54 await checkHook('action:api.video.uploaded')
80 }) 55 })
81 56
82 it('Should run action:api.video.updated', async function () { 57 it('Should run action:api.video.updated', async function () {
83 await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, { name: 'video updated' }) 58 await servers[0].videos.update({ id: videoUUID, attributes: { name: 'video updated' } })
84 59
85 await checkHook('action:api.video.updated') 60 await checkHook('action:api.video.updated')
86 }) 61 })
87 62
88 it('Should run action:api.video.viewed', async function () { 63 it('Should run action:api.video.viewed', async function () {
89 await viewVideo(servers[0].url, videoUUID) 64 await servers[0].videos.view({ id: videoUUID })
90 65
91 await checkHook('action:api.video.viewed') 66 await checkHook('action:api.video.viewed')
92 }) 67 })
@@ -98,10 +73,10 @@ describe('Test plugin action hooks', function () {
98 const attributes = { 73 const attributes = {
99 name: 'live', 74 name: 'live',
100 privacy: VideoPrivacy.PUBLIC, 75 privacy: VideoPrivacy.PUBLIC,
101 channelId: servers[0].videoChannel.id 76 channelId: servers[0].store.channel.id
102 } 77 }
103 78
104 await createLive(servers[0].url, servers[0].accessToken, attributes) 79 await servers[0].live.create({ fields: attributes })
105 80
106 await checkHook('action:api.live-video.created') 81 await checkHook('action:api.live-video.created')
107 }) 82 })
@@ -109,20 +84,20 @@ describe('Test plugin action hooks', function () {
109 84
110 describe('Comments hooks', function () { 85 describe('Comments hooks', function () {
111 it('Should run action:api.video-thread.created', async function () { 86 it('Should run action:api.video-thread.created', async function () {
112 const res = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'thread') 87 const created = await servers[0].comments.createThread({ videoId: videoUUID, text: 'thread' })
113 threadId = res.body.comment.id 88 threadId = created.id
114 89
115 await checkHook('action:api.video-thread.created') 90 await checkHook('action:api.video-thread.created')
116 }) 91 })
117 92
118 it('Should run action:api.video-comment-reply.created', async function () { 93 it('Should run action:api.video-comment-reply.created', async function () {
119 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID, threadId, 'reply') 94 await servers[0].comments.addReply({ videoId: videoUUID, toCommentId: threadId, text: 'reply' })
120 95
121 await checkHook('action:api.video-comment-reply.created') 96 await checkHook('action:api.video-comment-reply.created')
122 }) 97 })
123 98
124 it('Should run action:api.video-comment.deleted', async function () { 99 it('Should run action:api.video-comment.deleted', async function () {
125 await deleteVideoComment(servers[0].url, servers[0].accessToken, videoUUID, threadId) 100 await servers[0].comments.delete({ videoId: videoUUID, commentId: threadId })
126 101
127 await checkHook('action:api.video-comment.deleted') 102 await checkHook('action:api.video-comment.deleted')
128 }) 103 })
@@ -132,49 +107,44 @@ describe('Test plugin action hooks', function () {
132 let userId: number 107 let userId: number
133 108
134 it('Should run action:api.user.registered', async function () { 109 it('Should run action:api.user.registered', async function () {
135 await registerUser(servers[0].url, 'registered_user', 'super_password') 110 await servers[0].users.register({ username: 'registered_user' })
136 111
137 await checkHook('action:api.user.registered') 112 await checkHook('action:api.user.registered')
138 }) 113 })
139 114
140 it('Should run action:api.user.created', async function () { 115 it('Should run action:api.user.created', async function () {
141 const res = await createUser({ 116 const user = await servers[0].users.create({ username: 'created_user' })
142 url: servers[0].url, 117 userId = user.id
143 accessToken: servers[0].accessToken,
144 username: 'created_user',
145 password: 'super_password'
146 })
147 userId = res.body.user.id
148 118
149 await checkHook('action:api.user.created') 119 await checkHook('action:api.user.created')
150 }) 120 })
151 121
152 it('Should run action:api.user.oauth2-got-token', async function () { 122 it('Should run action:api.user.oauth2-got-token', async function () {
153 await userLogin(servers[0], { username: 'created_user', password: 'super_password' }) 123 await servers[0].login.login({ user: { username: 'created_user' } })
154 124
155 await checkHook('action:api.user.oauth2-got-token') 125 await checkHook('action:api.user.oauth2-got-token')
156 }) 126 })
157 127
158 it('Should run action:api.user.blocked', async function () { 128 it('Should run action:api.user.blocked', async function () {
159 await blockUser(servers[0].url, userId, servers[0].accessToken) 129 await servers[0].users.banUser({ userId })
160 130
161 await checkHook('action:api.user.blocked') 131 await checkHook('action:api.user.blocked')
162 }) 132 })
163 133
164 it('Should run action:api.user.unblocked', async function () { 134 it('Should run action:api.user.unblocked', async function () {
165 await unblockUser(servers[0].url, userId, servers[0].accessToken) 135 await servers[0].users.unbanUser({ userId })
166 136
167 await checkHook('action:api.user.unblocked') 137 await checkHook('action:api.user.unblocked')
168 }) 138 })
169 139
170 it('Should run action:api.user.updated', async function () { 140 it('Should run action:api.user.updated', async function () {
171 await updateUser({ url: servers[0].url, accessToken: servers[0].accessToken, userId, videoQuota: 50 }) 141 await servers[0].users.update({ userId, videoQuota: 50 })
172 142
173 await checkHook('action:api.user.updated') 143 await checkHook('action:api.user.updated')
174 }) 144 })
175 145
176 it('Should run action:api.user.deleted', async function () { 146 it('Should run action:api.user.deleted', async function () {
177 await removeUser(servers[0].url, userId, servers[0].accessToken) 147 await servers[0].users.remove({ userId })
178 148
179 await checkHook('action:api.user.deleted') 149 await checkHook('action:api.user.deleted')
180 }) 150 })
@@ -186,30 +156,23 @@ describe('Test plugin action hooks', function () {
186 156
187 before(async function () { 157 before(async function () {
188 { 158 {
189 const res = await createVideoPlaylist({ 159 const { id } = await servers[0].playlists.create({
190 url: servers[0].url, 160 attributes: {
191 token: servers[0].accessToken,
192 playlistAttrs: {
193 displayName: 'My playlist', 161 displayName: 'My playlist',
194 privacy: VideoPlaylistPrivacy.PRIVATE 162 privacy: VideoPlaylistPrivacy.PRIVATE
195 } 163 }
196 }) 164 })
197 playlistId = res.body.videoPlaylist.id 165 playlistId = id
198 } 166 }
199 167
200 { 168 {
201 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'my super name' }) 169 const { id } = await servers[0].videos.upload({ attributes: { name: 'my super name' } })
202 videoId = res.body.video.id 170 videoId = id
203 } 171 }
204 }) 172 })
205 173
206 it('Should run action:api.video-playlist-element.created', async function () { 174 it('Should run action:api.video-playlist-element.created', async function () {
207 await addVideoInPlaylist({ 175 await servers[0].playlists.addElement({ playlistId, attributes: { videoId } })
208 url: servers[0].url,
209 token: servers[0].accessToken,
210 playlistId,
211 elementAttrs: { videoId }
212 })
213 176
214 await checkHook('action:api.video-playlist-element.created') 177 await checkHook('action:api.video-playlist-element.created')
215 }) 178 })
diff --git a/server/tests/plugins/external-auth.ts b/server/tests/plugins/external-auth.ts
index 5addb45c7..f3e018d43 100644
--- a/server/tests/plugins/external-auth.ts
+++ b/server/tests/plugins/external-auth.ts
@@ -2,44 +2,32 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { ServerConfig, User, UserRole } from '@shared/models'
6import { 5import {
6 cleanupTests,
7 createSingleServer,
7 decodeQueryString, 8 decodeQueryString,
8 getConfig, 9 PeerTubeServer,
9 getExternalAuth, 10 PluginsCommand,
10 getMyUserInformation,
11 getPluginTestPath,
12 installPlugin,
13 loginUsingExternalToken,
14 logout,
15 refreshToken,
16 setAccessTokensToServers, 11 setAccessTokensToServers,
17 uninstallPlugin, 12 wait
18 updateMyUser, 13} from '@shared/extra-utils'
19 wait, 14import { HttpStatusCode, UserRole } from '@shared/models'
20 userLogin,
21 updatePluginSettings,
22 createUser
23} from '../../../shared/extra-utils'
24import { cleanupTests, flushAndRunServer, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers'
25import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
26 15
27async function loginExternal (options: { 16async function loginExternal (options: {
28 server: ServerInfo 17 server: PeerTubeServer
29 npmName: string 18 npmName: string
30 authName: string 19 authName: string
31 username: string 20 username: string
32 query?: any 21 query?: any
33 statusCodeExpected?: HttpStatusCode 22 expectedStatus?: HttpStatusCode
34 statusCodeExpectedStep2?: HttpStatusCode 23 expectedStatusStep2?: HttpStatusCode
35}) { 24}) {
36 const res = await getExternalAuth({ 25 const res = await options.server.plugins.getExternalAuth({
37 url: options.server.url,
38 npmName: options.npmName, 26 npmName: options.npmName,
39 npmVersion: '0.0.1', 27 npmVersion: '0.0.1',
40 authName: options.authName, 28 authName: options.authName,
41 query: options.query, 29 query: options.query,
42 statusCodeExpected: options.statusCodeExpected || HttpStatusCode.FOUND_302 30 expectedStatus: options.expectedStatus || HttpStatusCode.FOUND_302
43 }) 31 })
44 32
45 if (res.status !== HttpStatusCode.FOUND_302) return 33 if (res.status !== HttpStatusCode.FOUND_302) return
@@ -47,18 +35,17 @@ async function loginExternal (options: {
47 const location = res.header.location 35 const location = res.header.location
48 const { externalAuthToken } = decodeQueryString(location) 36 const { externalAuthToken } = decodeQueryString(location)
49 37
50 const resLogin = await loginUsingExternalToken( 38 const resLogin = await options.server.login.loginUsingExternalToken({
51 options.server, 39 username: options.username,
52 options.username, 40 externalAuthToken: externalAuthToken as string,
53 externalAuthToken as string, 41 expectedStatus: options.expectedStatusStep2
54 options.statusCodeExpectedStep2 42 })
55 )
56 43
57 return resLogin.body 44 return resLogin.body
58} 45}
59 46
60describe('Test external auth plugins', function () { 47describe('Test external auth plugins', function () {
61 let server: ServerInfo 48 let server: PeerTubeServer
62 49
63 let cyanAccessToken: string 50 let cyanAccessToken: string
64 let cyanRefreshToken: string 51 let cyanRefreshToken: string
@@ -71,22 +58,16 @@ describe('Test external auth plugins', function () {
71 before(async function () { 58 before(async function () {
72 this.timeout(30000) 59 this.timeout(30000)
73 60
74 server = await flushAndRunServer(1) 61 server = await createSingleServer(1)
75 await setAccessTokensToServers([ server ]) 62 await setAccessTokensToServers([ server ])
76 63
77 for (const suffix of [ 'one', 'two', 'three' ]) { 64 for (const suffix of [ 'one', 'two', 'three' ]) {
78 await installPlugin({ 65 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-external-auth-' + suffix) })
79 url: server.url,
80 accessToken: server.accessToken,
81 path: getPluginTestPath('-external-auth-' + suffix)
82 })
83 } 66 }
84 }) 67 })
85 68
86 it('Should display the correct configuration', async function () { 69 it('Should display the correct configuration', async function () {
87 const res = await getConfig(server.url) 70 const config = await server.config.getConfig()
88
89 const config: ServerConfig = res.body
90 71
91 const auths = config.plugin.registeredExternalAuths 72 const auths = config.plugin.registeredExternalAuths
92 expect(auths).to.have.lengthOf(8) 73 expect(auths).to.have.lengthOf(8)
@@ -98,15 +79,14 @@ describe('Test external auth plugins', function () {
98 }) 79 })
99 80
100 it('Should redirect for a Cyan login', async function () { 81 it('Should redirect for a Cyan login', async function () {
101 const res = await getExternalAuth({ 82 const res = await server.plugins.getExternalAuth({
102 url: server.url,
103 npmName: 'test-external-auth-one', 83 npmName: 'test-external-auth-one',
104 npmVersion: '0.0.1', 84 npmVersion: '0.0.1',
105 authName: 'external-auth-1', 85 authName: 'external-auth-1',
106 query: { 86 query: {
107 username: 'cyan' 87 username: 'cyan'
108 }, 88 },
109 statusCodeExpected: HttpStatusCode.FOUND_302 89 expectedStatus: HttpStatusCode.FOUND_302
110 }) 90 })
111 91
112 const location = res.header.location 92 const location = res.header.location
@@ -121,13 +101,17 @@ describe('Test external auth plugins', function () {
121 }) 101 })
122 102
123 it('Should reject auto external login with a missing or invalid token', async function () { 103 it('Should reject auto external login with a missing or invalid token', async function () {
124 await loginUsingExternalToken(server, 'cyan', '', HttpStatusCode.BAD_REQUEST_400) 104 const command = server.login
125 await loginUsingExternalToken(server, 'cyan', 'blabla', HttpStatusCode.BAD_REQUEST_400) 105
106 await command.loginUsingExternalToken({ username: 'cyan', externalAuthToken: '', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
107 await command.loginUsingExternalToken({ username: 'cyan', externalAuthToken: 'blabla', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
126 }) 108 })
127 109
128 it('Should reject auto external login with a missing or invalid username', async function () { 110 it('Should reject auto external login with a missing or invalid username', async function () {
129 await loginUsingExternalToken(server, '', externalAuthToken, HttpStatusCode.BAD_REQUEST_400) 111 const command = server.login
130 await loginUsingExternalToken(server, '', externalAuthToken, HttpStatusCode.BAD_REQUEST_400) 112
113 await command.loginUsingExternalToken({ username: '', externalAuthToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
114 await command.loginUsingExternalToken({ username: '', externalAuthToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
131 }) 115 })
132 116
133 it('Should reject auto external login with an expired token', async function () { 117 it('Should reject auto external login with an expired token', async function () {
@@ -135,9 +119,13 @@ describe('Test external auth plugins', function () {
135 119
136 await wait(5000) 120 await wait(5000)
137 121
138 await loginUsingExternalToken(server, 'cyan', externalAuthToken, HttpStatusCode.BAD_REQUEST_400) 122 await server.login.loginUsingExternalToken({
123 username: 'cyan',
124 externalAuthToken,
125 expectedStatus: HttpStatusCode.BAD_REQUEST_400
126 })
139 127
140 await waitUntilLog(server, 'expired external auth token', 2) 128 await server.servers.waitUntilLog('expired external auth token', 2)
141 }) 129 })
142 130
143 it('Should auto login Cyan, create the user and use the token', async function () { 131 it('Should auto login Cyan, create the user and use the token', async function () {
@@ -157,9 +145,7 @@ describe('Test external auth plugins', function () {
157 } 145 }
158 146
159 { 147 {
160 const res = await getMyUserInformation(server.url, cyanAccessToken) 148 const body = await server.users.getMyInfo({ token: cyanAccessToken })
161
162 const body: User = res.body
163 expect(body.username).to.equal('cyan') 149 expect(body.username).to.equal('cyan')
164 expect(body.account.displayName).to.equal('cyan') 150 expect(body.account.displayName).to.equal('cyan')
165 expect(body.email).to.equal('cyan@example.com') 151 expect(body.email).to.equal('cyan@example.com')
@@ -181,9 +167,7 @@ describe('Test external auth plugins', function () {
181 } 167 }
182 168
183 { 169 {
184 const res = await getMyUserInformation(server.url, kefkaAccessToken) 170 const body = await server.users.getMyInfo({ token: kefkaAccessToken })
185
186 const body: User = res.body
187 expect(body.username).to.equal('kefka') 171 expect(body.username).to.equal('kefka')
188 expect(body.account.displayName).to.equal('Kefka Palazzo') 172 expect(body.account.displayName).to.equal('Kefka Palazzo')
189 expect(body.email).to.equal('kefka@example.com') 173 expect(body.email).to.equal('kefka@example.com')
@@ -193,43 +177,39 @@ describe('Test external auth plugins', function () {
193 177
194 it('Should refresh Cyan token, but not Kefka token', async function () { 178 it('Should refresh Cyan token, but not Kefka token', async function () {
195 { 179 {
196 const resRefresh = await refreshToken(server, cyanRefreshToken) 180 const resRefresh = await server.login.refreshToken({ refreshToken: cyanRefreshToken })
197 cyanAccessToken = resRefresh.body.access_token 181 cyanAccessToken = resRefresh.body.access_token
198 cyanRefreshToken = resRefresh.body.refresh_token 182 cyanRefreshToken = resRefresh.body.refresh_token
199 183
200 const res = await getMyUserInformation(server.url, cyanAccessToken) 184 const body = await server.users.getMyInfo({ token: cyanAccessToken })
201 const user: User = res.body 185 expect(body.username).to.equal('cyan')
202 expect(user.username).to.equal('cyan')
203 } 186 }
204 187
205 { 188 {
206 await refreshToken(server, kefkaRefreshToken, HttpStatusCode.BAD_REQUEST_400) 189 await server.login.refreshToken({ refreshToken: kefkaRefreshToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
207 } 190 }
208 }) 191 })
209 192
210 it('Should update Cyan profile', async function () { 193 it('Should update Cyan profile', async function () {
211 await updateMyUser({ 194 await server.users.updateMe({
212 url: server.url, 195 token: cyanAccessToken,
213 accessToken: cyanAccessToken,
214 displayName: 'Cyan Garamonde', 196 displayName: 'Cyan Garamonde',
215 description: 'Retainer to the king of Doma' 197 description: 'Retainer to the king of Doma'
216 }) 198 })
217 199
218 const res = await getMyUserInformation(server.url, cyanAccessToken) 200 const body = await server.users.getMyInfo({ token: cyanAccessToken })
219
220 const body: User = res.body
221 expect(body.account.displayName).to.equal('Cyan Garamonde') 201 expect(body.account.displayName).to.equal('Cyan Garamonde')
222 expect(body.account.description).to.equal('Retainer to the king of Doma') 202 expect(body.account.description).to.equal('Retainer to the king of Doma')
223 }) 203 })
224 204
225 it('Should logout Cyan', async function () { 205 it('Should logout Cyan', async function () {
226 await logout(server.url, cyanAccessToken) 206 await server.login.logout({ token: cyanAccessToken })
227 }) 207 })
228 208
229 it('Should have logged out Cyan', async function () { 209 it('Should have logged out Cyan', async function () {
230 await waitUntilLog(server, 'On logout cyan') 210 await server.servers.waitUntilLog('On logout cyan')
231 211
232 await getMyUserInformation(server.url, cyanAccessToken, HttpStatusCode.UNAUTHORIZED_401) 212 await server.users.getMyInfo({ token: cyanAccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
233 }) 213 })
234 214
235 it('Should login Cyan and keep the old existing profile', async function () { 215 it('Should login Cyan and keep the old existing profile', async function () {
@@ -247,9 +227,7 @@ describe('Test external auth plugins', function () {
247 cyanAccessToken = res.access_token 227 cyanAccessToken = res.access_token
248 } 228 }
249 229
250 const res = await getMyUserInformation(server.url, cyanAccessToken) 230 const body = await server.users.getMyInfo({ token: cyanAccessToken })
251
252 const body: User = res.body
253 expect(body.username).to.equal('cyan') 231 expect(body.username).to.equal('cyan')
254 expect(body.account.displayName).to.equal('Cyan Garamonde') 232 expect(body.account.displayName).to.equal('Cyan Garamonde')
255 expect(body.account.description).to.equal('Retainer to the king of Doma') 233 expect(body.account.description).to.equal('Retainer to the king of Doma')
@@ -257,12 +235,11 @@ describe('Test external auth plugins', function () {
257 }) 235 })
258 236
259 it('Should not update an external auth email', async function () { 237 it('Should not update an external auth email', async function () {
260 await updateMyUser({ 238 await server.users.updateMe({
261 url: server.url, 239 token: cyanAccessToken,
262 accessToken: cyanAccessToken,
263 email: 'toto@example.com', 240 email: 'toto@example.com',
264 currentPassword: 'toto', 241 currentPassword: 'toto',
265 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 242 expectedStatus: HttpStatusCode.BAD_REQUEST_400
266 }) 243 })
267 }) 244 })
268 245
@@ -271,18 +248,16 @@ describe('Test external auth plugins', function () {
271 248
272 await wait(5000) 249 await wait(5000)
273 250
274 await getMyUserInformation(server.url, kefkaAccessToken, HttpStatusCode.UNAUTHORIZED_401) 251 await server.users.getMyInfo({ token: kefkaAccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
275 }) 252 })
276 253
277 it('Should unregister external-auth-2 and do not login existing Kefka', async function () { 254 it('Should unregister external-auth-2 and do not login existing Kefka', async function () {
278 await updatePluginSettings({ 255 await server.plugins.updateSettings({
279 url: server.url,
280 accessToken: server.accessToken,
281 npmName: 'peertube-plugin-test-external-auth-one', 256 npmName: 'peertube-plugin-test-external-auth-one',
282 settings: { disableKefka: true } 257 settings: { disableKefka: true }
283 }) 258 })
284 259
285 await userLogin(server, { username: 'kefka', password: 'fake' }, HttpStatusCode.BAD_REQUEST_400) 260 await server.login.login({ user: { username: 'kefka', password: 'fake' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
286 261
287 await loginExternal({ 262 await loginExternal({
288 server, 263 server,
@@ -292,14 +267,12 @@ describe('Test external auth plugins', function () {
292 username: 'kefka' 267 username: 'kefka'
293 }, 268 },
294 username: 'kefka', 269 username: 'kefka',
295 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 270 expectedStatus: HttpStatusCode.NOT_FOUND_404
296 }) 271 })
297 }) 272 })
298 273
299 it('Should have disabled this auth', async function () { 274 it('Should have disabled this auth', async function () {
300 const res = await getConfig(server.url) 275 const config = await server.config.getConfig()
301
302 const config: ServerConfig = res.body
303 276
304 const auths = config.plugin.registeredExternalAuths 277 const auths = config.plugin.registeredExternalAuths
305 expect(auths).to.have.lengthOf(7) 278 expect(auths).to.have.lengthOf(7)
@@ -309,11 +282,7 @@ describe('Test external auth plugins', function () {
309 }) 282 })
310 283
311 it('Should uninstall the plugin one and do not login Cyan', async function () { 284 it('Should uninstall the plugin one and do not login Cyan', async function () {
312 await uninstallPlugin({ 285 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-external-auth-one' })
313 url: server.url,
314 accessToken: server.accessToken,
315 npmName: 'peertube-plugin-test-external-auth-one'
316 })
317 286
318 await loginExternal({ 287 await loginExternal({
319 server, 288 server,
@@ -323,12 +292,12 @@ describe('Test external auth plugins', function () {
323 username: 'cyan' 292 username: 'cyan'
324 }, 293 },
325 username: 'cyan', 294 username: 'cyan',
326 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 295 expectedStatus: HttpStatusCode.NOT_FOUND_404
327 }) 296 })
328 297
329 await userLogin(server, { username: 'cyan', password: null }, HttpStatusCode.BAD_REQUEST_400) 298 await server.login.login({ user: { username: 'cyan', password: null }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
330 await userLogin(server, { username: 'cyan', password: '' }, HttpStatusCode.BAD_REQUEST_400) 299 await server.login.login({ user: { username: 'cyan', password: '' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
331 await userLogin(server, { username: 'cyan', password: 'fake' }, HttpStatusCode.BAD_REQUEST_400) 300 await server.login.login({ user: { username: 'cyan', password: 'fake' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
332 }) 301 })
333 302
334 it('Should not login kefka with another plugin', async function () { 303 it('Should not login kefka with another plugin', async function () {
@@ -337,7 +306,7 @@ describe('Test external auth plugins', function () {
337 npmName: 'test-external-auth-two', 306 npmName: 'test-external-auth-two',
338 authName: 'external-auth-4', 307 authName: 'external-auth-4',
339 username: 'kefka2', 308 username: 'kefka2',
340 statusCodeExpectedStep2: HttpStatusCode.BAD_REQUEST_400 309 expectedStatusStep2: HttpStatusCode.BAD_REQUEST_400
341 }) 310 })
342 311
343 await loginExternal({ 312 await loginExternal({
@@ -345,31 +314,24 @@ describe('Test external auth plugins', function () {
345 npmName: 'test-external-auth-two', 314 npmName: 'test-external-auth-two',
346 authName: 'external-auth-4', 315 authName: 'external-auth-4',
347 username: 'kefka', 316 username: 'kefka',
348 statusCodeExpectedStep2: HttpStatusCode.BAD_REQUEST_400 317 expectedStatusStep2: HttpStatusCode.BAD_REQUEST_400
349 }) 318 })
350 }) 319 })
351 320
352 it('Should not login an existing user', async function () { 321 it('Should not login an existing user', async function () {
353 await createUser({ 322 await server.users.create({ username: 'existing_user', password: 'super_password' })
354 url: server.url,
355 accessToken: server.accessToken,
356 username: 'existing_user',
357 password: 'super_password'
358 })
359 323
360 await loginExternal({ 324 await loginExternal({
361 server, 325 server,
362 npmName: 'test-external-auth-two', 326 npmName: 'test-external-auth-two',
363 authName: 'external-auth-6', 327 authName: 'external-auth-6',
364 username: 'existing_user', 328 username: 'existing_user',
365 statusCodeExpectedStep2: HttpStatusCode.BAD_REQUEST_400 329 expectedStatusStep2: HttpStatusCode.BAD_REQUEST_400
366 }) 330 })
367 }) 331 })
368 332
369 it('Should display the correct configuration', async function () { 333 it('Should display the correct configuration', async function () {
370 const res = await getConfig(server.url) 334 const config = await server.config.getConfig()
371
372 const config: ServerConfig = res.body
373 335
374 const auths = config.plugin.registeredExternalAuths 336 const auths = config.plugin.registeredExternalAuths
375 expect(auths).to.have.lengthOf(6) 337 expect(auths).to.have.lengthOf(6)
@@ -390,9 +352,8 @@ describe('Test external auth plugins', function () {
390 username: 'cid' 352 username: 'cid'
391 }) 353 })
392 354
393 const resLogout = await logout(server.url, resLogin.access_token) 355 const { redirectUrl } = await server.login.logout({ token: resLogin.access_token })
394 356 expect(redirectUrl).to.equal('https://example.com/redirectUrl')
395 expect(resLogout.body.redirectUrl).to.equal('https://example.com/redirectUrl')
396 }) 357 })
397 358
398 it('Should call the plugin\'s onLogout method with the request', async function () { 359 it('Should call the plugin\'s onLogout method with the request', async function () {
@@ -403,8 +364,7 @@ describe('Test external auth plugins', function () {
403 username: 'cid' 364 username: 'cid'
404 }) 365 })
405 366
406 const resLogout = await logout(server.url, resLogin.access_token) 367 const { redirectUrl } = await server.login.logout({ token: resLogin.access_token })
407 368 expect(redirectUrl).to.equal('https://example.com/redirectUrl?access_token=' + resLogin.access_token)
408 expect(resLogout.body.redirectUrl).to.equal('https://example.com/redirectUrl?access_token=' + resLogin.access_token)
409 }) 369 })
410}) 370})
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts
index 644b41dea..02915f08c 100644
--- a/server/tests/plugins/filter-hooks.ts
+++ b/server/tests/plugins/filter-hooks.ts
@@ -2,192 +2,152 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { advancedVideoChannelSearch } from '@shared/extra-utils/search/video-channels'
6import { ServerConfig } from '@shared/models'
7import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
8import { 5import {
9 addVideoCommentReply, 6 cleanupTests,
10 addVideoCommentThread, 7 createMultipleServers,
11 advancedVideoPlaylistSearch,
12 advancedVideosSearch,
13 createLive,
14 createVideoPlaylist,
15 doubleFollow, 8 doubleFollow,
16 getAccountVideos, 9 FIXTURE_URLS,
17 getConfig,
18 getMyVideos,
19 getPluginTestPath,
20 getVideo,
21 getVideoChannelVideos,
22 getVideoCommentThreads,
23 getVideoPlaylist,
24 getVideosList,
25 getVideosListPagination,
26 getVideoThreadComments,
27 getVideoWithToken,
28 installPlugin,
29 makeRawRequest, 10 makeRawRequest,
30 registerUser, 11 PeerTubeServer,
12 PluginsCommand,
31 setAccessTokensToServers, 13 setAccessTokensToServers,
32 setDefaultVideoChannel, 14 setDefaultVideoChannel,
33 updateCustomSubConfig,
34 updateVideo,
35 uploadVideo,
36 uploadVideoAndGetId,
37 waitJobs 15 waitJobs
38} from '../../../shared/extra-utils' 16} from '@shared/extra-utils'
39import { cleanupTests, flushAndRunMultipleServers, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers' 17import { HttpStatusCode, VideoDetails, VideoImportState, VideoPlaylist, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
40import { getGoodVideoUrl, getMyVideoImports, importVideo } from '../../../shared/extra-utils/videos/video-imports'
41import {
42 VideoCommentThreadTree,
43 VideoDetails,
44 VideoImport,
45 VideoImportState,
46 VideoPlaylist,
47 VideoPlaylistPrivacy,
48 VideoPrivacy
49} from '../../../shared/models/videos'
50 18
51const expect = chai.expect 19const expect = chai.expect
52 20
53describe('Test plugin filter hooks', function () { 21describe('Test plugin filter hooks', function () {
54 let servers: ServerInfo[] 22 let servers: PeerTubeServer[]
55 let videoUUID: string 23 let videoUUID: string
56 let threadId: number 24 let threadId: number
57 25
58 before(async function () { 26 before(async function () {
59 this.timeout(60000) 27 this.timeout(60000)
60 28
61 servers = await flushAndRunMultipleServers(2) 29 servers = await createMultipleServers(2)
62 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
63 await setDefaultVideoChannel(servers) 31 await setDefaultVideoChannel(servers)
64 await doubleFollow(servers[0], servers[1]) 32 await doubleFollow(servers[0], servers[1])
65 33
66 await installPlugin({ 34 await servers[0].plugins.install({ path: PluginsCommand.getPluginTestPath() })
67 url: servers[0].url, 35 await servers[0].plugins.install({ path: PluginsCommand.getPluginTestPath('-filter-translations') })
68 accessToken: servers[0].accessToken,
69 path: getPluginTestPath()
70 })
71
72 await installPlugin({
73 url: servers[0].url,
74 accessToken: servers[0].accessToken,
75 path: getPluginTestPath('-filter-translations')
76 })
77 36
78 for (let i = 0; i < 10; i++) { 37 for (let i = 0; i < 10; i++) {
79 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'default video ' + i }) 38 await servers[0].videos.upload({ attributes: { name: 'default video ' + i } })
80 } 39 }
81 40
82 const res = await getVideosList(servers[0].url) 41 const { data } = await servers[0].videos.list()
83 videoUUID = res.body.data[0].uuid 42 videoUUID = data[0].uuid
84 43
85 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 44 await servers[0].config.updateCustomSubConfig({
86 live: { enabled: true }, 45 newConfig: {
87 signup: { enabled: true }, 46 live: { enabled: true },
88 import: { 47 signup: { enabled: true },
89 videos: { 48 import: {
90 http: { enabled: true }, 49 videos: {
91 torrent: { enabled: true } 50 http: { enabled: true },
51 torrent: { enabled: true }
52 }
92 } 53 }
93 } 54 }
94 }) 55 })
95 }) 56 })
96 57
97 it('Should run filter:api.videos.list.params', async function () { 58 it('Should run filter:api.videos.list.params', async function () {
98 const res = await getVideosListPagination(servers[0].url, 0, 2) 59 const { data } = await servers[0].videos.list({ start: 0, count: 2 })
99 60
100 // 2 plugins do +1 to the count parameter 61 // 2 plugins do +1 to the count parameter
101 expect(res.body.data).to.have.lengthOf(4) 62 expect(data).to.have.lengthOf(4)
102 }) 63 })
103 64
104 it('Should run filter:api.videos.list.result', async function () { 65 it('Should run filter:api.videos.list.result', async function () {
105 const res = await getVideosListPagination(servers[0].url, 0, 0) 66 const { total } = await servers[0].videos.list({ start: 0, count: 0 })
106 67
107 // Plugin do +1 to the total result 68 // Plugin do +1 to the total result
108 expect(res.body.total).to.equal(11) 69 expect(total).to.equal(11)
109 }) 70 })
110 71
111 it('Should run filter:api.accounts.videos.list.params', async function () { 72 it('Should run filter:api.accounts.videos.list.params', async function () {
112 const res = await getAccountVideos(servers[0].url, servers[0].accessToken, 'root', 0, 2) 73 const { data } = await servers[0].videos.listByAccount({ handle: 'root', start: 0, count: 2 })
113 74
114 // 1 plugin do +1 to the count parameter 75 // 1 plugin do +1 to the count parameter
115 expect(res.body.data).to.have.lengthOf(3) 76 expect(data).to.have.lengthOf(3)
116 }) 77 })
117 78
118 it('Should run filter:api.accounts.videos.list.result', async function () { 79 it('Should run filter:api.accounts.videos.list.result', async function () {
119 const res = await getAccountVideos(servers[0].url, servers[0].accessToken, 'root', 0, 2) 80 const { total } = await servers[0].videos.listByAccount({ handle: 'root', start: 0, count: 2 })
120 81
121 // Plugin do +2 to the total result 82 // Plugin do +2 to the total result
122 expect(res.body.total).to.equal(12) 83 expect(total).to.equal(12)
123 }) 84 })
124 85
125 it('Should run filter:api.video-channels.videos.list.params', async function () { 86 it('Should run filter:api.video-channels.videos.list.params', async function () {
126 const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, 'root_channel', 0, 2) 87 const { data } = await servers[0].videos.listByChannel({ handle: 'root_channel', start: 0, count: 2 })
127 88
128 // 1 plugin do +3 to the count parameter 89 // 1 plugin do +3 to the count parameter
129 expect(res.body.data).to.have.lengthOf(5) 90 expect(data).to.have.lengthOf(5)
130 }) 91 })
131 92
132 it('Should run filter:api.video-channels.videos.list.result', async function () { 93 it('Should run filter:api.video-channels.videos.list.result', async function () {
133 const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, 'root_channel', 0, 2) 94 const { total } = await servers[0].videos.listByChannel({ handle: 'root_channel', start: 0, count: 2 })
134 95
135 // Plugin do +3 to the total result 96 // Plugin do +3 to the total result
136 expect(res.body.total).to.equal(13) 97 expect(total).to.equal(13)
137 }) 98 })
138 99
139 it('Should run filter:api.user.me.videos.list.params', async function () { 100 it('Should run filter:api.user.me.videos.list.params', async function () {
140 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 2) 101 const { data } = await servers[0].videos.listMyVideos({ start: 0, count: 2 })
141 102
142 // 1 plugin do +4 to the count parameter 103 // 1 plugin do +4 to the count parameter
143 expect(res.body.data).to.have.lengthOf(6) 104 expect(data).to.have.lengthOf(6)
144 }) 105 })
145 106
146 it('Should run filter:api.user.me.videos.list.result', async function () { 107 it('Should run filter:api.user.me.videos.list.result', async function () {
147 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 2) 108 const { total } = await servers[0].videos.listMyVideos({ start: 0, count: 2 })
148 109
149 // Plugin do +4 to the total result 110 // Plugin do +4 to the total result
150 expect(res.body.total).to.equal(14) 111 expect(total).to.equal(14)
151 }) 112 })
152 113
153 it('Should run filter:api.video.get.result', async function () { 114 it('Should run filter:api.video.get.result', async function () {
154 const res = await getVideo(servers[0].url, videoUUID) 115 const video = await servers[0].videos.get({ id: videoUUID })
155 116 expect(video.name).to.contain('<3')
156 expect(res.body.name).to.contain('<3')
157 }) 117 })
158 118
159 it('Should run filter:api.video.upload.accept.result', async function () { 119 it('Should run filter:api.video.upload.accept.result', async function () {
160 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video with bad word' }, HttpStatusCode.FORBIDDEN_403) 120 await servers[0].videos.upload({ attributes: { name: 'video with bad word' }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
161 }) 121 })
162 122
163 it('Should run filter:api.live-video.create.accept.result', async function () { 123 it('Should run filter:api.live-video.create.accept.result', async function () {
164 const attributes = { 124 const attributes = {
165 name: 'video with bad word', 125 name: 'video with bad word',
166 privacy: VideoPrivacy.PUBLIC, 126 privacy: VideoPrivacy.PUBLIC,
167 channelId: servers[0].videoChannel.id 127 channelId: servers[0].store.channel.id
168 } 128 }
169 129
170 await createLive(servers[0].url, servers[0].accessToken, attributes, HttpStatusCode.FORBIDDEN_403) 130 await servers[0].live.create({ fields: attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
171 }) 131 })
172 132
173 it('Should run filter:api.video.pre-import-url.accept.result', async function () { 133 it('Should run filter:api.video.pre-import-url.accept.result', async function () {
174 const baseAttributes = { 134 const attributes = {
175 name: 'normal title', 135 name: 'normal title',
176 privacy: VideoPrivacy.PUBLIC, 136 privacy: VideoPrivacy.PUBLIC,
177 channelId: servers[0].videoChannel.id, 137 channelId: servers[0].store.channel.id,
178 targetUrl: getGoodVideoUrl() + 'bad' 138 targetUrl: FIXTURE_URLS.goodVideo + 'bad'
179 } 139 }
180 await importVideo(servers[0].url, servers[0].accessToken, baseAttributes, HttpStatusCode.FORBIDDEN_403) 140 await servers[0].imports.importVideo({ attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
181 }) 141 })
182 142
183 it('Should run filter:api.video.pre-import-torrent.accept.result', async function () { 143 it('Should run filter:api.video.pre-import-torrent.accept.result', async function () {
184 const baseAttributes = { 144 const attributes = {
185 name: 'bad torrent', 145 name: 'bad torrent',
186 privacy: VideoPrivacy.PUBLIC, 146 privacy: VideoPrivacy.PUBLIC,
187 channelId: servers[0].videoChannel.id, 147 channelId: servers[0].store.channel.id,
188 torrentfile: 'video-720p.torrent' as any 148 torrentfile: 'video-720p.torrent' as any
189 } 149 }
190 await importVideo(servers[0].url, servers[0].accessToken, baseAttributes, HttpStatusCode.FORBIDDEN_403) 150 await servers[0].imports.importVideo({ attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
191 }) 151 })
192 152
193 it('Should run filter:api.video.post-import-url.accept.result', async function () { 153 it('Should run filter:api.video.post-import-url.accept.result', async function () {
@@ -196,21 +156,21 @@ describe('Test plugin filter hooks', function () {
196 let videoImportId: number 156 let videoImportId: number
197 157
198 { 158 {
199 const baseAttributes = { 159 const attributes = {
200 name: 'title with bad word', 160 name: 'title with bad word',
201 privacy: VideoPrivacy.PUBLIC, 161 privacy: VideoPrivacy.PUBLIC,
202 channelId: servers[0].videoChannel.id, 162 channelId: servers[0].store.channel.id,
203 targetUrl: getGoodVideoUrl() 163 targetUrl: FIXTURE_URLS.goodVideo
204 } 164 }
205 const res = await importVideo(servers[0].url, servers[0].accessToken, baseAttributes) 165 const body = await servers[0].imports.importVideo({ attributes })
206 videoImportId = res.body.id 166 videoImportId = body.id
207 } 167 }
208 168
209 await waitJobs(servers) 169 await waitJobs(servers)
210 170
211 { 171 {
212 const res = await getMyVideoImports(servers[0].url, servers[0].accessToken) 172 const body = await servers[0].imports.getMyVideoImports()
213 const videoImports = res.body.data as VideoImport[] 173 const videoImports = body.data
214 174
215 const videoImport = videoImports.find(i => i.id === videoImportId) 175 const videoImport = videoImports.find(i => i.id === videoImportId)
216 176
@@ -225,21 +185,20 @@ describe('Test plugin filter hooks', function () {
225 let videoImportId: number 185 let videoImportId: number
226 186
227 { 187 {
228 const baseAttributes = { 188 const attributes = {
229 name: 'title with bad word', 189 name: 'title with bad word',
230 privacy: VideoPrivacy.PUBLIC, 190 privacy: VideoPrivacy.PUBLIC,
231 channelId: servers[0].videoChannel.id, 191 channelId: servers[0].store.channel.id,
232 torrentfile: 'video-720p.torrent' as any 192 torrentfile: 'video-720p.torrent' as any
233 } 193 }
234 const res = await importVideo(servers[0].url, servers[0].accessToken, baseAttributes) 194 const body = await servers[0].imports.importVideo({ attributes })
235 videoImportId = res.body.id 195 videoImportId = body.id
236 } 196 }
237 197
238 await waitJobs(servers) 198 await waitJobs(servers)
239 199
240 { 200 {
241 const res = await getMyVideoImports(servers[0].url, servers[0].accessToken) 201 const { data: videoImports } = await servers[0].imports.getMyVideoImports()
242 const videoImports = res.body.data as VideoImport[]
243 202
244 const videoImport = videoImports.find(i => i.id === videoImportId) 203 const videoImport = videoImports.find(i => i.id === videoImportId)
245 204
@@ -249,60 +208,71 @@ describe('Test plugin filter hooks', function () {
249 }) 208 })
250 209
251 it('Should run filter:api.video-thread.create.accept.result', async function () { 210 it('Should run filter:api.video-thread.create.accept.result', async function () {
252 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'comment with bad word', HttpStatusCode.FORBIDDEN_403) 211 await servers[0].comments.createThread({
212 videoId: videoUUID,
213 text: 'comment with bad word',
214 expectedStatus: HttpStatusCode.FORBIDDEN_403
215 })
253 }) 216 })
254 217
255 it('Should run filter:api.video-comment-reply.create.accept.result', async function () { 218 it('Should run filter:api.video-comment-reply.create.accept.result', async function () {
256 const res = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'thread') 219 const created = await servers[0].comments.createThread({ videoId: videoUUID, text: 'thread' })
257 threadId = res.body.comment.id 220 threadId = created.id
258 221
259 await addVideoCommentReply( 222 await servers[0].comments.addReply({
260 servers[0].url, 223 videoId: videoUUID,
261 servers[0].accessToken, 224 toCommentId: threadId,
262 videoUUID, 225 text: 'comment with bad word',
263 threadId, 226 expectedStatus: HttpStatusCode.FORBIDDEN_403
264 'comment with bad word', 227 })
265 HttpStatusCode.FORBIDDEN_403 228 await servers[0].comments.addReply({
266 ) 229 videoId: videoUUID,
267 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID, threadId, 'comment with good word', HttpStatusCode.OK_200) 230 toCommentId: threadId,
231 text: 'comment with good word',
232 expectedStatus: HttpStatusCode.OK_200
233 })
268 }) 234 })
269 235
270 it('Should run filter:api.video-threads.list.params', async function () { 236 it('Should run filter:api.video-threads.list.params', async function () {
271 const res = await getVideoCommentThreads(servers[0].url, videoUUID, 0, 0) 237 const { data } = await servers[0].comments.listThreads({ videoId: videoUUID, start: 0, count: 0 })
272 238
273 // our plugin do +1 to the count parameter 239 // our plugin do +1 to the count parameter
274 expect(res.body.data).to.have.lengthOf(1) 240 expect(data).to.have.lengthOf(1)
275 }) 241 })
276 242
277 it('Should run filter:api.video-threads.list.result', async function () { 243 it('Should run filter:api.video-threads.list.result', async function () {
278 const res = await getVideoCommentThreads(servers[0].url, videoUUID, 0, 0) 244 const { total } = await servers[0].comments.listThreads({ videoId: videoUUID, start: 0, count: 0 })
279 245
280 // Plugin do +1 to the total result 246 // Plugin do +1 to the total result
281 expect(res.body.total).to.equal(2) 247 expect(total).to.equal(2)
282 }) 248 })
283 249
284 it('Should run filter:api.video-thread-comments.list.params') 250 it('Should run filter:api.video-thread-comments.list.params')
285 251
286 it('Should run filter:api.video-thread-comments.list.result', async function () { 252 it('Should run filter:api.video-thread-comments.list.result', async function () {
287 const res = await getVideoThreadComments(servers[0].url, videoUUID, threadId) 253 const thread = await servers[0].comments.getThread({ videoId: videoUUID, threadId })
288 254
289 const thread = res.body as VideoCommentThreadTree
290 expect(thread.comment.text.endsWith(' <3')).to.be.true 255 expect(thread.comment.text.endsWith(' <3')).to.be.true
291 }) 256 })
292 257
293 describe('Should run filter:video.auto-blacklist.result', function () { 258 it('Should run filter:api.overviews.videos.list.{params,result}', async function () {
259 await servers[0].overviews.getVideos({ page: 1 })
294 260
295 async function checkIsBlacklisted (oldRes: any, value: boolean) { 261 // 3 because we get 3 samples per page
296 const videoId = oldRes.body.video.uuid 262 await servers[0].servers.waitUntilLog('Run hook filter:api.overviews.videos.list.params', 3)
263 await servers[0].servers.waitUntilLog('Run hook filter:api.overviews.videos.list.result', 3)
264 })
297 265
298 const res = await getVideoWithToken(servers[0].url, servers[0].accessToken, videoId) 266 describe('Should run filter:video.auto-blacklist.result', function () {
299 const video: VideoDetails = res.body 267
268 async function checkIsBlacklisted (id: number | string, value: boolean) {
269 const video = await servers[0].videos.getWithToken({ id })
300 expect(video.blacklisted).to.equal(value) 270 expect(video.blacklisted).to.equal(value)
301 } 271 }
302 272
303 it('Should blacklist on upload', async function () { 273 it('Should blacklist on upload', async function () {
304 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video please blacklist me' }) 274 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video please blacklist me' } })
305 await checkIsBlacklisted(res, true) 275 await checkIsBlacklisted(uuid, true)
306 }) 276 })
307 277
308 it('Should blacklist on import', async function () { 278 it('Should blacklist on import', async function () {
@@ -310,60 +280,62 @@ describe('Test plugin filter hooks', function () {
310 280
311 const attributes = { 281 const attributes = {
312 name: 'video please blacklist me', 282 name: 'video please blacklist me',
313 targetUrl: getGoodVideoUrl(), 283 targetUrl: FIXTURE_URLS.goodVideo,
314 channelId: servers[0].videoChannel.id 284 channelId: servers[0].store.channel.id
315 } 285 }
316 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 286 const body = await servers[0].imports.importVideo({ attributes })
317 await checkIsBlacklisted(res, true) 287 await checkIsBlacklisted(body.video.uuid, true)
318 }) 288 })
319 289
320 it('Should blacklist on update', async function () { 290 it('Should blacklist on update', async function () {
321 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' }) 291 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video' } })
322 const videoId = res.body.video.uuid 292 await checkIsBlacklisted(uuid, false)
323 await checkIsBlacklisted(res, false)
324 293
325 await updateVideo(servers[0].url, servers[0].accessToken, videoId, { name: 'please blacklist me' }) 294 await servers[0].videos.update({ id: uuid, attributes: { name: 'please blacklist me' } })
326 await checkIsBlacklisted(res, true) 295 await checkIsBlacklisted(uuid, true)
327 }) 296 })
328 297
329 it('Should blacklist on remote upload', async function () { 298 it('Should blacklist on remote upload', async function () {
330 this.timeout(120000) 299 this.timeout(120000)
331 300
332 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'remote please blacklist me' }) 301 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'remote please blacklist me' } })
333 await waitJobs(servers) 302 await waitJobs(servers)
334 303
335 await checkIsBlacklisted(res, true) 304 await checkIsBlacklisted(uuid, true)
336 }) 305 })
337 306
338 it('Should blacklist on remote update', async function () { 307 it('Should blacklist on remote update', async function () {
339 this.timeout(120000) 308 this.timeout(120000)
340 309
341 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video' }) 310 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video' } })
342 await waitJobs(servers) 311 await waitJobs(servers)
343 312
344 const videoId = res.body.video.uuid 313 await checkIsBlacklisted(uuid, false)
345 await checkIsBlacklisted(res, false)
346 314
347 await updateVideo(servers[1].url, servers[1].accessToken, videoId, { name: 'please blacklist me' }) 315 await servers[1].videos.update({ id: uuid, attributes: { name: 'please blacklist me' } })
348 await waitJobs(servers) 316 await waitJobs(servers)
349 317
350 await checkIsBlacklisted(res, true) 318 await checkIsBlacklisted(uuid, true)
351 }) 319 })
352 }) 320 })
353 321
354 describe('Should run filter:api.user.signup.allowed.result', function () { 322 describe('Should run filter:api.user.signup.allowed.result', function () {
355 323
356 it('Should run on config endpoint', async function () { 324 it('Should run on config endpoint', async function () {
357 const res = await getConfig(servers[0].url) 325 const body = await servers[0].config.getConfig()
358 expect((res.body as ServerConfig).signup.allowed).to.be.true 326 expect(body.signup.allowed).to.be.true
359 }) 327 })
360 328
361 it('Should allow a signup', async function () { 329 it('Should allow a signup', async function () {
362 await registerUser(servers[0].url, 'john', 'password') 330 await servers[0].users.register({ username: 'john', password: 'password' })
363 }) 331 })
364 332
365 it('Should not allow a signup', async function () { 333 it('Should not allow a signup', async function () {
366 const res = await registerUser(servers[0].url, 'jma', 'password', HttpStatusCode.FORBIDDEN_403) 334 const res = await servers[0].users.register({
335 username: 'jma',
336 password: 'password',
337 expectedStatus: HttpStatusCode.FORBIDDEN_403
338 })
367 339
368 expect(res.body.error).to.equal('No jma') 340 expect(res.body.error).to.equal('No jma')
369 }) 341 })
@@ -375,13 +347,15 @@ describe('Test plugin filter hooks', function () {
375 before(async function () { 347 before(async function () {
376 this.timeout(120000) 348 this.timeout(120000)
377 349
378 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 350 await servers[0].config.updateCustomSubConfig({
379 transcoding: { 351 newConfig: {
380 webtorrent: { 352 transcoding: {
381 enabled: true 353 webtorrent: {
382 }, 354 enabled: true
383 hls: { 355 },
384 enabled: true 356 hls: {
357 enabled: true
358 }
385 } 359 }
386 } 360 }
387 }) 361 })
@@ -389,15 +363,14 @@ describe('Test plugin filter hooks', function () {
389 const uuids: string[] = [] 363 const uuids: string[] = []
390 364
391 for (const name of [ 'bad torrent', 'bad file', 'bad playlist file' ]) { 365 for (const name of [ 'bad torrent', 'bad file', 'bad playlist file' ]) {
392 const uuid = (await uploadVideoAndGetId({ server: servers[0], videoName: name })).uuid 366 const uuid = (await servers[0].videos.quickUpload({ name: name })).uuid
393 uuids.push(uuid) 367 uuids.push(uuid)
394 } 368 }
395 369
396 await waitJobs(servers) 370 await waitJobs(servers)
397 371
398 for (const uuid of uuids) { 372 for (const uuid of uuids) {
399 const res = await getVideo(servers[0].url, uuid) 373 downloadVideos.push(await servers[0].videos.get({ id: uuid }))
400 downloadVideos.push(res.body)
401 } 374 }
402 }) 375 })
403 376
@@ -437,25 +410,26 @@ describe('Test plugin filter hooks', function () {
437 before(async function () { 410 before(async function () {
438 this.timeout(60000) 411 this.timeout(60000)
439 412
440 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 413 await servers[0].config.updateCustomSubConfig({
441 transcoding: { 414 newConfig: {
442 enabled: false 415 transcoding: {
416 enabled: false
417 }
443 } 418 }
444 }) 419 })
445 420
446 for (const name of [ 'bad embed', 'good embed' ]) { 421 for (const name of [ 'bad embed', 'good embed' ]) {
447 { 422 {
448 const uuid = (await uploadVideoAndGetId({ server: servers[0], videoName: name })).uuid 423 const uuid = (await servers[0].videos.quickUpload({ name: name })).uuid
449 const res = await getVideo(servers[0].url, uuid) 424 embedVideos.push(await servers[0].videos.get({ id: uuid }))
450 embedVideos.push(res.body)
451 } 425 }
452 426
453 { 427 {
454 const playlistAttrs = { displayName: name, videoChannelId: servers[0].videoChannel.id, privacy: VideoPlaylistPrivacy.PUBLIC } 428 const attributes = { displayName: name, videoChannelId: servers[0].store.channel.id, privacy: VideoPlaylistPrivacy.PUBLIC }
455 const res = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs }) 429 const { id } = await servers[0].playlists.create({ attributes })
456 430
457 const resPlaylist = await getVideoPlaylist(servers[0].url, res.body.videoPlaylist.id) 431 const playlist = await servers[0].playlists.get({ playlistId: id })
458 embedPlaylists.push(resPlaylist.body) 432 embedPlaylists.push(playlist)
459 } 433 }
460 } 434 }
461 }) 435 })
@@ -474,78 +448,92 @@ describe('Test plugin filter hooks', function () {
474 describe('Search filters', function () { 448 describe('Search filters', function () {
475 449
476 before(async function () { 450 before(async function () {
477 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 451 await servers[0].config.updateCustomSubConfig({
478 search: { 452 newConfig: {
479 searchIndex: { 453 search: {
480 enabled: true, 454 searchIndex: {
481 isDefaultSearch: false, 455 enabled: true,
482 disableLocalSearch: false 456 isDefaultSearch: false,
457 disableLocalSearch: false
458 }
483 } 459 }
484 } 460 }
485 }) 461 })
486 }) 462 })
487 463
488 it('Should run filter:api.search.videos.local.list.{params,result}', async function () { 464 it('Should run filter:api.search.videos.local.list.{params,result}', async function () {
489 await advancedVideosSearch(servers[0].url, { 465 await servers[0].search.advancedVideoSearch({
490 search: 'Sun Quan' 466 search: {
467 search: 'Sun Quan'
468 }
491 }) 469 })
492 470
493 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.params', 1) 471 await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.local.list.params', 1)
494 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.result', 1) 472 await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.local.list.result', 1)
495 }) 473 })
496 474
497 it('Should run filter:api.search.videos.index.list.{params,result}', async function () { 475 it('Should run filter:api.search.videos.index.list.{params,result}', async function () {
498 await advancedVideosSearch(servers[0].url, { 476 await servers[0].search.advancedVideoSearch({
499 search: 'Sun Quan', 477 search: {
500 searchTarget: 'search-index' 478 search: 'Sun Quan',
479 searchTarget: 'search-index'
480 }
501 }) 481 })
502 482
503 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.params', 1) 483 await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.local.list.params', 1)
504 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.result', 1) 484 await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.local.list.result', 1)
505 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.index.list.params', 1) 485 await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.index.list.params', 1)
506 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.index.list.result', 1) 486 await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.index.list.result', 1)
507 }) 487 })
508 488
509 it('Should run filter:api.search.video-channels.local.list.{params,result}', async function () { 489 it('Should run filter:api.search.video-channels.local.list.{params,result}', async function () {
510 await advancedVideoChannelSearch(servers[0].url, { 490 await servers[0].search.advancedChannelSearch({
511 search: 'Sun Ce' 491 search: {
492 search: 'Sun Ce'
493 }
512 }) 494 })
513 495
514 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.params', 1) 496 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.local.list.params', 1)
515 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.result', 1) 497 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.local.list.result', 1)
516 }) 498 })
517 499
518 it('Should run filter:api.search.video-channels.index.list.{params,result}', async function () { 500 it('Should run filter:api.search.video-channels.index.list.{params,result}', async function () {
519 await advancedVideoChannelSearch(servers[0].url, { 501 await servers[0].search.advancedChannelSearch({
520 search: 'Sun Ce', 502 search: {
521 searchTarget: 'search-index' 503 search: 'Sun Ce',
504 searchTarget: 'search-index'
505 }
522 }) 506 })
523 507
524 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.params', 1) 508 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.local.list.params', 1)
525 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.result', 1) 509 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.local.list.result', 1)
526 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.index.list.params', 1) 510 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.index.list.params', 1)
527 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.index.list.result', 1) 511 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.index.list.result', 1)
528 }) 512 })
529 513
530 it('Should run filter:api.search.video-playlists.local.list.{params,result}', async function () { 514 it('Should run filter:api.search.video-playlists.local.list.{params,result}', async function () {
531 await advancedVideoPlaylistSearch(servers[0].url, { 515 await servers[0].search.advancedPlaylistSearch({
532 search: 'Sun Jian' 516 search: {
517 search: 'Sun Jian'
518 }
533 }) 519 })
534 520
535 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-playlists.local.list.params', 1) 521 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.local.list.params', 1)
536 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-playlists.local.list.result', 1) 522 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.local.list.result', 1)
537 }) 523 })
538 524
539 it('Should run filter:api.search.video-playlists.index.list.{params,result}', async function () { 525 it('Should run filter:api.search.video-playlists.index.list.{params,result}', async function () {
540 await advancedVideoPlaylistSearch(servers[0].url, { 526 await servers[0].search.advancedPlaylistSearch({
541 search: 'Sun Jian', 527 search: {
542 searchTarget: 'search-index' 528 search: 'Sun Jian',
529 searchTarget: 'search-index'
530 }
543 }) 531 })
544 532
545 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-playlists.local.list.params', 1) 533 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.local.list.params', 1)
546 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-playlists.local.list.result', 1) 534 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.local.list.result', 1)
547 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-playlists.index.list.params', 1) 535 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.index.list.params', 1)
548 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-playlists.index.list.result', 1) 536 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.index.list.result', 1)
549 }) 537 })
550 }) 538 })
551 539
diff --git a/server/tests/plugins/html-injection.ts b/server/tests/plugins/html-injection.ts
index 4fa8caa3a..95c0cd687 100644
--- a/server/tests/plugins/html-injection.ts
+++ b/server/tests/plugins/html-injection.ts
@@ -4,31 +4,32 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 flushAndRunServer, 7 createSingleServer,
8 getPluginsCSS,
9 installPlugin,
10 makeHTMLRequest, 8 makeHTMLRequest,
11 ServerInfo, 9 PeerTubeServer,
12 setAccessTokensToServers, 10 PluginsCommand,
13 uninstallPlugin 11 setAccessTokensToServers
14} from '../../../shared/extra-utils' 12} from '../../../shared/extra-utils'
15 13
16const expect = chai.expect 14const expect = chai.expect
17 15
18describe('Test plugins HTML injection', function () { 16describe('Test plugins HTML injection', function () {
19 let server: ServerInfo = null 17 let server: PeerTubeServer = null
18 let command: PluginsCommand
20 19
21 before(async function () { 20 before(async function () {
22 this.timeout(30000) 21 this.timeout(30000)
23 22
24 server = await flushAndRunServer(1) 23 server = await createSingleServer(1)
25 await setAccessTokensToServers([ server ]) 24 await setAccessTokensToServers([ server ])
25
26 command = server.plugins
26 }) 27 })
27 28
28 it('Should not inject global css file in HTML', async function () { 29 it('Should not inject global css file in HTML', async function () {
29 { 30 {
30 const res = await getPluginsCSS(server.url) 31 const text = await command.getCSS()
31 expect(res.text).to.be.empty 32 expect(text).to.be.empty
32 } 33 }
33 34
34 for (const path of [ '/', '/videos/embed/1', '/video-playlists/embed/1' ]) { 35 for (const path of [ '/', '/videos/embed/1', '/video-playlists/embed/1' ]) {
@@ -40,17 +41,13 @@ describe('Test plugins HTML injection', function () {
40 it('Should install a plugin and a theme', async function () { 41 it('Should install a plugin and a theme', async function () {
41 this.timeout(30000) 42 this.timeout(30000)
42 43
43 await installPlugin({ 44 await command.install({ npmName: 'peertube-plugin-hello-world' })
44 url: server.url,
45 accessToken: server.accessToken,
46 npmName: 'peertube-plugin-hello-world'
47 })
48 }) 45 })
49 46
50 it('Should have the correct global css', async function () { 47 it('Should have the correct global css', async function () {
51 { 48 {
52 const res = await getPluginsCSS(server.url) 49 const text = await command.getCSS()
53 expect(res.text).to.contain('background-color: red') 50 expect(text).to.contain('background-color: red')
54 } 51 }
55 52
56 for (const path of [ '/', '/videos/embed/1', '/video-playlists/embed/1' ]) { 53 for (const path of [ '/', '/videos/embed/1', '/video-playlists/embed/1' ]) {
@@ -60,15 +57,11 @@ describe('Test plugins HTML injection', function () {
60 }) 57 })
61 58
62 it('Should have an empty global css on uninstall', async function () { 59 it('Should have an empty global css on uninstall', async function () {
63 await uninstallPlugin({ 60 await command.uninstall({ npmName: 'peertube-plugin-hello-world' })
64 url: server.url,
65 accessToken: server.accessToken,
66 npmName: 'peertube-plugin-hello-world'
67 })
68 61
69 { 62 {
70 const res = await getPluginsCSS(server.url) 63 const text = await command.getCSS()
71 expect(res.text).to.be.empty 64 expect(text).to.be.empty
72 } 65 }
73 66
74 for (const path of [ '/', '/videos/embed/1', '/video-playlists/embed/1' ]) { 67 for (const path of [ '/', '/videos/embed/1', '/video-playlists/embed/1' ]) {
diff --git a/server/tests/plugins/id-and-pass-auth.ts b/server/tests/plugins/id-and-pass-auth.ts
index cbba638c2..fde0166f9 100644
--- a/server/tests/plugins/id-and-pass-auth.ts
+++ b/server/tests/plugins/id-and-pass-auth.ts
@@ -1,24 +1,12 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { cleanupTests, flushAndRunServer, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers'
5import {
6 getMyUserInformation,
7 getPluginTestPath,
8 installPlugin,
9 logout,
10 setAccessTokensToServers,
11 uninstallPlugin,
12 updateMyUser,
13 userLogin,
14 wait,
15 login, refreshToken, getConfig, updatePluginSettings, getUsersList
16} from '../../../shared/extra-utils'
17import { User, UserRole, ServerConfig } from '@shared/models'
18import { expect } from 'chai' 4import { expect } from 'chai'
5import { cleanupTests, createSingleServer, PeerTubeServer, PluginsCommand, setAccessTokensToServers, wait } from '@shared/extra-utils'
6import { HttpStatusCode, UserRole } from '@shared/models'
19 7
20describe('Test id and pass auth plugins', function () { 8describe('Test id and pass auth plugins', function () {
21 let server: ServerInfo 9 let server: PeerTubeServer
22 10
23 let crashAccessToken: string 11 let crashAccessToken: string
24 let crashRefreshToken: string 12 let crashRefreshToken: string
@@ -29,22 +17,16 @@ describe('Test id and pass auth plugins', function () {
29 before(async function () { 17 before(async function () {
30 this.timeout(30000) 18 this.timeout(30000)
31 19
32 server = await flushAndRunServer(1) 20 server = await createSingleServer(1)
33 await setAccessTokensToServers([ server ]) 21 await setAccessTokensToServers([ server ])
34 22
35 for (const suffix of [ 'one', 'two', 'three' ]) { 23 for (const suffix of [ 'one', 'two', 'three' ]) {
36 await installPlugin({ 24 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-id-pass-auth-' + suffix) })
37 url: server.url,
38 accessToken: server.accessToken,
39 path: getPluginTestPath('-id-pass-auth-' + suffix)
40 })
41 } 25 }
42 }) 26 })
43 27
44 it('Should display the correct configuration', async function () { 28 it('Should display the correct configuration', async function () {
45 const res = await getConfig(server.url) 29 const config = await server.config.getConfig()
46
47 const config: ServerConfig = res.body
48 30
49 const auths = config.plugin.registeredIdAndPassAuths 31 const auths = config.plugin.registeredIdAndPassAuths
50 expect(auths).to.have.lengthOf(8) 32 expect(auths).to.have.lengthOf(8)
@@ -56,15 +38,14 @@ describe('Test id and pass auth plugins', function () {
56 }) 38 })
57 39
58 it('Should not login', async function () { 40 it('Should not login', async function () {
59 await userLogin(server, { username: 'toto', password: 'password' }, 400) 41 await server.login.login({ user: { username: 'toto', password: 'password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
60 }) 42 })
61 43
62 it('Should login Spyro, create the user and use the token', async function () { 44 it('Should login Spyro, create the user and use the token', async function () {
63 const accessToken = await userLogin(server, { username: 'spyro', password: 'spyro password' }) 45 const accessToken = await server.login.getAccessToken({ username: 'spyro', password: 'spyro password' })
64 46
65 const res = await getMyUserInformation(server.url, accessToken) 47 const body = await server.users.getMyInfo({ token: accessToken })
66 48
67 const body: User = res.body
68 expect(body.username).to.equal('spyro') 49 expect(body.username).to.equal('spyro')
69 expect(body.account.displayName).to.equal('Spyro the Dragon') 50 expect(body.account.displayName).to.equal('Spyro the Dragon')
70 expect(body.role).to.equal(UserRole.USER) 51 expect(body.role).to.equal(UserRole.USER)
@@ -72,15 +53,14 @@ describe('Test id and pass auth plugins', function () {
72 53
73 it('Should login Crash, create the user and use the token', async function () { 54 it('Should login Crash, create the user and use the token', async function () {
74 { 55 {
75 const res = await login(server.url, server.client, { username: 'crash', password: 'crash password' }) 56 const body = await server.login.login({ user: { username: 'crash', password: 'crash password' } })
76 crashAccessToken = res.body.access_token 57 crashAccessToken = body.access_token
77 crashRefreshToken = res.body.refresh_token 58 crashRefreshToken = body.refresh_token
78 } 59 }
79 60
80 { 61 {
81 const res = await getMyUserInformation(server.url, crashAccessToken) 62 const body = await server.users.getMyInfo({ token: crashAccessToken })
82 63
83 const body: User = res.body
84 expect(body.username).to.equal('crash') 64 expect(body.username).to.equal('crash')
85 expect(body.account.displayName).to.equal('Crash Bandicoot') 65 expect(body.account.displayName).to.equal('Crash Bandicoot')
86 expect(body.role).to.equal(UserRole.MODERATOR) 66 expect(body.role).to.equal(UserRole.MODERATOR)
@@ -89,15 +69,14 @@ describe('Test id and pass auth plugins', function () {
89 69
90 it('Should login the first Laguna, create the user and use the token', async function () { 70 it('Should login the first Laguna, create the user and use the token', async function () {
91 { 71 {
92 const res = await login(server.url, server.client, { username: 'laguna', password: 'laguna password' }) 72 const body = await server.login.login({ user: { username: 'laguna', password: 'laguna password' } })
93 lagunaAccessToken = res.body.access_token 73 lagunaAccessToken = body.access_token
94 lagunaRefreshToken = res.body.refresh_token 74 lagunaRefreshToken = body.refresh_token
95 } 75 }
96 76
97 { 77 {
98 const res = await getMyUserInformation(server.url, lagunaAccessToken) 78 const body = await server.users.getMyInfo({ token: lagunaAccessToken })
99 79
100 const body: User = res.body
101 expect(body.username).to.equal('laguna') 80 expect(body.username).to.equal('laguna')
102 expect(body.account.displayName).to.equal('laguna') 81 expect(body.account.displayName).to.equal('laguna')
103 expect(body.role).to.equal(UserRole.USER) 82 expect(body.role).to.equal(UserRole.USER)
@@ -106,51 +85,47 @@ describe('Test id and pass auth plugins', function () {
106 85
107 it('Should refresh crash token, but not laguna token', async function () { 86 it('Should refresh crash token, but not laguna token', async function () {
108 { 87 {
109 const resRefresh = await refreshToken(server, crashRefreshToken) 88 const resRefresh = await server.login.refreshToken({ refreshToken: crashRefreshToken })
110 crashAccessToken = resRefresh.body.access_token 89 crashAccessToken = resRefresh.body.access_token
111 crashRefreshToken = resRefresh.body.refresh_token 90 crashRefreshToken = resRefresh.body.refresh_token
112 91
113 const res = await getMyUserInformation(server.url, crashAccessToken) 92 const body = await server.users.getMyInfo({ token: crashAccessToken })
114 const user: User = res.body 93 expect(body.username).to.equal('crash')
115 expect(user.username).to.equal('crash')
116 } 94 }
117 95
118 { 96 {
119 await refreshToken(server, lagunaRefreshToken, 400) 97 await server.login.refreshToken({ refreshToken: lagunaRefreshToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
120 } 98 }
121 }) 99 })
122 100
123 it('Should update Crash profile', async function () { 101 it('Should update Crash profile', async function () {
124 await updateMyUser({ 102 await server.users.updateMe({
125 url: server.url, 103 token: crashAccessToken,
126 accessToken: crashAccessToken,
127 displayName: 'Beautiful Crash', 104 displayName: 'Beautiful Crash',
128 description: 'Mutant eastern barred bandicoot' 105 description: 'Mutant eastern barred bandicoot'
129 }) 106 })
130 107
131 const res = await getMyUserInformation(server.url, crashAccessToken) 108 const body = await server.users.getMyInfo({ token: crashAccessToken })
132 109
133 const body: User = res.body
134 expect(body.account.displayName).to.equal('Beautiful Crash') 110 expect(body.account.displayName).to.equal('Beautiful Crash')
135 expect(body.account.description).to.equal('Mutant eastern barred bandicoot') 111 expect(body.account.description).to.equal('Mutant eastern barred bandicoot')
136 }) 112 })
137 113
138 it('Should logout Crash', async function () { 114 it('Should logout Crash', async function () {
139 await logout(server.url, crashAccessToken) 115 await server.login.logout({ token: crashAccessToken })
140 }) 116 })
141 117
142 it('Should have logged out Crash', async function () { 118 it('Should have logged out Crash', async function () {
143 await waitUntilLog(server, 'On logout for auth 1 - 2') 119 await server.servers.waitUntilLog('On logout for auth 1 - 2')
144 120
145 await getMyUserInformation(server.url, crashAccessToken, 401) 121 await server.users.getMyInfo({ token: crashAccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
146 }) 122 })
147 123
148 it('Should login Crash and keep the old existing profile', async function () { 124 it('Should login Crash and keep the old existing profile', async function () {
149 crashAccessToken = await userLogin(server, { username: 'crash', password: 'crash password' }) 125 crashAccessToken = await server.login.getAccessToken({ username: 'crash', password: 'crash password' })
150 126
151 const res = await getMyUserInformation(server.url, crashAccessToken) 127 const body = await server.users.getMyInfo({ token: crashAccessToken })
152 128
153 const body: User = res.body
154 expect(body.username).to.equal('crash') 129 expect(body.username).to.equal('crash')
155 expect(body.account.displayName).to.equal('Beautiful Crash') 130 expect(body.account.displayName).to.equal('Beautiful Crash')
156 expect(body.account.description).to.equal('Mutant eastern barred bandicoot') 131 expect(body.account.description).to.equal('Mutant eastern barred bandicoot')
@@ -162,39 +137,38 @@ describe('Test id and pass auth plugins', function () {
162 137
163 await wait(5000) 138 await wait(5000)
164 139
165 await getMyUserInformation(server.url, lagunaAccessToken, 401) 140 await server.users.getMyInfo({ token: lagunaAccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
166 }) 141 })
167 142
168 it('Should reject an invalid username, email, role or display name', async function () { 143 it('Should reject an invalid username, email, role or display name', async function () {
169 await userLogin(server, { username: 'ward', password: 'ward password' }, 400) 144 const command = server.login
170 await waitUntilLog(server, 'valid username')
171 145
172 await userLogin(server, { username: 'kiros', password: 'kiros password' }, 400) 146 await command.login({ user: { username: 'ward', password: 'ward password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
173 await waitUntilLog(server, 'valid display name') 147 await server.servers.waitUntilLog('valid username')
174 148
175 await userLogin(server, { username: 'raine', password: 'raine password' }, 400) 149 await command.login({ user: { username: 'kiros', password: 'kiros password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
176 await waitUntilLog(server, 'valid role') 150 await server.servers.waitUntilLog('valid display name')
177 151
178 await userLogin(server, { username: 'ellone', password: 'elonne password' }, 400) 152 await command.login({ user: { username: 'raine', password: 'raine password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
179 await waitUntilLog(server, 'valid email') 153 await server.servers.waitUntilLog('valid role')
154
155 await command.login({ user: { username: 'ellone', password: 'elonne password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
156 await server.servers.waitUntilLog('valid email')
180 }) 157 })
181 158
182 it('Should unregister spyro-auth and do not login existing Spyro', async function () { 159 it('Should unregister spyro-auth and do not login existing Spyro', async function () {
183 await updatePluginSettings({ 160 await server.plugins.updateSettings({
184 url: server.url,
185 accessToken: server.accessToken,
186 npmName: 'peertube-plugin-test-id-pass-auth-one', 161 npmName: 'peertube-plugin-test-id-pass-auth-one',
187 settings: { disableSpyro: true } 162 settings: { disableSpyro: true }
188 }) 163 })
189 164
190 await userLogin(server, { username: 'spyro', password: 'spyro password' }, 400) 165 const command = server.login
191 await userLogin(server, { username: 'spyro', password: 'fake' }, 400) 166 await command.login({ user: { username: 'spyro', password: 'spyro password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
167 await command.login({ user: { username: 'spyro', password: 'fake' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
192 }) 168 })
193 169
194 it('Should have disabled this auth', async function () { 170 it('Should have disabled this auth', async function () {
195 const res = await getConfig(server.url) 171 const config = await server.config.getConfig()
196
197 const config: ServerConfig = res.body
198 172
199 const auths = config.plugin.registeredIdAndPassAuths 173 const auths = config.plugin.registeredIdAndPassAuths
200 expect(auths).to.have.lengthOf(7) 174 expect(auths).to.have.lengthOf(7)
@@ -204,19 +178,16 @@ describe('Test id and pass auth plugins', function () {
204 }) 178 })
205 179
206 it('Should uninstall the plugin one and do not login existing Crash', async function () { 180 it('Should uninstall the plugin one and do not login existing Crash', async function () {
207 await uninstallPlugin({ 181 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-id-pass-auth-one' })
208 url: server.url,
209 accessToken: server.accessToken,
210 npmName: 'peertube-plugin-test-id-pass-auth-one'
211 })
212 182
213 await userLogin(server, { username: 'crash', password: 'crash password' }, 400) 183 await server.login.login({
184 user: { username: 'crash', password: 'crash password' },
185 expectedStatus: HttpStatusCode.BAD_REQUEST_400
186 })
214 }) 187 })
215 188
216 it('Should display the correct configuration', async function () { 189 it('Should display the correct configuration', async function () {
217 const res = await getConfig(server.url) 190 const config = await server.config.getConfig()
218
219 const config: ServerConfig = res.body
220 191
221 const auths = config.plugin.registeredIdAndPassAuths 192 const auths = config.plugin.registeredIdAndPassAuths
222 expect(auths).to.have.lengthOf(6) 193 expect(auths).to.have.lengthOf(6)
@@ -226,13 +197,11 @@ describe('Test id and pass auth plugins', function () {
226 }) 197 })
227 198
228 it('Should display plugin auth information in users list', async function () { 199 it('Should display plugin auth information in users list', async function () {
229 const res = await getUsersList(server.url, server.accessToken) 200 const { data } = await server.users.list()
230
231 const users: User[] = res.body.data
232 201
233 const root = users.find(u => u.username === 'root') 202 const root = data.find(u => u.username === 'root')
234 const crash = users.find(u => u.username === 'crash') 203 const crash = data.find(u => u.username === 'crash')
235 const laguna = users.find(u => u.username === 'laguna') 204 const laguna = data.find(u => u.username === 'laguna')
236 205
237 expect(root.pluginAuth).to.be.null 206 expect(root.pluginAuth).to.be.null
238 expect(crash.pluginAuth).to.equal('peertube-plugin-test-id-pass-auth-one') 207 expect(crash.pluginAuth).to.equal('peertube-plugin-test-id-pass-auth-one')
diff --git a/server/tests/plugins/plugin-helpers.ts b/server/tests/plugins/plugin-helpers.ts
index 0296d6eb7..5d16b28a4 100644
--- a/server/tests/plugins/plugin-helpers.ts
+++ b/server/tests/plugins/plugin-helpers.ts
@@ -1,25 +1,22 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai'
4import { 5import {
5 checkVideoFilesWereRemoved, 6 checkVideoFilesWereRemoved,
7 cleanupTests,
8 createMultipleServers,
6 doubleFollow, 9 doubleFollow,
7 getPluginTestPath, 10 makeGetRequest,
8 getVideo,
9 installPlugin,
10 makePostBodyRequest, 11 makePostBodyRequest,
12 PeerTubeServer,
13 PluginsCommand,
11 setAccessTokensToServers, 14 setAccessTokensToServers,
12 uploadVideoAndGetId, 15 waitJobs
13 viewVideo, 16} from '@shared/extra-utils'
14 getVideosList, 17import { HttpStatusCode } from '@shared/models'
15 waitJobs,
16 makeGetRequest
17} from '../../../shared/extra-utils'
18import { cleanupTests, flushAndRunMultipleServers, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers'
19import { expect } from 'chai'
20import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
21 18
22function postCommand (server: ServerInfo, command: string, bodyArg?: object) { 19function postCommand (server: PeerTubeServer, command: string, bodyArg?: object) {
23 const body = { command } 20 const body = { command }
24 if (bodyArg) Object.assign(body, bodyArg) 21 if (bodyArg) Object.assign(body, bodyArg)
25 22
@@ -27,54 +24,50 @@ function postCommand (server: ServerInfo, command: string, bodyArg?: object) {
27 url: server.url, 24 url: server.url,
28 path: '/plugins/test-four/router/commander', 25 path: '/plugins/test-four/router/commander',
29 fields: body, 26 fields: body,
30 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 27 expectedStatus: HttpStatusCode.NO_CONTENT_204
31 }) 28 })
32} 29}
33 30
34describe('Test plugin helpers', function () { 31describe('Test plugin helpers', function () {
35 let servers: ServerInfo[] 32 let servers: PeerTubeServer[]
36 33
37 before(async function () { 34 before(async function () {
38 this.timeout(60000) 35 this.timeout(60000)
39 36
40 servers = await flushAndRunMultipleServers(2) 37 servers = await createMultipleServers(2)
41 await setAccessTokensToServers(servers) 38 await setAccessTokensToServers(servers)
42 39
43 await doubleFollow(servers[0], servers[1]) 40 await doubleFollow(servers[0], servers[1])
44 41
45 await installPlugin({ 42 await servers[0].plugins.install({ path: PluginsCommand.getPluginTestPath('-four') })
46 url: servers[0].url,
47 accessToken: servers[0].accessToken,
48 path: getPluginTestPath('-four')
49 })
50 }) 43 })
51 44
52 describe('Logger', function () { 45 describe('Logger', function () {
53 46
54 it('Should have logged things', async function () { 47 it('Should have logged things', async function () {
55 await waitUntilLog(servers[0], 'localhost:' + servers[0].port + ' peertube-plugin-test-four', 1, false) 48 await servers[0].servers.waitUntilLog('localhost:' + servers[0].port + ' peertube-plugin-test-four', 1, false)
56 await waitUntilLog(servers[0], 'Hello world from plugin four', 1) 49 await servers[0].servers.waitUntilLog('Hello world from plugin four', 1)
57 }) 50 })
58 }) 51 })
59 52
60 describe('Database', function () { 53 describe('Database', function () {
61 54
62 it('Should have made a query', async function () { 55 it('Should have made a query', async function () {
63 await waitUntilLog(servers[0], `root email is admin${servers[0].internalServerNumber}@example.com`) 56 await servers[0].servers.waitUntilLog(`root email is admin${servers[0].internalServerNumber}@example.com`)
64 }) 57 })
65 }) 58 })
66 59
67 describe('Config', function () { 60 describe('Config', function () {
68 61
69 it('Should have the correct webserver url', async function () { 62 it('Should have the correct webserver url', async function () {
70 await waitUntilLog(servers[0], `server url is http://localhost:${servers[0].port}`) 63 await servers[0].servers.waitUntilLog(`server url is http://localhost:${servers[0].port}`)
71 }) 64 })
72 65
73 it('Should have the correct config', async function () { 66 it('Should have the correct config', async function () {
74 const res = await makeGetRequest({ 67 const res = await makeGetRequest({
75 url: servers[0].url, 68 url: servers[0].url,
76 path: '/plugins/test-four/router/server-config', 69 path: '/plugins/test-four/router/server-config',
77 statusCodeExpected: HttpStatusCode.OK_200 70 expectedStatus: HttpStatusCode.OK_200
78 }) 71 })
79 72
80 expect(res.body.serverConfig).to.exist 73 expect(res.body.serverConfig).to.exist
@@ -85,7 +78,7 @@ describe('Test plugin helpers', function () {
85 describe('Server', function () { 78 describe('Server', function () {
86 79
87 it('Should get the server actor', async function () { 80 it('Should get the server actor', async function () {
88 await waitUntilLog(servers[0], 'server actor name is peertube') 81 await servers[0].servers.waitUntilLog('server actor name is peertube')
89 }) 82 })
90 }) 83 })
91 84
@@ -95,7 +88,7 @@ describe('Test plugin helpers', function () {
95 const res = await makeGetRequest({ 88 const res = await makeGetRequest({
96 url: servers[0].url, 89 url: servers[0].url,
97 path: '/plugins/test-four/router/static-route', 90 path: '/plugins/test-four/router/static-route',
98 statusCodeExpected: HttpStatusCode.OK_200 91 expectedStatus: HttpStatusCode.OK_200
99 }) 92 })
100 93
101 expect(res.body.staticRoute).to.equal('/plugins/test-four/0.0.1/static/') 94 expect(res.body.staticRoute).to.equal('/plugins/test-four/0.0.1/static/')
@@ -107,7 +100,7 @@ describe('Test plugin helpers', function () {
107 const res = await makeGetRequest({ 100 const res = await makeGetRequest({
108 url: servers[0].url, 101 url: servers[0].url,
109 path: baseRouter + 'router-route', 102 path: baseRouter + 'router-route',
110 statusCodeExpected: HttpStatusCode.OK_200 103 expectedStatus: HttpStatusCode.OK_200
111 }) 104 })
112 105
113 expect(res.body.routerRoute).to.equal(baseRouter) 106 expect(res.body.routerRoute).to.equal(baseRouter)
@@ -120,7 +113,7 @@ describe('Test plugin helpers', function () {
120 await makeGetRequest({ 113 await makeGetRequest({
121 url: servers[0].url, 114 url: servers[0].url,
122 path: '/plugins/test-four/router/user', 115 path: '/plugins/test-four/router/user',
123 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 116 expectedStatus: HttpStatusCode.NOT_FOUND_404
124 }) 117 })
125 }) 118 })
126 119
@@ -129,7 +122,7 @@ describe('Test plugin helpers', function () {
129 url: servers[0].url, 122 url: servers[0].url,
130 token: servers[0].accessToken, 123 token: servers[0].accessToken,
131 path: '/plugins/test-four/router/user', 124 path: '/plugins/test-four/router/user',
132 statusCodeExpected: HttpStatusCode.OK_200 125 expectedStatus: HttpStatusCode.OK_200
133 }) 126 })
134 127
135 expect(res.body.username).to.equal('root') 128 expect(res.body.username).to.equal('root')
@@ -147,59 +140,54 @@ describe('Test plugin helpers', function () {
147 this.timeout(60000) 140 this.timeout(60000)
148 141
149 { 142 {
150 const res = await uploadVideoAndGetId({ server: servers[0], videoName: 'video server 1' }) 143 const res = await servers[0].videos.quickUpload({ name: 'video server 1' })
151 videoUUIDServer1 = res.uuid 144 videoUUIDServer1 = res.uuid
152 } 145 }
153 146
154 { 147 {
155 await uploadVideoAndGetId({ server: servers[1], videoName: 'video server 2' }) 148 await servers[1].videos.quickUpload({ name: 'video server 2' })
156 } 149 }
157 150
158 await waitJobs(servers) 151 await waitJobs(servers)
159 152
160 const res = await getVideosList(servers[0].url) 153 const { data } = await servers[0].videos.list()
161 const videos = res.body.data
162 154
163 expect(videos).to.have.lengthOf(2) 155 expect(data).to.have.lengthOf(2)
164 }) 156 })
165 157
166 it('Should mute server 2', async function () { 158 it('Should mute server 2', async function () {
167 this.timeout(10000) 159 this.timeout(10000)
168 await postCommand(servers[0], 'blockServer', { hostToBlock: `localhost:${servers[1].port}` }) 160 await postCommand(servers[0], 'blockServer', { hostToBlock: `localhost:${servers[1].port}` })
169 161
170 const res = await getVideosList(servers[0].url) 162 const { data } = await servers[0].videos.list()
171 const videos = res.body.data
172 163
173 expect(videos).to.have.lengthOf(1) 164 expect(data).to.have.lengthOf(1)
174 expect(videos[0].name).to.equal('video server 1') 165 expect(data[0].name).to.equal('video server 1')
175 }) 166 })
176 167
177 it('Should unmute server 2', async function () { 168 it('Should unmute server 2', async function () {
178 await postCommand(servers[0], 'unblockServer', { hostToUnblock: `localhost:${servers[1].port}` }) 169 await postCommand(servers[0], 'unblockServer', { hostToUnblock: `localhost:${servers[1].port}` })
179 170
180 const res = await getVideosList(servers[0].url) 171 const { data } = await servers[0].videos.list()
181 const videos = res.body.data
182 172
183 expect(videos).to.have.lengthOf(2) 173 expect(data).to.have.lengthOf(2)
184 }) 174 })
185 175
186 it('Should mute account of server 2', async function () { 176 it('Should mute account of server 2', async function () {
187 await postCommand(servers[0], 'blockAccount', { handleToBlock: `root@localhost:${servers[1].port}` }) 177 await postCommand(servers[0], 'blockAccount', { handleToBlock: `root@localhost:${servers[1].port}` })
188 178
189 const res = await getVideosList(servers[0].url) 179 const { data } = await servers[0].videos.list()
190 const videos = res.body.data
191 180
192 expect(videos).to.have.lengthOf(1) 181 expect(data).to.have.lengthOf(1)
193 expect(videos[0].name).to.equal('video server 1') 182 expect(data[0].name).to.equal('video server 1')
194 }) 183 })
195 184
196 it('Should unmute account of server 2', async function () { 185 it('Should unmute account of server 2', async function () {
197 await postCommand(servers[0], 'unblockAccount', { handleToUnblock: `root@localhost:${servers[1].port}` }) 186 await postCommand(servers[0], 'unblockAccount', { handleToUnblock: `root@localhost:${servers[1].port}` })
198 187
199 const res = await getVideosList(servers[0].url) 188 const { data } = await servers[0].videos.list()
200 const videos = res.body.data
201 189
202 expect(videos).to.have.lengthOf(2) 190 expect(data).to.have.lengthOf(2)
203 }) 191 })
204 192
205 it('Should blacklist video', async function () { 193 it('Should blacklist video', async function () {
@@ -210,11 +198,10 @@ describe('Test plugin helpers', function () {
210 await waitJobs(servers) 198 await waitJobs(servers)
211 199
212 for (const server of servers) { 200 for (const server of servers) {
213 const res = await getVideosList(server.url) 201 const { data } = await server.videos.list()
214 const videos = res.body.data
215 202
216 expect(videos).to.have.lengthOf(1) 203 expect(data).to.have.lengthOf(1)
217 expect(videos[0].name).to.equal('video server 2') 204 expect(data[0].name).to.equal('video server 2')
218 } 205 }
219 }) 206 })
220 207
@@ -226,10 +213,9 @@ describe('Test plugin helpers', function () {
226 await waitJobs(servers) 213 await waitJobs(servers)
227 214
228 for (const server of servers) { 215 for (const server of servers) {
229 const res = await getVideosList(server.url) 216 const { data } = await server.videos.list()
230 const videos = res.body.data
231 217
232 expect(videos).to.have.lengthOf(2) 218 expect(data).to.have.lengthOf(2)
233 } 219 }
234 }) 220 })
235 }) 221 })
@@ -238,7 +224,7 @@ describe('Test plugin helpers', function () {
238 let videoUUID: string 224 let videoUUID: string
239 225
240 before(async () => { 226 before(async () => {
241 const res = await uploadVideoAndGetId({ server: servers[0], videoName: 'video1' }) 227 const res = await servers[0].videos.quickUpload({ name: 'video1' })
242 videoUUID = res.uuid 228 videoUUID = res.uuid
243 }) 229 })
244 230
@@ -246,25 +232,25 @@ describe('Test plugin helpers', function () {
246 this.timeout(40000) 232 this.timeout(40000)
247 233
248 // Should not throw -> video exists 234 // Should not throw -> video exists
249 await getVideo(servers[0].url, videoUUID) 235 const video = await servers[0].videos.get({ id: videoUUID })
250 // Should delete the video 236 // Should delete the video
251 await viewVideo(servers[0].url, videoUUID) 237 await servers[0].videos.view({ id: videoUUID })
252 238
253 await waitUntilLog(servers[0], 'Video deleted by plugin four.') 239 await servers[0].servers.waitUntilLog('Video deleted by plugin four.')
254 240
255 try { 241 try {
256 // Should throw because the video should have been deleted 242 // Should throw because the video should have been deleted
257 await getVideo(servers[0].url, videoUUID) 243 await servers[0].videos.get({ id: videoUUID })
258 throw new Error('Video exists') 244 throw new Error('Video exists')
259 } catch (err) { 245 } catch (err) {
260 if (err.message.includes('exists')) throw err 246 if (err.message.includes('exists')) throw err
261 } 247 }
262 248
263 await checkVideoFilesWereRemoved(videoUUID, servers[0].internalServerNumber) 249 await checkVideoFilesWereRemoved({ server: servers[0], video })
264 }) 250 })
265 251
266 it('Should have fetched the video by URL', async function () { 252 it('Should have fetched the video by URL', async function () {
267 await waitUntilLog(servers[0], `video from DB uuid is ${videoUUID}`) 253 await servers[0].servers.waitUntilLog(`video from DB uuid is ${videoUUID}`)
268 }) 254 })
269 }) 255 })
270 256
diff --git a/server/tests/plugins/plugin-router.ts b/server/tests/plugins/plugin-router.ts
index 24e6a1e83..b1ac9e2fe 100644
--- a/server/tests/plugins/plugin-router.ts
+++ b/server/tests/plugins/plugin-router.ts
@@ -1,19 +1,20 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers' 4import { expect } from 'chai'
5import { 5import {
6 getPluginTestPath, 6 cleanupTests,
7 installPlugin, 7 createSingleServer,
8 makeGetRequest, 8 makeGetRequest,
9 makePostBodyRequest, 9 makePostBodyRequest,
10 setAccessTokensToServers, uninstallPlugin 10 PeerTubeServer,
11} from '../../../shared/extra-utils' 11 PluginsCommand,
12import { expect } from 'chai' 12 setAccessTokensToServers
13import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 13} from '@shared/extra-utils'
14import { HttpStatusCode } from '@shared/models'
14 15
15describe('Test plugin helpers', function () { 16describe('Test plugin helpers', function () {
16 let server: ServerInfo 17 let server: PeerTubeServer
17 const basePaths = [ 18 const basePaths = [
18 '/plugins/test-five/router/', 19 '/plugins/test-five/router/',
19 '/plugins/test-five/0.0.1/router/' 20 '/plugins/test-five/0.0.1/router/'
@@ -22,14 +23,10 @@ describe('Test plugin helpers', function () {
22 before(async function () { 23 before(async function () {
23 this.timeout(30000) 24 this.timeout(30000)
24 25
25 server = await flushAndRunServer(1) 26 server = await createSingleServer(1)
26 await setAccessTokensToServers([ server ]) 27 await setAccessTokensToServers([ server ])
27 28
28 await installPlugin({ 29 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-five') })
29 url: server.url,
30 accessToken: server.accessToken,
31 path: getPluginTestPath('-five')
32 })
33 }) 30 })
34 31
35 it('Should answer "pong"', async function () { 32 it('Should answer "pong"', async function () {
@@ -37,7 +34,7 @@ describe('Test plugin helpers', function () {
37 const res = await makeGetRequest({ 34 const res = await makeGetRequest({
38 url: server.url, 35 url: server.url,
39 path: path + 'ping', 36 path: path + 'ping',
40 statusCodeExpected: HttpStatusCode.OK_200 37 expectedStatus: HttpStatusCode.OK_200
41 }) 38 })
42 39
43 expect(res.body.message).to.equal('pong') 40 expect(res.body.message).to.equal('pong')
@@ -50,7 +47,7 @@ describe('Test plugin helpers', function () {
50 url: server.url, 47 url: server.url,
51 path: path + 'is-authenticated', 48 path: path + 'is-authenticated',
52 token: server.accessToken, 49 token: server.accessToken,
53 statusCodeExpected: 200 50 expectedStatus: 200
54 }) 51 })
55 52
56 expect(res.body.isAuthenticated).to.equal(true) 53 expect(res.body.isAuthenticated).to.equal(true)
@@ -58,7 +55,7 @@ describe('Test plugin helpers', function () {
58 const secRes = await makeGetRequest({ 55 const secRes = await makeGetRequest({
59 url: server.url, 56 url: server.url,
60 path: path + 'is-authenticated', 57 path: path + 'is-authenticated',
61 statusCodeExpected: 200 58 expectedStatus: 200
62 }) 59 })
63 60
64 expect(secRes.body.isAuthenticated).to.equal(false) 61 expect(secRes.body.isAuthenticated).to.equal(false)
@@ -77,7 +74,7 @@ describe('Test plugin helpers', function () {
77 url: server.url, 74 url: server.url,
78 path: path + 'form/post/mirror', 75 path: path + 'form/post/mirror',
79 fields: body, 76 fields: body,
80 statusCodeExpected: HttpStatusCode.OK_200 77 expectedStatus: HttpStatusCode.OK_200
81 }) 78 })
82 79
83 expect(res.body).to.deep.equal(body) 80 expect(res.body).to.deep.equal(body)
@@ -85,24 +82,20 @@ describe('Test plugin helpers', function () {
85 }) 82 })
86 83
87 it('Should remove the plugin and remove the routes', async function () { 84 it('Should remove the plugin and remove the routes', async function () {
88 await uninstallPlugin({ 85 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-five' })
89 url: server.url,
90 accessToken: server.accessToken,
91 npmName: 'peertube-plugin-test-five'
92 })
93 86
94 for (const path of basePaths) { 87 for (const path of basePaths) {
95 await makeGetRequest({ 88 await makeGetRequest({
96 url: server.url, 89 url: server.url,
97 path: path + 'ping', 90 path: path + 'ping',
98 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 91 expectedStatus: HttpStatusCode.NOT_FOUND_404
99 }) 92 })
100 93
101 await makePostBodyRequest({ 94 await makePostBodyRequest({
102 url: server.url, 95 url: server.url,
103 path: path + 'ping', 96 path: path + 'ping',
104 fields: {}, 97 fields: {},
105 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 98 expectedStatus: HttpStatusCode.NOT_FOUND_404
106 }) 99 })
107 } 100 }
108 }) 101 })
diff --git a/server/tests/plugins/plugin-storage.ts b/server/tests/plugins/plugin-storage.ts
index 3c46b2585..e20c36dba 100644
--- a/server/tests/plugins/plugin-storage.ts
+++ b/server/tests/plugins/plugin-storage.ts
@@ -4,37 +4,32 @@ import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { pathExists, readdir, readFile } from 'fs-extra' 5import { pathExists, readdir, readFile } from 'fs-extra'
6import { join } from 'path' 6import { join } from 'path'
7import { HttpStatusCode } from '@shared/core-utils'
8import { 7import {
9 buildServerDirectory, 8 cleanupTests,
10 getPluginTestPath, 9 createSingleServer,
11 installPlugin,
12 makeGetRequest, 10 makeGetRequest,
13 setAccessTokensToServers, 11 PeerTubeServer,
14 uninstallPlugin 12 PluginsCommand,
15} from '../../../shared/extra-utils' 13 setAccessTokensToServers
16import { cleanupTests, flushAndRunServer, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers' 14} from '@shared/extra-utils'
15import { HttpStatusCode } from '@shared/models'
17 16
18describe('Test plugin storage', function () { 17describe('Test plugin storage', function () {
19 let server: ServerInfo 18 let server: PeerTubeServer
20 19
21 before(async function () { 20 before(async function () {
22 this.timeout(30000) 21 this.timeout(30000)
23 22
24 server = await flushAndRunServer(1) 23 server = await createSingleServer(1)
25 await setAccessTokensToServers([ server ]) 24 await setAccessTokensToServers([ server ])
26 25
27 await installPlugin({ 26 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-six') })
28 url: server.url,
29 accessToken: server.accessToken,
30 path: getPluginTestPath('-six')
31 })
32 }) 27 })
33 28
34 describe('DB storage', function () { 29 describe('DB storage', function () {
35 30
36 it('Should correctly store a subkey', async function () { 31 it('Should correctly store a subkey', async function () {
37 await waitUntilLog(server, 'superkey stored value is toto') 32 await server.servers.waitUntilLog('superkey stored value is toto')
38 }) 33 })
39 }) 34 })
40 35
@@ -50,12 +45,12 @@ describe('Test plugin storage', function () {
50 } 45 }
51 46
52 before(function () { 47 before(function () {
53 dataPath = buildServerDirectory(server, 'plugins/data') 48 dataPath = server.servers.buildDirectory('plugins/data')
54 pluginDataPath = join(dataPath, 'peertube-plugin-test-six') 49 pluginDataPath = join(dataPath, 'peertube-plugin-test-six')
55 }) 50 })
56 51
57 it('Should have created the directory on install', async function () { 52 it('Should have created the directory on install', async function () {
58 const dataPath = buildServerDirectory(server, 'plugins/data') 53 const dataPath = server.servers.buildDirectory('plugins/data')
59 const pluginDataPath = join(dataPath, 'peertube-plugin-test-six') 54 const pluginDataPath = join(dataPath, 'peertube-plugin-test-six')
60 55
61 expect(await pathExists(dataPath)).to.be.true 56 expect(await pathExists(dataPath)).to.be.true
@@ -68,7 +63,7 @@ describe('Test plugin storage', function () {
68 url: server.url, 63 url: server.url,
69 token: server.accessToken, 64 token: server.accessToken,
70 path: '/plugins/test-six/router/create-file', 65 path: '/plugins/test-six/router/create-file',
71 statusCodeExpected: HttpStatusCode.OK_200 66 expectedStatus: HttpStatusCode.OK_200
72 }) 67 })
73 68
74 const content = await getFileContent() 69 const content = await getFileContent()
@@ -76,22 +71,14 @@ describe('Test plugin storage', function () {
76 }) 71 })
77 72
78 it('Should still have the file after an uninstallation', async function () { 73 it('Should still have the file after an uninstallation', async function () {
79 await uninstallPlugin({ 74 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-six' })
80 url: server.url,
81 accessToken: server.accessToken,
82 npmName: 'peertube-plugin-test-six'
83 })
84 75
85 const content = await getFileContent() 76 const content = await getFileContent()
86 expect(content).to.equal('Prince Ali') 77 expect(content).to.equal('Prince Ali')
87 }) 78 })
88 79
89 it('Should still have the file after the reinstallation', async function () { 80 it('Should still have the file after the reinstallation', async function () {
90 await installPlugin({ 81 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-six') })
91 url: server.url,
92 accessToken: server.accessToken,
93 path: getPluginTestPath('-six')
94 })
95 82
96 const content = await getFileContent() 83 const content = await getFileContent()
97 expect(content).to.equal('Prince Ali') 84 expect(content).to.equal('Prince Ali')
diff --git a/server/tests/plugins/plugin-transcoding.ts b/server/tests/plugins/plugin-transcoding.ts
index eefb2294d..93637e3ce 100644
--- a/server/tests/plugins/plugin-transcoding.ts
+++ b/server/tests/plugins/plugin-transcoding.ts
@@ -2,79 +2,73 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { join } from 'path'
6import { getAudioStream, getVideoFileFPS, getVideoStreamFromFile } from '@server/helpers/ffprobe-utils' 5import { getAudioStream, getVideoFileFPS, getVideoStreamFromFile } from '@server/helpers/ffprobe-utils'
7import { ServerConfig, VideoDetails, VideoPrivacy } from '@shared/models'
8import { 6import {
9 buildServerDirectory, 7 cleanupTests,
10 createLive, 8 createSingleServer,
11 getConfig, 9 PeerTubeServer,
12 getPluginTestPath, 10 PluginsCommand,
13 getVideo,
14 installPlugin,
15 sendRTMPStreamInVideo,
16 setAccessTokensToServers, 11 setAccessTokensToServers,
17 setDefaultVideoChannel, 12 setDefaultVideoChannel,
18 testFfmpegStreamError, 13 testFfmpegStreamError,
19 uninstallPlugin, 14 waitJobs
20 updateCustomSubConfig, 15} from '@shared/extra-utils'
21 uploadVideoAndGetId, 16import { VideoPrivacy } from '@shared/models'
22 waitJobs, 17
23 waitUntilLivePublished 18async function createLiveWrapper (server: PeerTubeServer) {
24} from '../../../shared/extra-utils'
25import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers'
26
27async function createLiveWrapper (server: ServerInfo) {
28 const liveAttributes = { 19 const liveAttributes = {
29 name: 'live video', 20 name: 'live video',
30 channelId: server.videoChannel.id, 21 channelId: server.store.channel.id,
31 privacy: VideoPrivacy.PUBLIC 22 privacy: VideoPrivacy.PUBLIC
32 } 23 }
33 24
34 const res = await createLive(server.url, server.accessToken, liveAttributes) 25 const { uuid } = await server.live.create({ fields: liveAttributes })
35 return res.body.video.uuid 26
27 return uuid
36} 28}
37 29
38function updateConf (server: ServerInfo, vodProfile: string, liveProfile: string) { 30function updateConf (server: PeerTubeServer, vodProfile: string, liveProfile: string) {
39 return updateCustomSubConfig(server.url, server.accessToken, { 31 return server.config.updateCustomSubConfig({
40 transcoding: { 32 newConfig: {
41 enabled: true,
42 profile: vodProfile,
43 hls: {
44 enabled: true
45 },
46 webtorrent: {
47 enabled: true
48 },
49 resolutions: {
50 '240p': true,
51 '360p': false,
52 '480p': false,
53 '720p': true
54 }
55 },
56 live: {
57 transcoding: { 33 transcoding: {
58 profile: liveProfile,
59 enabled: true, 34 enabled: true,
35 profile: vodProfile,
36 hls: {
37 enabled: true
38 },
39 webtorrent: {
40 enabled: true
41 },
60 resolutions: { 42 resolutions: {
61 '240p': true, 43 '240p': true,
62 '360p': false, 44 '360p': false,
63 '480p': false, 45 '480p': false,
64 '720p': true 46 '720p': true
65 } 47 }
48 },
49 live: {
50 transcoding: {
51 profile: liveProfile,
52 enabled: true,
53 resolutions: {
54 '240p': true,
55 '360p': false,
56 '480p': false,
57 '720p': true
58 }
59 }
66 } 60 }
67 } 61 }
68 }) 62 })
69} 63}
70 64
71describe('Test transcoding plugins', function () { 65describe('Test transcoding plugins', function () {
72 let server: ServerInfo 66 let server: PeerTubeServer
73 67
74 before(async function () { 68 before(async function () {
75 this.timeout(60000) 69 this.timeout(60000)
76 70
77 server = await flushAndRunServer(1) 71 server = await createSingleServer(1)
78 await setAccessTokensToServers([ server ]) 72 await setAccessTokensToServers([ server ])
79 await setDefaultVideoChannel([ server ]) 73 await setDefaultVideoChannel([ server ])
80 74
@@ -84,8 +78,7 @@ describe('Test transcoding plugins', function () {
84 describe('When using a plugin adding profiles to existing encoders', function () { 78 describe('When using a plugin adding profiles to existing encoders', function () {
85 79
86 async function checkVideoFPS (uuid: string, type: 'above' | 'below', fps: number) { 80 async function checkVideoFPS (uuid: string, type: 'above' | 'below', fps: number) {
87 const res = await getVideo(server.url, uuid) 81 const video = await server.videos.get({ id: uuid })
88 const video = res.body as VideoDetails
89 const files = video.files.concat(...video.streamingPlaylists.map(p => p.files)) 82 const files = video.files.concat(...video.streamingPlaylists.map(p => p.files))
90 83
91 for (const file of files) { 84 for (const file of files) {
@@ -109,134 +102,132 @@ describe('Test transcoding plugins', function () {
109 } 102 }
110 103
111 before(async function () { 104 before(async function () {
112 await installPlugin({ 105 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-transcoding-one') })
113 url: server.url,
114 accessToken: server.accessToken,
115 path: getPluginTestPath('-transcoding-one')
116 })
117 }) 106 })
118 107
119 it('Should have the appropriate available profiles', async function () { 108 it('Should have the appropriate available profiles', async function () {
120 const res = await getConfig(server.url) 109 const config = await server.config.getConfig()
121 const config = res.body as ServerConfig
122 110
123 expect(config.transcoding.availableProfiles).to.have.members([ 'default', 'low-vod', 'input-options-vod', 'bad-scale-vod' ]) 111 expect(config.transcoding.availableProfiles).to.have.members([ 'default', 'low-vod', 'input-options-vod', 'bad-scale-vod' ])
124 expect(config.live.transcoding.availableProfiles).to.have.members([ 'default', 'low-live', 'input-options-live', 'bad-scale-live' ]) 112 expect(config.live.transcoding.availableProfiles).to.have.members([ 'default', 'high-live', 'input-options-live', 'bad-scale-live' ])
125 }) 113 })
126 114
127 it('Should not use the plugin profile if not chosen by the admin', async function () { 115 describe('VOD', function () {
128 this.timeout(240000)
129 116
130 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 117 it('Should not use the plugin profile if not chosen by the admin', async function () {
131 await waitJobs([ server ]) 118 this.timeout(240000)
132 119
133 await checkVideoFPS(videoUUID, 'above', 20) 120 const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
134 }) 121 await waitJobs([ server ])
135 122
136 it('Should use the vod profile', async function () { 123 await checkVideoFPS(videoUUID, 'above', 20)
137 this.timeout(240000) 124 })
138 125
139 await updateConf(server, 'low-vod', 'default') 126 it('Should use the vod profile', async function () {
127 this.timeout(240000)
140 128
141 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 129 await updateConf(server, 'low-vod', 'default')
142 await waitJobs([ server ])
143 130
144 await checkVideoFPS(videoUUID, 'below', 12) 131 const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
145 }) 132 await waitJobs([ server ])
146 133
147 it('Should apply input options in vod profile', async function () { 134 await checkVideoFPS(videoUUID, 'below', 12)
148 this.timeout(240000) 135 })
149 136
150 await updateConf(server, 'input-options-vod', 'default') 137 it('Should apply input options in vod profile', async function () {
138 this.timeout(240000)
151 139
152 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 140 await updateConf(server, 'input-options-vod', 'default')
153 await waitJobs([ server ])
154 141
155 await checkVideoFPS(videoUUID, 'below', 6) 142 const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
156 }) 143 await waitJobs([ server ])
157 144
158 it('Should apply the scale filter in vod profile', async function () { 145 await checkVideoFPS(videoUUID, 'below', 6)
159 this.timeout(240000) 146 })
160 147
161 await updateConf(server, 'bad-scale-vod', 'default') 148 it('Should apply the scale filter in vod profile', async function () {
149 this.timeout(240000)
162 150
163 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 151 await updateConf(server, 'bad-scale-vod', 'default')
164 await waitJobs([ server ])
165 152
166 // Transcoding failed 153 const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
167 const res = await getVideo(server.url, videoUUID) 154 await waitJobs([ server ])
168 const video: VideoDetails = res.body
169 155
170 expect(video.files).to.have.lengthOf(1) 156 // Transcoding failed
171 expect(video.streamingPlaylists).to.have.lengthOf(0) 157 const video = await server.videos.get({ id: videoUUID })
158 expect(video.files).to.have.lengthOf(1)
159 expect(video.streamingPlaylists).to.have.lengthOf(0)
160 })
172 }) 161 })
173 162
174 it('Should not use the plugin profile if not chosen by the admin', async function () { 163 describe('Live', function () {
175 this.timeout(240000)
176 164
177 const liveVideoId = await createLiveWrapper(server) 165 it('Should not use the plugin profile if not chosen by the admin', async function () {
166 this.timeout(240000)
178 167
179 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 168 const liveVideoId = await createLiveWrapper(server)
180 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId)
181 await waitJobs([ server ])
182 169
183 await checkLiveFPS(liveVideoId, 'above', 20) 170 await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_very_short_240p.mp4' })
184 }) 171 await server.live.waitUntilPublished({ videoId: liveVideoId })
172 await waitJobs([ server ])
185 173
186 it('Should use the live profile', async function () { 174 await checkLiveFPS(liveVideoId, 'above', 20)
187 this.timeout(240000) 175 })
188 176
189 await updateConf(server, 'low-vod', 'low-live') 177 it('Should use the live profile', async function () {
178 this.timeout(240000)
190 179
191 const liveVideoId = await createLiveWrapper(server) 180 await updateConf(server, 'low-vod', 'high-live')
192 181
193 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 182 const liveVideoId = await createLiveWrapper(server)
194 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId)
195 await waitJobs([ server ])
196 183
197 await checkLiveFPS(liveVideoId, 'below', 12) 184 await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_very_short_240p.mp4' })
198 }) 185 await server.live.waitUntilPublished({ videoId: liveVideoId })
186 await waitJobs([ server ])
199 187
200 it('Should apply the input options on live profile', async function () { 188 await checkLiveFPS(liveVideoId, 'above', 45)
201 this.timeout(240000) 189 })
202 190
203 await updateConf(server, 'low-vod', 'input-options-live') 191 it('Should apply the input options on live profile', async function () {
192 this.timeout(240000)
204 193
205 const liveVideoId = await createLiveWrapper(server) 194 await updateConf(server, 'low-vod', 'input-options-live')
206 195
207 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 196 const liveVideoId = await createLiveWrapper(server)
208 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId)
209 await waitJobs([ server ])
210 197
211 await checkLiveFPS(liveVideoId, 'below', 6) 198 await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_very_short_240p.mp4' })
212 }) 199 await server.live.waitUntilPublished({ videoId: liveVideoId })
200 await waitJobs([ server ])
213 201
214 it('Should apply the scale filter name on live profile', async function () { 202 await checkLiveFPS(liveVideoId, 'above', 45)
215 this.timeout(240000) 203 })
216 204
217 await updateConf(server, 'low-vod', 'bad-scale-live') 205 it('Should apply the scale filter name on live profile', async function () {
206 this.timeout(240000)
218 207
219 const liveVideoId = await createLiveWrapper(server) 208 await updateConf(server, 'low-vod', 'bad-scale-live')
220 209
221 const command = await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 210 const liveVideoId = await createLiveWrapper(server)
222 await testFfmpegStreamError(command, true)
223 })
224 211
225 it('Should default to the default profile if the specified profile does not exist', async function () { 212 const command = await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_very_short_240p.mp4' })
226 this.timeout(240000) 213 await testFfmpegStreamError(command, true)
214 })
227 215
228 await uninstallPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-plugin-test-transcoding-one' }) 216 it('Should default to the default profile if the specified profile does not exist', async function () {
217 this.timeout(240000)
229 218
230 const res = await getConfig(server.url) 219 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-transcoding-one' })
231 const config = res.body as ServerConfig
232 220
233 expect(config.transcoding.availableProfiles).to.deep.equal([ 'default' ]) 221 const config = await server.config.getConfig()
234 expect(config.live.transcoding.availableProfiles).to.deep.equal([ 'default' ])
235 222
236 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 223 expect(config.transcoding.availableProfiles).to.deep.equal([ 'default' ])
237 await waitJobs([ server ]) 224 expect(config.live.transcoding.availableProfiles).to.deep.equal([ 'default' ])
225
226 const videoUUID = (await server.videos.quickUpload({ name: 'video', fixture: 'video_very_short_240p.mp4' })).uuid
227 await waitJobs([ server ])
238 228
239 await checkVideoFPS(videoUUID, 'above', 20) 229 await checkVideoFPS(videoUUID, 'above', 20)
230 })
240 }) 231 })
241 232
242 }) 233 })
@@ -244,11 +235,7 @@ describe('Test transcoding plugins', function () {
244 describe('When using a plugin adding new encoders', function () { 235 describe('When using a plugin adding new encoders', function () {
245 236
246 before(async function () { 237 before(async function () {
247 await installPlugin({ 238 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-transcoding-two') })
248 url: server.url,
249 accessToken: server.accessToken,
250 path: getPluginTestPath('-transcoding-two')
251 })
252 239
253 await updateConf(server, 'test-vod-profile', 'test-live-profile') 240 await updateConf(server, 'test-vod-profile', 'test-live-profile')
254 }) 241 })
@@ -256,10 +243,12 @@ describe('Test transcoding plugins', function () {
256 it('Should use the new vod encoders', async function () { 243 it('Should use the new vod encoders', async function () {
257 this.timeout(240000) 244 this.timeout(240000)
258 245
259 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video', fixture: 'video_short_240p.mp4' })).uuid 246 const videoUUID = (await server.videos.quickUpload({ name: 'video', fixture: 'video_very_short_240p.mp4' })).uuid
260 await waitJobs([ server ]) 247 await waitJobs([ server ])
261 248
262 const path = buildServerDirectory(server, join('videos', videoUUID + '-240.mp4')) 249 const video = await server.videos.get({ id: videoUUID })
250
251 const path = server.servers.buildWebTorrentFilePath(video.files[0].fileUrl)
263 const audioProbe = await getAudioStream(path) 252 const audioProbe = await getAudioStream(path)
264 expect(audioProbe.audioStream.codec_name).to.equal('opus') 253 expect(audioProbe.audioStream.codec_name).to.equal('opus')
265 254
@@ -272,8 +261,8 @@ describe('Test transcoding plugins', function () {
272 261
273 const liveVideoId = await createLiveWrapper(server) 262 const liveVideoId = await createLiveWrapper(server)
274 263
275 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 264 await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
276 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) 265 await server.live.waitUntilPublished({ videoId: liveVideoId })
277 await waitJobs([ server ]) 266 await waitJobs([ server ])
278 267
279 const playlistUrl = `${server.url}/static/streaming-playlists/hls/${liveVideoId}/0.m3u8` 268 const playlistUrl = `${server.url}/static/streaming-playlists/hls/${liveVideoId}/0.m3u8`
diff --git a/server/tests/plugins/plugin-unloading.ts b/server/tests/plugins/plugin-unloading.ts
index 74ca82e2f..6bf2fda9b 100644
--- a/server/tests/plugins/plugin-unloading.ts
+++ b/server/tests/plugins/plugin-unloading.ts
@@ -1,42 +1,36 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai'
4import { 5import {
5 cleanupTests, 6 cleanupTests,
6 flushAndRunServer, 7 createSingleServer,
7 getPluginTestPath,
8 makeGetRequest, 8 makeGetRequest,
9 installPlugin, 9 PeerTubeServer,
10 uninstallPlugin, 10 PluginsCommand,
11 ServerInfo,
12 setAccessTokensToServers 11 setAccessTokensToServers
13} from '../../../shared/extra-utils' 12} from '@shared/extra-utils'
14import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 13import { HttpStatusCode } from '@shared/models'
15import { expect } from 'chai'
16 14
17describe('Test plugins module unloading', function () { 15describe('Test plugins module unloading', function () {
18 let server: ServerInfo = null 16 let server: PeerTubeServer = null
19 const requestPath = '/plugins/test-unloading/router/get' 17 const requestPath = '/plugins/test-unloading/router/get'
20 let value: string = null 18 let value: string = null
21 19
22 before(async function () { 20 before(async function () {
23 this.timeout(30000) 21 this.timeout(30000)
24 22
25 server = await flushAndRunServer(1) 23 server = await createSingleServer(1)
26 await setAccessTokensToServers([ server ]) 24 await setAccessTokensToServers([ server ])
27 25
28 await installPlugin({ 26 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-unloading') })
29 url: server.url,
30 accessToken: server.accessToken,
31 path: getPluginTestPath('-unloading')
32 })
33 }) 27 })
34 28
35 it('Should return a numeric value', async function () { 29 it('Should return a numeric value', async function () {
36 const res = await makeGetRequest({ 30 const res = await makeGetRequest({
37 url: server.url, 31 url: server.url,
38 path: requestPath, 32 path: requestPath,
39 statusCodeExpected: HttpStatusCode.OK_200 33 expectedStatus: HttpStatusCode.OK_200
40 }) 34 })
41 35
42 expect(res.body.message).to.match(/^\d+$/) 36 expect(res.body.message).to.match(/^\d+$/)
@@ -47,36 +41,29 @@ describe('Test plugins module unloading', function () {
47 const res = await makeGetRequest({ 41 const res = await makeGetRequest({
48 url: server.url, 42 url: server.url,
49 path: requestPath, 43 path: requestPath,
50 statusCodeExpected: HttpStatusCode.OK_200 44 expectedStatus: HttpStatusCode.OK_200
51 }) 45 })
52 46
53 expect(res.body.message).to.be.equal(value) 47 expect(res.body.message).to.be.equal(value)
54 }) 48 })
55 49
56 it('Should uninstall the plugin and free the route', async function () { 50 it('Should uninstall the plugin and free the route', async function () {
57 await uninstallPlugin({ 51 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-unloading' })
58 url: server.url,
59 accessToken: server.accessToken,
60 npmName: 'peertube-plugin-test-unloading'
61 })
62 52
63 await makeGetRequest({ 53 await makeGetRequest({
64 url: server.url, 54 url: server.url,
65 path: requestPath, 55 path: requestPath,
66 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 56 expectedStatus: HttpStatusCode.NOT_FOUND_404
67 }) 57 })
68 }) 58 })
69 59
70 it('Should return a different numeric value', async function () { 60 it('Should return a different numeric value', async function () {
71 await installPlugin({ 61 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-unloading') })
72 url: server.url, 62
73 accessToken: server.accessToken,
74 path: getPluginTestPath('-unloading')
75 })
76 const res = await makeGetRequest({ 63 const res = await makeGetRequest({
77 url: server.url, 64 url: server.url,
78 path: requestPath, 65 path: requestPath,
79 statusCodeExpected: HttpStatusCode.OK_200 66 expectedStatus: HttpStatusCode.OK_200
80 }) 67 })
81 68
82 expect(res.body.message).to.match(/^\d+$/) 69 expect(res.body.message).to.match(/^\d+$/)
diff --git a/server/tests/plugins/translations.ts b/server/tests/plugins/translations.ts
index 9fd2ba1c5..8b25c6b75 100644
--- a/server/tests/plugins/translations.ts
+++ b/server/tests/plugins/translations.ts
@@ -1,50 +1,37 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers' 4import * as chai from 'chai'
6import { 5import { cleanupTests, createSingleServer, PeerTubeServer, PluginsCommand, setAccessTokensToServers } from '@shared/extra-utils'
7 getPluginTestPath,
8 getPluginTranslations,
9 installPlugin,
10 setAccessTokensToServers,
11 uninstallPlugin
12} from '../../../shared/extra-utils'
13 6
14const expect = chai.expect 7const expect = chai.expect
15 8
16describe('Test plugin translations', function () { 9describe('Test plugin translations', function () {
17 let server: ServerInfo 10 let server: PeerTubeServer
11 let command: PluginsCommand
18 12
19 before(async function () { 13 before(async function () {
20 this.timeout(30000) 14 this.timeout(30000)
21 15
22 server = await flushAndRunServer(1) 16 server = await createSingleServer(1)
23 await setAccessTokensToServers([ server ]) 17 await setAccessTokensToServers([ server ])
24 18
25 await installPlugin({ 19 command = server.plugins
26 url: server.url,
27 accessToken: server.accessToken,
28 path: getPluginTestPath()
29 })
30 20
31 await installPlugin({ 21 await command.install({ path: PluginsCommand.getPluginTestPath() })
32 url: server.url, 22 await command.install({ path: PluginsCommand.getPluginTestPath('-filter-translations') })
33 accessToken: server.accessToken,
34 path: getPluginTestPath('-filter-translations')
35 })
36 }) 23 })
37 24
38 it('Should not have translations for locale pt', async function () { 25 it('Should not have translations for locale pt', async function () {
39 const res = await getPluginTranslations({ url: server.url, locale: 'pt' }) 26 const body = await command.getTranslations({ locale: 'pt' })
40 27
41 expect(res.body).to.deep.equal({}) 28 expect(body).to.deep.equal({})
42 }) 29 })
43 30
44 it('Should have translations for locale fr', async function () { 31 it('Should have translations for locale fr', async function () {
45 const res = await getPluginTranslations({ url: server.url, locale: 'fr-FR' }) 32 const body = await command.getTranslations({ locale: 'fr-FR' })
46 33
47 expect(res.body).to.deep.equal({ 34 expect(body).to.deep.equal({
48 'peertube-plugin-test': { 35 'peertube-plugin-test': {
49 Hi: 'Coucou' 36 Hi: 'Coucou'
50 }, 37 },
@@ -55,9 +42,9 @@ describe('Test plugin translations', function () {
55 }) 42 })
56 43
57 it('Should have translations of locale it', async function () { 44 it('Should have translations of locale it', async function () {
58 const res = await getPluginTranslations({ url: server.url, locale: 'it-IT' }) 45 const body = await command.getTranslations({ locale: 'it-IT' })
59 46
60 expect(res.body).to.deep.equal({ 47 expect(body).to.deep.equal({
61 'peertube-plugin-test-filter-translations': { 48 'peertube-plugin-test-filter-translations': {
62 'Hello world': 'Ciao, mondo!' 49 'Hello world': 'Ciao, mondo!'
63 } 50 }
@@ -65,12 +52,12 @@ describe('Test plugin translations', function () {
65 }) 52 })
66 53
67 it('Should remove the plugin and remove the locales', async function () { 54 it('Should remove the plugin and remove the locales', async function () {
68 await uninstallPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-plugin-test-filter-translations' }) 55 await command.uninstall({ npmName: 'peertube-plugin-test-filter-translations' })
69 56
70 { 57 {
71 const res = await getPluginTranslations({ url: server.url, locale: 'fr-FR' }) 58 const body = await command.getTranslations({ locale: 'fr-FR' })
72 59
73 expect(res.body).to.deep.equal({ 60 expect(body).to.deep.equal({
74 'peertube-plugin-test': { 61 'peertube-plugin-test': {
75 Hi: 'Coucou' 62 Hi: 'Coucou'
76 } 63 }
@@ -78,9 +65,9 @@ describe('Test plugin translations', function () {
78 } 65 }
79 66
80 { 67 {
81 const res = await getPluginTranslations({ url: server.url, locale: 'it-IT' }) 68 const body = await command.getTranslations({ locale: 'it-IT' })
82 69
83 expect(res.body).to.deep.equal({}) 70 expect(body).to.deep.equal({})
84 } 71 }
85 }) 72 })
86 73
diff --git a/server/tests/plugins/video-constants.ts b/server/tests/plugins/video-constants.ts
index eb014c596..19cba6c2c 100644
--- a/server/tests/plugins/video-constants.ts
+++ b/server/tests/plugins/video-constants.ts
@@ -1,44 +1,33 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers' 4import * as chai from 'chai'
6import { 5import {
7 createVideoPlaylist, 6 cleanupTests,
8 getPluginTestPath, 7 createSingleServer,
9 getVideo, 8 makeGetRequest,
10 getVideoCategories, 9 PeerTubeServer,
11 getVideoLanguages, 10 PluginsCommand,
12 getVideoLicences, getVideoPlaylistPrivacies, getVideoPrivacies, 11 setAccessTokensToServers
13 installPlugin, 12} from '@shared/extra-utils'
14 setAccessTokensToServers, 13import { HttpStatusCode, VideoPlaylistPrivacy } from '@shared/models'
15 uninstallPlugin,
16 uploadVideo
17} from '../../../shared/extra-utils'
18import { VideoDetails, VideoPlaylistPrivacy } from '../../../shared/models/videos'
19import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
20 14
21const expect = chai.expect 15const expect = chai.expect
22 16
23describe('Test plugin altering video constants', function () { 17describe('Test plugin altering video constants', function () {
24 let server: ServerInfo 18 let server: PeerTubeServer
25 19
26 before(async function () { 20 before(async function () {
27 this.timeout(30000) 21 this.timeout(30000)
28 22
29 server = await flushAndRunServer(1) 23 server = await createSingleServer(1)
30 await setAccessTokensToServers([ server ]) 24 await setAccessTokensToServers([ server ])
31 25
32 await installPlugin({ 26 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-video-constants') })
33 url: server.url,
34 accessToken: server.accessToken,
35 path: getPluginTestPath('-video-constants')
36 })
37 }) 27 })
38 28
39 it('Should have updated languages', async function () { 29 it('Should have updated languages', async function () {
40 const res = await getVideoLanguages(server.url) 30 const languages = await server.videos.getLanguages()
41 const languages = res.body
42 31
43 expect(languages['en']).to.not.exist 32 expect(languages['en']).to.not.exist
44 expect(languages['fr']).to.not.exist 33 expect(languages['fr']).to.not.exist
@@ -49,8 +38,7 @@ describe('Test plugin altering video constants', function () {
49 }) 38 })
50 39
51 it('Should have updated categories', async function () { 40 it('Should have updated categories', async function () {
52 const res = await getVideoCategories(server.url) 41 const categories = await server.videos.getCategories()
53 const categories = res.body
54 42
55 expect(categories[1]).to.not.exist 43 expect(categories[1]).to.not.exist
56 expect(categories[2]).to.not.exist 44 expect(categories[2]).to.not.exist
@@ -60,8 +48,7 @@ describe('Test plugin altering video constants', function () {
60 }) 48 })
61 49
62 it('Should have updated licences', async function () { 50 it('Should have updated licences', async function () {
63 const res = await getVideoLicences(server.url) 51 const licences = await server.videos.getLicences()
64 const licences = res.body
65 52
66 expect(licences[1]).to.not.exist 53 expect(licences[1]).to.not.exist
67 expect(licences[7]).to.not.exist 54 expect(licences[7]).to.not.exist
@@ -71,8 +58,7 @@ describe('Test plugin altering video constants', function () {
71 }) 58 })
72 59
73 it('Should have updated video privacies', async function () { 60 it('Should have updated video privacies', async function () {
74 const res = await getVideoPrivacies(server.url) 61 const privacies = await server.videos.getPrivacies()
75 const privacies = res.body
76 62
77 expect(privacies[1]).to.exist 63 expect(privacies[1]).to.exist
78 expect(privacies[2]).to.not.exist 64 expect(privacies[2]).to.not.exist
@@ -81,8 +67,7 @@ describe('Test plugin altering video constants', function () {
81 }) 67 })
82 68
83 it('Should have updated playlist privacies', async function () { 69 it('Should have updated playlist privacies', async function () {
84 const res = await getVideoPlaylistPrivacies(server.url) 70 const playlistPrivacies = await server.playlists.getPrivacies()
85 const playlistPrivacies = res.body
86 71
87 expect(playlistPrivacies[1]).to.exist 72 expect(playlistPrivacies[1]).to.exist
88 expect(playlistPrivacies[2]).to.exist 73 expect(playlistPrivacies[2]).to.exist
@@ -90,38 +75,30 @@ describe('Test plugin altering video constants', function () {
90 }) 75 })
91 76
92 it('Should not be able to create a video with this privacy', async function () { 77 it('Should not be able to create a video with this privacy', async function () {
93 const attrs = { name: 'video', privacy: 2 } 78 const attributes = { name: 'video', privacy: 2 }
94 await uploadVideo(server.url, server.accessToken, attrs, HttpStatusCode.BAD_REQUEST_400) 79 await server.videos.upload({ attributes, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
95 }) 80 })
96 81
97 it('Should not be able to create a video with this privacy', async function () { 82 it('Should not be able to create a video with this privacy', async function () {
98 const attrs = { displayName: 'video playlist', privacy: VideoPlaylistPrivacy.PRIVATE } 83 const attributes = { displayName: 'video playlist', privacy: VideoPlaylistPrivacy.PRIVATE }
99 await createVideoPlaylist({ 84 await server.playlists.create({ attributes, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
100 url: server.url,
101 token: server.accessToken,
102 playlistAttrs: attrs,
103 expectedStatus: HttpStatusCode.BAD_REQUEST_400
104 })
105 }) 85 })
106 86
107 it('Should be able to upload a video with these values', async function () { 87 it('Should be able to upload a video with these values', async function () {
108 const attrs = { name: 'video', category: 42, licence: 42, language: 'al_bhed2' } 88 const attributes = { name: 'video', category: 42, licence: 42, language: 'al_bhed2' }
109 const resUpload = await uploadVideo(server.url, server.accessToken, attrs) 89 const { uuid } = await server.videos.upload({ attributes })
110 90
111 const res = await getVideo(server.url, resUpload.body.video.uuid) 91 const video = await server.videos.get({ id: uuid })
112
113 const video: VideoDetails = res.body
114 expect(video.language.label).to.equal('Al Bhed 2') 92 expect(video.language.label).to.equal('Al Bhed 2')
115 expect(video.licence.label).to.equal('Best licence') 93 expect(video.licence.label).to.equal('Best licence')
116 expect(video.category.label).to.equal('Best category') 94 expect(video.category.label).to.equal('Best category')
117 }) 95 })
118 96
119 it('Should uninstall the plugin and reset languages, categories, licences and privacies', async function () { 97 it('Should uninstall the plugin and reset languages, categories, licences and privacies', async function () {
120 await uninstallPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-plugin-test-video-constants' }) 98 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-video-constants' })
121 99
122 { 100 {
123 const res = await getVideoLanguages(server.url) 101 const languages = await server.videos.getLanguages()
124 const languages = res.body
125 102
126 expect(languages['en']).to.equal('English') 103 expect(languages['en']).to.equal('English')
127 expect(languages['fr']).to.equal('French') 104 expect(languages['fr']).to.equal('French')
@@ -132,8 +109,7 @@ describe('Test plugin altering video constants', function () {
132 } 109 }
133 110
134 { 111 {
135 const res = await getVideoCategories(server.url) 112 const categories = await server.videos.getCategories()
136 const categories = res.body
137 113
138 expect(categories[1]).to.equal('Music') 114 expect(categories[1]).to.equal('Music')
139 expect(categories[2]).to.equal('Films') 115 expect(categories[2]).to.equal('Films')
@@ -143,8 +119,7 @@ describe('Test plugin altering video constants', function () {
143 } 119 }
144 120
145 { 121 {
146 const res = await getVideoLicences(server.url) 122 const licences = await server.videos.getLicences()
147 const licences = res.body
148 123
149 expect(licences[1]).to.equal('Attribution') 124 expect(licences[1]).to.equal('Attribution')
150 expect(licences[7]).to.equal('Public Domain Dedication') 125 expect(licences[7]).to.equal('Public Domain Dedication')
@@ -154,8 +129,7 @@ describe('Test plugin altering video constants', function () {
154 } 129 }
155 130
156 { 131 {
157 const res = await getVideoPrivacies(server.url) 132 const privacies = await server.videos.getPrivacies()
158 const privacies = res.body
159 133
160 expect(privacies[1]).to.exist 134 expect(privacies[1]).to.exist
161 expect(privacies[2]).to.exist 135 expect(privacies[2]).to.exist
@@ -164,8 +138,7 @@ describe('Test plugin altering video constants', function () {
164 } 138 }
165 139
166 { 140 {
167 const res = await getVideoPlaylistPrivacies(server.url) 141 const playlistPrivacies = await server.playlists.getPrivacies()
168 const playlistPrivacies = res.body
169 142
170 expect(playlistPrivacies[1]).to.exist 143 expect(playlistPrivacies[1]).to.exist
171 expect(playlistPrivacies[2]).to.exist 144 expect(playlistPrivacies[2]).to.exist
@@ -173,6 +146,37 @@ describe('Test plugin altering video constants', function () {
173 } 146 }
174 }) 147 })
175 148
149 it('Should be able to reset categories', async function () {
150 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-video-constants') })
151
152 {
153 const categories = await server.videos.getCategories()
154
155 expect(categories[1]).to.not.exist
156 expect(categories[2]).to.not.exist
157
158 expect(categories[42]).to.exist
159 expect(categories[43]).to.exist
160 }
161
162 await makeGetRequest({
163 url: server.url,
164 token: server.accessToken,
165 path: '/plugins/test-video-constants/router/reset-categories',
166 expectedStatus: HttpStatusCode.NO_CONTENT_204
167 })
168
169 {
170 const categories = await server.videos.getCategories()
171
172 expect(categories[1]).to.exist
173 expect(categories[2]).to.exist
174
175 expect(categories[42]).to.not.exist
176 expect(categories[43]).to.not.exist
177 }
178 })
179
176 after(async function () { 180 after(async function () {
177 await cleanupTests([ server ]) 181 await cleanupTests([ server ])
178 }) 182 })
diff --git a/server/tools/cli.ts b/server/tools/cli.ts
index 7b94306cd..52e6ea593 100644
--- a/server/tools/cli.ts
+++ b/server/tools/cli.ts
@@ -1,14 +1,11 @@
1import { Command } from 'commander'
1import { Netrc } from 'netrc-parser' 2import { Netrc } from 'netrc-parser'
2import { getAppNumber, isTestInstance } from '../helpers/core-utils'
3import { join } from 'path' 3import { join } from 'path'
4import { root } from '../../shared/extra-utils/miscs/miscs'
5import { getVideoChannel } from '../../shared/extra-utils/videos/video-channels'
6import { VideoChannel, VideoPrivacy } from '../../shared/models/videos'
7import { createLogger, format, transports } from 'winston' 4import { createLogger, format, transports } from 'winston'
8import { getMyUserInformation } from '@shared/extra-utils/users/users' 5import { PeerTubeServer } from '@shared/extra-utils'
9import { User, UserRole } from '@shared/models' 6import { UserRole } from '@shared/models'
10import { getAccessToken } from '@shared/extra-utils/users/login' 7import { VideoPrivacy } from '../../shared/models/videos'
11import { Command } from 'commander' 8import { getAppNumber, isTestInstance, root } from '../helpers/core-utils'
12 9
13let configName = 'PeerTube/CLI' 10let configName = 'PeerTube/CLI'
14if (isTestInstance()) configName += `-${getAppNumber()}` 11if (isTestInstance()) configName += `-${getAppNumber()}`
@@ -17,17 +14,16 @@ const config = require('application-config')(configName)
17 14
18const version = require('../../../package.json').version 15const version = require('../../../package.json').version
19 16
20async function getAdminTokenOrDie (url: string, username: string, password: string) { 17async function getAdminTokenOrDie (server: PeerTubeServer, username: string, password: string) {
21 const accessToken = await getAccessToken(url, username, password) 18 const token = await server.login.getAccessToken(username, password)
22 const resMe = await getMyUserInformation(url, accessToken) 19 const me = await server.users.getMyInfo({ token })
23 const me: User = resMe.body
24 20
25 if (me.role !== UserRole.ADMINISTRATOR) { 21 if (me.role !== UserRole.ADMINISTRATOR) {
26 console.error('You must be an administrator.') 22 console.error('You must be an administrator.')
27 process.exit(-1) 23 process.exit(-1)
28 } 24 }
29 25
30 return accessToken 26 return token
31} 27}
32 28
33interface Settings { 29interface Settings {
@@ -128,7 +124,7 @@ function buildCommonVideoOptions (command: Command) {
128 .option('-v, --verbose <verbose>', 'Verbosity, from 0/\'error\' to 4/\'debug\'', 'info') 124 .option('-v, --verbose <verbose>', 'Verbosity, from 0/\'error\' to 4/\'debug\'', 'info')
129} 125}
130 126
131async function buildVideoAttributesFromCommander (url: string, command: Command, defaultAttributes: any = {}) { 127async function buildVideoAttributesFromCommander (server: PeerTubeServer, command: Command, defaultAttributes: any = {}) {
132 const options = command.opts() 128 const options = command.opts()
133 129
134 const defaultBooleanAttributes = { 130 const defaultBooleanAttributes = {
@@ -164,8 +160,7 @@ async function buildVideoAttributesFromCommander (url: string, command: Command,
164 Object.assign(videoAttributes, booleanAttributes) 160 Object.assign(videoAttributes, booleanAttributes)
165 161
166 if (options.channelName) { 162 if (options.channelName) {
167 const res = await getVideoChannel(url, options.channelName) 163 const videoChannel = await server.channels.get({ channelName: options.channelName })
168 const videoChannel: VideoChannel = res.body
169 164
170 Object.assign(videoAttributes, { channelId: videoChannel.id }) 165 Object.assign(videoAttributes, { channelId: videoChannel.id })
171 166
@@ -184,6 +179,19 @@ function getServerCredentials (program: Command) {
184 }) 179 })
185} 180}
186 181
182function buildServer (url: string) {
183 return new PeerTubeServer({ url })
184}
185
186async function assignToken (server: PeerTubeServer, username: string, password: string) {
187 const bodyClient = await server.login.getClient()
188 const client = { id: bodyClient.client_id, secret: bodyClient.client_secret }
189
190 const body = await server.login.login({ client, user: { username, password } })
191
192 server.accessToken = body.access_token
193}
194
187function getLogger (logLevel = 'info') { 195function getLogger (logLevel = 'info') {
188 const logLevels = { 196 const logLevels = {
189 0: 0, 197 0: 0,
@@ -230,5 +238,7 @@ export {
230 buildCommonVideoOptions, 238 buildCommonVideoOptions,
231 buildVideoAttributesFromCommander, 239 buildVideoAttributesFromCommander,
232 240
233 getAdminTokenOrDie 241 getAdminTokenOrDie,
242 buildServer,
243 assignToken
234} 244}
diff --git a/server/tools/peertube-auth.ts b/server/tools/peertube-auth.ts
index 1934e7986..b9f4ef4f8 100644
--- a/server/tools/peertube-auth.ts
+++ b/server/tools/peertube-auth.ts
@@ -5,9 +5,8 @@ registerTSPaths()
5 5
6import { OptionValues, program } from 'commander' 6import { OptionValues, program } from 'commander'
7import * as prompt from 'prompt' 7import * as prompt from 'prompt'
8import { getNetrc, getSettings, writeSettings } from './cli' 8import { assignToken, buildServer, getNetrc, getSettings, writeSettings } from './cli'
9import { isUserUsernameValid } from '../helpers/custom-validators/users' 9import { isUserUsernameValid } from '../helpers/custom-validators/users'
10import { getAccessToken } from '../../shared/extra-utils'
11import * as CliTable3 from 'cli-table3' 10import * as CliTable3 from 'cli-table3'
12 11
13async function delInstance (url: string) { 12async function delInstance (url: string) {
@@ -97,7 +96,8 @@ program
97 // @see https://github.com/Chocobozzz/PeerTube/issues/3520 96 // @see https://github.com/Chocobozzz/PeerTube/issues/3520
98 result.url = stripExtraneousFromPeerTubeUrl(result.url) 97 result.url = stripExtraneousFromPeerTubeUrl(result.url)
99 98
100 await getAccessToken(result.url, result.username, result.password) 99 const server = buildServer(result.url)
100 await assignToken(server, result.username, result.password)
101 } catch (err) { 101 } catch (err) {
102 console.error(err.message) 102 console.error(err.message)
103 process.exit(-1) 103 process.exit(-1)
diff --git a/server/tools/peertube-get-access-token.ts b/server/tools/peertube-get-access-token.ts
index 9488eba0e..a67de9180 100644
--- a/server/tools/peertube-get-access-token.ts
+++ b/server/tools/peertube-get-access-token.ts
@@ -2,7 +2,7 @@ import { registerTSPaths } from '../helpers/register-ts-paths'
2registerTSPaths() 2registerTSPaths()
3 3
4import { program } from 'commander' 4import { program } from 'commander'
5import { getClient, Server, serverLogin } from '../../shared/extra-utils' 5import { assignToken, buildServer } from './cli'
6 6
7program 7program
8 .option('-u, --url <url>', 'Server url') 8 .option('-u, --url <url>', 'Server url')
@@ -24,24 +24,11 @@ if (
24 process.exit(-1) 24 process.exit(-1)
25} 25}
26 26
27getClient(options.url) 27const server = buildServer(options.url)
28 .then(res => {
29 const server = {
30 url: options.url,
31 user: {
32 username: options.username,
33 password: options.password
34 },
35 client: {
36 id: res.body.client_id,
37 secret: res.body.client_secret
38 }
39 } as Server
40 28
41 return serverLogin(server) 29assignToken(server, options.username, options.password)
42 }) 30 .then(() => {
43 .then(accessToken => { 31 console.log(server.accessToken)
44 console.log(accessToken)
45 process.exit(0) 32 process.exit(0)
46 }) 33 })
47 .catch(err => { 34 .catch(err => {
diff --git a/server/tools/peertube-import-videos.ts b/server/tools/peertube-import-videos.ts
index 101a95b2a..52aae3d2c 100644
--- a/server/tools/peertube-import-videos.ts
+++ b/server/tools/peertube-import-videos.ts
@@ -8,17 +8,19 @@ import { truncate } from 'lodash'
8import { join } from 'path' 8import { join } from 'path'
9import * as prompt from 'prompt' 9import * as prompt from 'prompt'
10import { promisify } from 'util' 10import { promisify } from 'util'
11import { advancedVideosSearch, getClient, getVideoCategories, login, uploadVideo } from '../../shared/extra-utils/index' 11import { YoutubeDL } from '@server/helpers/youtube-dl'
12import { sha256 } from '../helpers/core-utils' 12import { sha256 } from '../helpers/core-utils'
13import { doRequestAndSaveToFile } from '../helpers/requests' 13import { doRequestAndSaveToFile } from '../helpers/requests'
14import { CONSTRAINTS_FIELDS } from '../initializers/constants' 14import { CONSTRAINTS_FIELDS } from '../initializers/constants'
15import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getLogger, getServerCredentials } from './cli' 15import {
16import { YoutubeDL } from '@server/helpers/youtube-dl' 16 assignToken,
17 17 buildCommonVideoOptions,
18type UserInfo = { 18 buildServer,
19 username: string 19 buildVideoAttributesFromCommander,
20 password: string 20 getLogger,
21} 21 getServerCredentials
22} from './cli'
23import { PeerTubeServer } from '@shared/extra-utils'
22 24
23const processOptions = { 25const processOptions = {
24 maxBuffer: Infinity 26 maxBuffer: Infinity
@@ -62,17 +64,13 @@ getServerCredentials(command)
62 url = normalizeTargetUrl(url) 64 url = normalizeTargetUrl(url)
63 options.targetUrl = normalizeTargetUrl(options.targetUrl) 65 options.targetUrl = normalizeTargetUrl(options.targetUrl)
64 66
65 const user = { username, password } 67 run(url, username, password)
66
67 run(url, user)
68 .catch(err => exitError(err)) 68 .catch(err => exitError(err))
69 }) 69 })
70 .catch(err => console.error(err)) 70 .catch(err => console.error(err))
71 71
72async function run (url: string, user: UserInfo) { 72async function run (url: string, username: string, password: string) {
73 if (!user.password) { 73 if (!password) password = await promptPassword()
74 user.password = await promptPassword()
75 }
76 74
77 const youtubeDLBinary = await YoutubeDL.safeGetYoutubeDL() 75 const youtubeDLBinary = await YoutubeDL.safeGetYoutubeDL()
78 76
@@ -111,7 +109,8 @@ async function run (url: string, user: UserInfo) {
111 await processVideo({ 109 await processVideo({
112 cwd: options.tmpdir, 110 cwd: options.tmpdir,
113 url, 111 url,
114 user, 112 username,
113 password,
115 youtubeInfo: info 114 youtubeInfo: info
116 }) 115 })
117 } catch (err) { 116 } catch (err) {
@@ -119,17 +118,18 @@ async function run (url: string, user: UserInfo) {
119 } 118 }
120 } 119 }
121 120
122 log.info('Video/s for user %s imported: %s', user.username, options.targetUrl) 121 log.info('Video/s for user %s imported: %s', username, options.targetUrl)
123 process.exit(0) 122 process.exit(0)
124} 123}
125 124
126async function processVideo (parameters: { 125async function processVideo (parameters: {
127 cwd: string 126 cwd: string
128 url: string 127 url: string
129 user: { username: string, password: string } 128 username: string
129 password: string
130 youtubeInfo: any 130 youtubeInfo: any
131}) { 131}) {
132 const { youtubeInfo, cwd, url, user } = parameters 132 const { youtubeInfo, cwd, url, username, password } = parameters
133 const youtubeDL = new YoutubeDL('', []) 133 const youtubeDL = new YoutubeDL('', [])
134 134
135 log.debug('Fetching object.', youtubeInfo) 135 log.debug('Fetching object.', youtubeInfo)
@@ -138,22 +138,29 @@ async function processVideo (parameters: {
138 log.debug('Fetched object.', videoInfo) 138 log.debug('Fetched object.', videoInfo)
139 139
140 const originallyPublishedAt = youtubeDL.buildOriginallyPublishedAt(videoInfo) 140 const originallyPublishedAt = youtubeDL.buildOriginallyPublishedAt(videoInfo)
141
141 if (options.since && originallyPublishedAt && originallyPublishedAt.getTime() < options.since.getTime()) { 142 if (options.since && originallyPublishedAt && originallyPublishedAt.getTime() < options.since.getTime()) {
142 log.info('Video "%s" has been published before "%s", don\'t upload it.\n', 143 log.info('Video "%s" has been published before "%s", don\'t upload it.\n', videoInfo.title, formatDate(options.since))
143 videoInfo.title, formatDate(options.since))
144 return 144 return
145 } 145 }
146
146 if (options.until && originallyPublishedAt && originallyPublishedAt.getTime() > options.until.getTime()) { 147 if (options.until && originallyPublishedAt && originallyPublishedAt.getTime() > options.until.getTime()) {
147 log.info('Video "%s" has been published after "%s", don\'t upload it.\n', 148 log.info('Video "%s" has been published after "%s", don\'t upload it.\n', videoInfo.title, formatDate(options.until))
148 videoInfo.title, formatDate(options.until))
149 return 149 return
150 } 150 }
151 151
152 const result = await advancedVideosSearch(url, { search: videoInfo.title, sort: '-match', searchTarget: 'local' }) 152 const server = buildServer(url)
153 const { data } = await server.search.advancedVideoSearch({
154 search: {
155 search: videoInfo.title,
156 sort: '-match',
157 searchTarget: 'local'
158 }
159 })
153 160
154 log.info('############################################################\n') 161 log.info('############################################################\n')
155 162
156 if (result.body.data.find(v => v.name === videoInfo.title)) { 163 if (data.find(v => v.name === videoInfo.title)) {
157 log.info('Video "%s" already exists, don\'t reupload it.\n', videoInfo.title) 164 log.info('Video "%s" already exists, don\'t reupload it.\n', videoInfo.title)
158 return 165 return
159 } 166 }
@@ -172,7 +179,8 @@ async function processVideo (parameters: {
172 youtubeDL, 179 youtubeDL,
173 cwd, 180 cwd,
174 url, 181 url,
175 user, 182 username,
183 password,
176 videoInfo: normalizeObject(videoInfo), 184 videoInfo: normalizeObject(videoInfo),
177 videoPath: path 185 videoPath: path
178 }) 186 })
@@ -187,11 +195,15 @@ async function uploadVideoOnPeerTube (parameters: {
187 videoPath: string 195 videoPath: string
188 cwd: string 196 cwd: string
189 url: string 197 url: string
190 user: { username: string, password: string } 198 username: string
199 password: string
191}) { 200}) {
192 const { youtubeDL, videoInfo, videoPath, cwd, url, user } = parameters 201 const { youtubeDL, videoInfo, videoPath, cwd, url, username, password } = parameters
193 202
194 const category = await getCategory(videoInfo.categories, url) 203 const server = buildServer(url)
204 await assignToken(server, username, password)
205
206 const category = await getCategory(server, videoInfo.categories)
195 const licence = getLicence(videoInfo.license) 207 const licence = getLicence(videoInfo.license)
196 let tags = [] 208 let tags = []
197 if (Array.isArray(videoInfo.tags)) { 209 if (Array.isArray(videoInfo.tags)) {
@@ -223,28 +235,28 @@ async function uploadVideoOnPeerTube (parameters: {
223 tags 235 tags
224 } 236 }
225 237
226 const videoAttributes = await buildVideoAttributesFromCommander(url, program, defaultAttributes) 238 const baseAttributes = await buildVideoAttributesFromCommander(server, program, defaultAttributes)
239
240 const attributes = {
241 ...baseAttributes,
227 242
228 Object.assign(videoAttributes, {
229 originallyPublishedAt: originallyPublishedAt ? originallyPublishedAt.toISOString() : null, 243 originallyPublishedAt: originallyPublishedAt ? originallyPublishedAt.toISOString() : null,
230 thumbnailfile, 244 thumbnailfile,
231 previewfile: thumbnailfile, 245 previewfile: thumbnailfile,
232 fixture: videoPath 246 fixture: videoPath
233 }) 247 }
234
235 log.info('\nUploading on PeerTube video "%s".', videoAttributes.name)
236 248
237 let accessToken = await getAccessTokenOrDie(url, user) 249 log.info('\nUploading on PeerTube video "%s".', attributes.name)
238 250
239 try { 251 try {
240 await uploadVideo(url, accessToken, videoAttributes) 252 await server.videos.upload({ attributes })
241 } catch (err) { 253 } catch (err) {
242 if (err.message.indexOf('401') !== -1) { 254 if (err.message.indexOf('401') !== -1) {
243 log.info('Got 401 Unauthorized, token may have expired, renewing token and retry.') 255 log.info('Got 401 Unauthorized, token may have expired, renewing token and retry.')
244 256
245 accessToken = await getAccessTokenOrDie(url, user) 257 server.accessToken = await server.login.getAccessToken(username, password)
246 258
247 await uploadVideo(url, accessToken, videoAttributes) 259 await server.videos.upload({ attributes })
248 } else { 260 } else {
249 exitError(err.message) 261 exitError(err.message)
250 } 262 }
@@ -253,20 +265,19 @@ async function uploadVideoOnPeerTube (parameters: {
253 await remove(videoPath) 265 await remove(videoPath)
254 if (thumbnailfile) await remove(thumbnailfile) 266 if (thumbnailfile) await remove(thumbnailfile)
255 267
256 log.warn('Uploaded video "%s"!\n', videoAttributes.name) 268 log.warn('Uploaded video "%s"!\n', attributes.name)
257} 269}
258 270
259/* ---------------------------------------------------------- */ 271/* ---------------------------------------------------------- */
260 272
261async function getCategory (categories: string[], url: string) { 273async function getCategory (server: PeerTubeServer, categories: string[]) {
262 if (!categories) return undefined 274 if (!categories) return undefined
263 275
264 const categoryString = categories[0] 276 const categoryString = categories[0]
265 277
266 if (categoryString === 'News & Politics') return 11 278 if (categoryString === 'News & Politics') return 11
267 279
268 const res = await getVideoCategories(url) 280 const categoriesServer = await server.videos.getCategories()
269 const categoriesServer = res.body
270 281
271 for (const key of Object.keys(categoriesServer)) { 282 for (const key of Object.keys(categoriesServer)) {
272 const categoryServer = categoriesServer[key] 283 const categoryServer = categoriesServer[key]
@@ -362,21 +373,6 @@ async function promptPassword () {
362 }) 373 })
363} 374}
364 375
365async function getAccessTokenOrDie (url: string, user: UserInfo) {
366 const resClient = await getClient(url)
367 const client = {
368 id: resClient.body.client_id,
369 secret: resClient.body.client_secret
370 }
371
372 try {
373 const res = await login(url, client, user)
374 return res.body.access_token
375 } catch (err) {
376 exitError('Cannot authenticate. Please check your username/password.')
377 }
378}
379
380function parseDate (dateAsStr: string): Date { 376function parseDate (dateAsStr: string): Date {
381 if (!/\d{4}-\d{2}-\d{2}/.test(dateAsStr)) { 377 if (!/\d{4}-\d{2}-\d{2}/.test(dateAsStr)) {
382 exitError(`Invalid date passed: ${dateAsStr}. Expected format: YYYY-MM-DD. See help for usage.`) 378 exitError(`Invalid date passed: ${dateAsStr}. Expected format: YYYY-MM-DD. See help for usage.`)
diff --git a/server/tools/peertube-plugins.ts b/server/tools/peertube-plugins.ts
index 54ea1264d..d9c285115 100644
--- a/server/tools/peertube-plugins.ts
+++ b/server/tools/peertube-plugins.ts
@@ -4,9 +4,8 @@ import { registerTSPaths } from '../helpers/register-ts-paths'
4registerTSPaths() 4registerTSPaths()
5 5
6import { program, Command, OptionValues } from 'commander' 6import { program, Command, OptionValues } from 'commander'
7import { installPlugin, listPlugins, uninstallPlugin, updatePlugin } from '../../shared/extra-utils/server/plugins' 7import { assignToken, buildServer, getServerCredentials } from './cli'
8import { getAdminTokenOrDie, getServerCredentials } from './cli' 8import { PluginType } from '../../shared/models'
9import { PeerTubePlugin, PluginType } from '../../shared/models'
10import { isAbsolute } from 'path' 9import { isAbsolute } from 'path'
11import * as CliTable3 from 'cli-table3' 10import * as CliTable3 from 'cli-table3'
12 11
@@ -63,28 +62,21 @@ program.parse(process.argv)
63 62
64async function pluginsListCLI (command: Command, options: OptionValues) { 63async function pluginsListCLI (command: Command, options: OptionValues) {
65 const { url, username, password } = await getServerCredentials(command) 64 const { url, username, password } = await getServerCredentials(command)
66 const accessToken = await getAdminTokenOrDie(url, username, password) 65 const server = buildServer(url)
66 await assignToken(server, username, password)
67 67
68 let pluginType: PluginType 68 let pluginType: PluginType
69 if (options.onlyThemes) pluginType = PluginType.THEME 69 if (options.onlyThemes) pluginType = PluginType.THEME
70 if (options.onlyPlugins) pluginType = PluginType.PLUGIN 70 if (options.onlyPlugins) pluginType = PluginType.PLUGIN
71 71
72 const res = await listPlugins({ 72 const { data } = await server.plugins.list({ start: 0, count: 100, sort: 'name', pluginType })
73 url,
74 accessToken,
75 start: 0,
76 count: 100,
77 sort: 'name',
78 pluginType
79 })
80 const plugins: PeerTubePlugin[] = res.body.data
81 73
82 const table = new CliTable3({ 74 const table = new CliTable3({
83 head: [ 'name', 'version', 'homepage' ], 75 head: [ 'name', 'version', 'homepage' ],
84 colWidths: [ 50, 10, 50 ] 76 colWidths: [ 50, 10, 50 ]
85 }) as any 77 }) as any
86 78
87 for (const plugin of plugins) { 79 for (const plugin of data) {
88 const npmName = plugin.type === PluginType.PLUGIN 80 const npmName = plugin.type === PluginType.PLUGIN
89 ? 'peertube-plugin-' + plugin.name 81 ? 'peertube-plugin-' + plugin.name
90 : 'peertube-theme-' + plugin.name 82 : 'peertube-theme-' + plugin.name
@@ -113,15 +105,11 @@ async function installPluginCLI (command: Command, options: OptionValues) {
113 } 105 }
114 106
115 const { url, username, password } = await getServerCredentials(command) 107 const { url, username, password } = await getServerCredentials(command)
116 const accessToken = await getAdminTokenOrDie(url, username, password) 108 const server = buildServer(url)
109 await assignToken(server, username, password)
117 110
118 try { 111 try {
119 await installPlugin({ 112 await server.plugins.install({ npmName: options.npmName, path: options.path })
120 url,
121 accessToken,
122 npmName: options.npmName,
123 path: options.path
124 })
125 } catch (err) { 113 } catch (err) {
126 console.error('Cannot install plugin.', err) 114 console.error('Cannot install plugin.', err)
127 process.exit(-1) 115 process.exit(-1)
@@ -144,15 +132,11 @@ async function updatePluginCLI (command: Command, options: OptionValues) {
144 } 132 }
145 133
146 const { url, username, password } = await getServerCredentials(command) 134 const { url, username, password } = await getServerCredentials(command)
147 const accessToken = await getAdminTokenOrDie(url, username, password) 135 const server = buildServer(url)
136 await assignToken(server, username, password)
148 137
149 try { 138 try {
150 await updatePlugin({ 139 await server.plugins.update({ npmName: options.npmName, path: options.path })
151 url,
152 accessToken,
153 npmName: options.npmName,
154 path: options.path
155 })
156 } catch (err) { 140 } catch (err) {
157 console.error('Cannot update plugin.', err) 141 console.error('Cannot update plugin.', err)
158 process.exit(-1) 142 process.exit(-1)
@@ -170,14 +154,11 @@ async function uninstallPluginCLI (command: Command, options: OptionValues) {
170 } 154 }
171 155
172 const { url, username, password } = await getServerCredentials(command) 156 const { url, username, password } = await getServerCredentials(command)
173 const accessToken = await getAdminTokenOrDie(url, username, password) 157 const server = buildServer(url)
158 await assignToken(server, username, password)
174 159
175 try { 160 try {
176 await uninstallPlugin({ 161 await server.plugins.uninstall({ npmName: options.npmName })
177 url,
178 accessToken,
179 npmName: options.npmName
180 })
181 } catch (err) { 162 } catch (err) {
182 console.error('Cannot uninstall plugin.', err) 163 console.error('Cannot uninstall plugin.', err)
183 process.exit(-1) 164 process.exit(-1)
diff --git a/server/tools/peertube-redundancy.ts b/server/tools/peertube-redundancy.ts
index 4810deee0..73b026ac8 100644
--- a/server/tools/peertube-redundancy.ts
+++ b/server/tools/peertube-redundancy.ts
@@ -1,17 +1,13 @@
1// eslint-disable @typescript-eslint/no-unnecessary-type-assertion
2
3import { registerTSPaths } from '../helpers/register-ts-paths' 1import { registerTSPaths } from '../helpers/register-ts-paths'
4registerTSPaths() 2registerTSPaths()
5 3
6import { program, Command } from 'commander'
7import { getAdminTokenOrDie, getServerCredentials } from './cli'
8import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models'
9import { addVideoRedundancy, listVideoRedundancies, removeVideoRedundancy } from '@shared/extra-utils/server/redundancy'
10import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
11import validator from 'validator'
12import * as CliTable3 from 'cli-table3' 4import * as CliTable3 from 'cli-table3'
13import { URL } from 'url' 5import { Command, program } from 'commander'
14import { uniq } from 'lodash' 6import { uniq } from 'lodash'
7import { URL } from 'url'
8import validator from 'validator'
9import { HttpStatusCode, VideoRedundanciesTarget } from '@shared/models'
10import { assignToken, buildServer, getServerCredentials } from './cli'
15 11
16import bytes = require('bytes') 12import bytes = require('bytes')
17 13
@@ -63,15 +59,16 @@ program.parse(process.argv)
63 59
64async function listRedundanciesCLI (target: VideoRedundanciesTarget) { 60async function listRedundanciesCLI (target: VideoRedundanciesTarget) {
65 const { url, username, password } = await getServerCredentials(program) 61 const { url, username, password } = await getServerCredentials(program)
66 const accessToken = await getAdminTokenOrDie(url, username, password) 62 const server = buildServer(url)
63 await assignToken(server, username, password)
67 64
68 const redundancies = await listVideoRedundanciesData(url, accessToken, target) 65 const { data } = await server.redundancy.listVideos({ start: 0, count: 100, sort: 'name', target })
69 66
70 const table = new CliTable3({ 67 const table = new CliTable3({
71 head: [ 'video id', 'video name', 'video url', 'files', 'playlists', 'by instances', 'total size' ] 68 head: [ 'video id', 'video name', 'video url', 'files', 'playlists', 'by instances', 'total size' ]
72 }) as any 69 }) as any
73 70
74 for (const redundancy of redundancies) { 71 for (const redundancy of data) {
75 const webtorrentFiles = redundancy.redundancies.files 72 const webtorrentFiles = redundancy.redundancies.files
76 const streamingPlaylists = redundancy.redundancies.streamingPlaylists 73 const streamingPlaylists = redundancy.redundancies.streamingPlaylists
77 74
@@ -106,7 +103,8 @@ async function listRedundanciesCLI (target: VideoRedundanciesTarget) {
106 103
107async function addRedundancyCLI (options: { video: number }, command: Command) { 104async function addRedundancyCLI (options: { video: number }, command: Command) {
108 const { url, username, password } = await getServerCredentials(command) 105 const { url, username, password } = await getServerCredentials(command)
109 const accessToken = await getAdminTokenOrDie(url, username, password) 106 const server = buildServer(url)
107 await assignToken(server, username, password)
110 108
111 if (!options.video || validator.isInt('' + options.video) === false) { 109 if (!options.video || validator.isInt('' + options.video) === false) {
112 console.error('You need to specify the video id to duplicate and it should be a number.\n') 110 console.error('You need to specify the video id to duplicate and it should be a number.\n')
@@ -115,11 +113,7 @@ async function addRedundancyCLI (options: { video: number }, command: Command) {
115 } 113 }
116 114
117 try { 115 try {
118 await addVideoRedundancy({ 116 await server.redundancy.addVideo({ videoId: options.video })
119 url,
120 accessToken,
121 videoId: options.video
122 })
123 117
124 console.log('Video will be duplicated by your instance!') 118 console.log('Video will be duplicated by your instance!')
125 119
@@ -139,7 +133,8 @@ async function addRedundancyCLI (options: { video: number }, command: Command) {
139 133
140async function removeRedundancyCLI (options: { video: number }, command: Command) { 134async function removeRedundancyCLI (options: { video: number }, command: Command) {
141 const { url, username, password } = await getServerCredentials(command) 135 const { url, username, password } = await getServerCredentials(command)
142 const accessToken = await getAdminTokenOrDie(url, username, password) 136 const server = buildServer(url)
137 await assignToken(server, username, password)
143 138
144 if (!options.video || validator.isInt('' + options.video) === false) { 139 if (!options.video || validator.isInt('' + options.video) === false) {
145 console.error('You need to specify the video id to remove from your redundancies.\n') 140 console.error('You need to specify the video id to remove from your redundancies.\n')
@@ -149,12 +144,12 @@ async function removeRedundancyCLI (options: { video: number }, command: Command
149 144
150 const videoId = parseInt(options.video + '', 10) 145 const videoId = parseInt(options.video + '', 10)
151 146
152 let redundancies = await listVideoRedundanciesData(url, accessToken, 'my-videos') 147 const myVideoRedundancies = await server.redundancy.listVideos({ target: 'my-videos' })
153 let videoRedundancy = redundancies.find(r => videoId === r.id) 148 let videoRedundancy = myVideoRedundancies.data.find(r => videoId === r.id)
154 149
155 if (!videoRedundancy) { 150 if (!videoRedundancy) {
156 redundancies = await listVideoRedundanciesData(url, accessToken, 'remote-videos') 151 const remoteVideoRedundancies = await server.redundancy.listVideos({ target: 'remote-videos' })
157 videoRedundancy = redundancies.find(r => videoId === r.id) 152 videoRedundancy = remoteVideoRedundancies.data.find(r => videoId === r.id)
158 } 153 }
159 154
160 if (!videoRedundancy) { 155 if (!videoRedundancy) {
@@ -168,11 +163,7 @@ async function removeRedundancyCLI (options: { video: number }, command: Command
168 .map(r => r.id) 163 .map(r => r.id)
169 164
170 for (const id of ids) { 165 for (const id of ids) {
171 await removeVideoRedundancy({ 166 await server.redundancy.removeVideo({ redundancyId: id })
172 url,
173 accessToken,
174 redundancyId: id
175 })
176 } 167 }
177 168
178 console.log('Video redundancy removed!') 169 console.log('Video redundancy removed!')
@@ -183,16 +174,3 @@ async function removeRedundancyCLI (options: { video: number }, command: Command
183 process.exit(-1) 174 process.exit(-1)
184 } 175 }
185} 176}
186
187async function listVideoRedundanciesData (url: string, accessToken: string, target: VideoRedundanciesTarget) {
188 const res = await listVideoRedundancies({
189 url,
190 accessToken,
191 start: 0,
192 count: 100,
193 sort: 'name',
194 target
195 })
196
197 return res.body.data as VideoRedundancy[]
198}
diff --git a/server/tools/peertube-upload.ts b/server/tools/peertube-upload.ts
index 02edbd809..01fb1fe8d 100644
--- a/server/tools/peertube-upload.ts
+++ b/server/tools/peertube-upload.ts
@@ -4,9 +4,7 @@ registerTSPaths()
4import { program } from 'commander' 4import { program } from 'commander'
5import { access, constants } from 'fs-extra' 5import { access, constants } from 'fs-extra'
6import { isAbsolute } from 'path' 6import { isAbsolute } from 'path'
7import { getAccessToken } from '../../shared/extra-utils' 7import { assignToken, buildCommonVideoOptions, buildServer, buildVideoAttributesFromCommander, getServerCredentials } from './cli'
8import { uploadVideo } from '../../shared/extra-utils/'
9import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getServerCredentials } from './cli'
10 8
11let command = program 9let command = program
12 .name('upload') 10 .name('upload')
@@ -46,22 +44,25 @@ getServerCredentials(command)
46 .catch(err => console.error(err)) 44 .catch(err => console.error(err))
47 45
48async function run (url: string, username: string, password: string) { 46async function run (url: string, username: string, password: string) {
49 const accessToken = await getAccessToken(url, username, password) 47 const server = buildServer(url)
48 await assignToken(server, username, password)
50 49
51 await access(options.file, constants.F_OK) 50 await access(options.file, constants.F_OK)
52 51
53 console.log('Uploading %s video...', options.videoName) 52 console.log('Uploading %s video...', options.videoName)
54 53
55 const videoAttributes = await buildVideoAttributesFromCommander(url, program) 54 const baseAttributes = await buildVideoAttributesFromCommander(server, program)
55
56 const attributes = {
57 ...baseAttributes,
56 58
57 Object.assign(videoAttributes, {
58 fixture: options.file, 59 fixture: options.file,
59 thumbnailfile: options.thumbnail, 60 thumbnailfile: options.thumbnail,
60 previewfile: options.preview 61 previewfile: options.preview
61 }) 62 }
62 63
63 try { 64 try {
64 await uploadVideo(url, accessToken, videoAttributes) 65 await server.videos.upload({ attributes })
65 console.log(`Video ${options.videoName} uploaded.`) 66 console.log(`Video ${options.videoName} uploaded.`)
66 process.exit(0) 67 process.exit(0)
67 } catch (err) { 68 } catch (err) {
diff --git a/server/tools/test.ts b/server/tools/test-live.ts
index fbdbae0b0..50dc04438 100644
--- a/server/tools/test.ts
+++ b/server/tools/test-live.ts
@@ -1,26 +1,23 @@
1import { registerTSPaths } from '../helpers/register-ts-paths'
2registerTSPaths()
3
4import { LiveVideo, LiveVideoCreate, VideoPrivacy } from '@shared/models'
5import { program } from 'commander' 1import { program } from 'commander'
2import { LiveVideoCreate, VideoPrivacy } from '@shared/models'
6import { 3import {
7 createLive, 4 createSingleServer,
8 flushAndRunServer,
9 getLive,
10 killallServers, 5 killallServers,
11 sendRTMPStream, 6 sendRTMPStream,
12 ServerInfo, 7 PeerTubeServer,
13 setAccessTokensToServers, 8 setAccessTokensToServers,
14 setDefaultVideoChannel, 9 setDefaultVideoChannel
15 updateCustomSubConfig
16} from '../../shared/extra-utils' 10} from '../../shared/extra-utils'
11import { registerTSPaths } from '../helpers/register-ts-paths'
12
13registerTSPaths()
17 14
18type CommandType = 'live-mux' | 'live-transcoding' 15type CommandType = 'live-mux' | 'live-transcoding'
19 16
20registerTSPaths() 17registerTSPaths()
21 18
22const command = program 19const command = program
23 .name('test') 20 .name('test-live')
24 .option('-t, --type <type>', 'live-muxing|live-transcoding') 21 .option('-t, --type <type>', 'live-muxing|live-transcoding')
25 .parse(process.argv) 22 .parse(process.argv)
26 23
@@ -39,11 +36,11 @@ async function run () {
39 36
40 console.log('Starting server.') 37 console.log('Starting server.')
41 38
42 const server = await flushAndRunServer(1, {}, [], { hideLogs: false, execArgv: [ '--inspect' ] }) 39 const server = await createSingleServer(1, {}, { hideLogs: false, nodeArgs: [ '--inspect' ] })
43 40
44 const cleanup = () => { 41 const cleanup = async () => {
45 console.log('Killing server') 42 console.log('Killing server')
46 killallServers([ server ]) 43 await killallServers([ server ])
47 } 44 }
48 45
49 process.on('exit', cleanup) 46 process.on('exit', cleanup)
@@ -57,17 +54,15 @@ async function run () {
57 const attributes: LiveVideoCreate = { 54 const attributes: LiveVideoCreate = {
58 name: 'live', 55 name: 'live',
59 saveReplay: true, 56 saveReplay: true,
60 channelId: server.videoChannel.id, 57 channelId: server.store.channel.id,
61 privacy: VideoPrivacy.PUBLIC 58 privacy: VideoPrivacy.PUBLIC
62 } 59 }
63 60
64 console.log('Creating live.') 61 console.log('Creating live.')
65 62
66 const res = await createLive(server.url, server.accessToken, attributes) 63 const { uuid: liveVideoUUID } = await server.live.create({ fields: attributes })
67 const liveVideoUUID = res.body.video.uuid
68 64
69 const resLive = await getLive(server.url, server.accessToken, liveVideoUUID) 65 const live = await server.live.get({ videoId: liveVideoUUID })
70 const live: LiveVideo = resLive.body
71 66
72 console.log('Sending RTMP stream.') 67 console.log('Sending RTMP stream.')
73 68
@@ -86,19 +81,21 @@ async function run () {
86 81
87// ---------------------------------------------------------------------------- 82// ----------------------------------------------------------------------------
88 83
89async function buildConfig (server: ServerInfo, commandType: CommandType) { 84async function buildConfig (server: PeerTubeServer, commandType: CommandType) {
90 await updateCustomSubConfig(server.url, server.accessToken, { 85 await server.config.updateCustomSubConfig({
91 instance: { 86 newConfig: {
92 customizations: { 87 instance: {
93 javascript: '', 88 customizations: {
94 css: '' 89 javascript: '',
95 } 90 css: ''
96 }, 91 }
97 live: { 92 },
98 enabled: true, 93 live: {
99 allowReplay: true, 94 enabled: true,
100 transcoding: { 95 allowReplay: true,
101 enabled: commandType === 'live-transcoding' 96 transcoding: {
97 enabled: commandType === 'live-transcoding'
98 }
102 } 99 }
103 } 100 }
104 }) 101 })
diff --git a/server/types/models/video/video-streaming-playlist.ts b/server/types/models/video/video-streaming-playlist.ts
index 8b3ef51fc..1e4dccb8e 100644
--- a/server/types/models/video/video-streaming-playlist.ts
+++ b/server/types/models/video/video-streaming-playlist.ts
@@ -39,5 +39,5 @@ export type MStreamingPlaylistRedundanciesOpt =
39 PickWithOpt<VideoStreamingPlaylistModel, 'RedundancyVideos', MVideoRedundancyFileUrl[]> 39 PickWithOpt<VideoStreamingPlaylistModel, 'RedundancyVideos', MVideoRedundancyFileUrl[]>
40 40
41export function isStreamingPlaylist (value: MVideo | MStreamingPlaylistVideo): value is MStreamingPlaylistVideo { 41export function isStreamingPlaylist (value: MVideo | MStreamingPlaylistVideo): value is MStreamingPlaylistVideo {
42 return !!(value as MStreamingPlaylist).playlistUrl 42 return !!(value as MStreamingPlaylist).videoId
43} 43}
diff --git a/server/typings/express/index.d.ts b/server/typings/express/index.d.ts
index 1a8dc3430..1a99b598a 100644
--- a/server/typings/express/index.d.ts
+++ b/server/typings/express/index.d.ts
@@ -1,4 +1,5 @@
1 1
2import { OutgoingHttpHeaders } from 'http'
2import { RegisterServerAuthExternalOptions } from '@server/types' 3import { RegisterServerAuthExternalOptions } from '@server/types'
3import { 4import {
4 MAbuseMessage, 5 MAbuseMessage,
@@ -22,8 +23,7 @@ import { MPlugin, MServer, MServerBlocklist } from '@server/types/models/server'
22import { MVideoImportDefault } from '@server/types/models/video/video-import' 23import { MVideoImportDefault } from '@server/types/models/video/video-import'
23import { MVideoPlaylistElement, MVideoPlaylistElementVideoUrlPlaylistPrivacy } from '@server/types/models/video/video-playlist-element' 24import { MVideoPlaylistElement, MVideoPlaylistElementVideoUrlPlaylistPrivacy } from '@server/types/models/video/video-playlist-element'
24import { MAccountVideoRateAccountVideo } from '@server/types/models/video/video-rate' 25import { MAccountVideoRateAccountVideo } from '@server/types/models/video/video-rate'
25import { HttpMethod } from '@shared/core-utils/miscs/http-methods' 26import { HttpMethod, PeerTubeProblemDocumentData, ServerErrorCode, VideoCreate } from '@shared/models'
26import { PeerTubeProblemDocumentData, ServerErrorCode, VideoCreate } from '@shared/models'
27import { File as UploadXFile, Metadata } from '@uploadx/core' 27import { File as UploadXFile, Metadata } from '@uploadx/core'
28import { RegisteredPlugin } from '../../lib/plugins/plugin-manager' 28import { RegisteredPlugin } from '../../lib/plugins/plugin-manager'
29import { 29import {
@@ -41,6 +41,7 @@ import {
41 MVideoShareActor, 41 MVideoShareActor,
42 MVideoThumbnail 42 MVideoThumbnail
43} from '../../types/models' 43} from '../../types/models'
44import { Writable } from 'stream'
44 45
45declare module 'express' { 46declare module 'express' {
46 export interface Request { 47 export interface Request {
@@ -99,6 +100,15 @@ declare module 'express' {
99 }) => void 100 }) => void
100 101
101 locals: { 102 locals: {
103 apicache: {
104 content: string | Buffer
105 write: Writable['write']
106 writeHead: Response['writeHead']
107 end: Response['end']
108 cacheable: boolean
109 headers: OutgoingHttpHeaders
110 }
111
102 docUrl?: string 112 docUrl?: string
103 113
104 videoAPI?: MVideoFormattableDetails 114 videoAPI?: MVideoFormattableDetails
diff --git a/shared/core-utils/miscs/date.ts b/shared/core-utils/common/date.ts
index 4f92f758f..3e4a3c08c 100644
--- a/shared/core-utils/miscs/date.ts
+++ b/shared/core-utils/common/date.ts
@@ -43,6 +43,49 @@ function isLastWeek (d: Date) {
43 return getDaysDifferences(now, d) <= 7 43 return getDaysDifferences(now, d) <= 7
44} 44}
45 45
46function timeToInt (time: number | string) {
47 if (!time) return 0
48 if (typeof time === 'number') return time
49
50 const reg = /^((\d+)[h:])?((\d+)[m:])?((\d+)s?)?$/
51 const matches = time.match(reg)
52
53 if (!matches) return 0
54
55 const hours = parseInt(matches[2] || '0', 10)
56 const minutes = parseInt(matches[4] || '0', 10)
57 const seconds = parseInt(matches[6] || '0', 10)
58
59 return hours * 3600 + minutes * 60 + seconds
60}
61
62function secondsToTime (seconds: number, full = false, symbol?: string) {
63 let time = ''
64
65 if (seconds === 0 && !full) return '0s'
66
67 const hourSymbol = (symbol || 'h')
68 const minuteSymbol = (symbol || 'm')
69 const secondsSymbol = full ? '' : 's'
70
71 const hours = Math.floor(seconds / 3600)
72 if (hours >= 1) time = hours + hourSymbol
73 else if (full) time = '0' + hourSymbol
74
75 seconds %= 3600
76 const minutes = Math.floor(seconds / 60)
77 if (minutes >= 1 && minutes < 10 && full) time += '0' + minutes + minuteSymbol
78 else if (minutes >= 1) time += minutes + minuteSymbol
79 else if (full) time += '00' + minuteSymbol
80
81 seconds %= 60
82 if (seconds >= 1 && seconds < 10 && full) time += '0' + seconds + secondsSymbol
83 else if (seconds >= 1) time += seconds + secondsSymbol
84 else if (full) time += '00'
85
86 return time
87}
88
46// --------------------------------------------------------------------------- 89// ---------------------------------------------------------------------------
47 90
48export { 91export {
@@ -51,7 +94,9 @@ export {
51 isThisMonth, 94 isThisMonth,
52 isToday, 95 isToday,
53 isLastMonth, 96 isLastMonth,
54 isLastWeek 97 isLastWeek,
98 timeToInt,
99 secondsToTime
55} 100}
56 101
57// --------------------------------------------------------------------------- 102// ---------------------------------------------------------------------------
diff --git a/shared/core-utils/common/index.ts b/shared/core-utils/common/index.ts
new file mode 100644
index 000000000..0908ff981
--- /dev/null
+++ b/shared/core-utils/common/index.ts
@@ -0,0 +1,6 @@
1export * from './date'
2export * from './miscs'
3export * from './regexp'
4export * from './promises'
5export * from './types'
6export * from './url'
diff --git a/shared/core-utils/miscs/miscs.ts b/shared/core-utils/common/miscs.ts
index 4780ca922..bc65dc338 100644
--- a/shared/core-utils/miscs/miscs.ts
+++ b/shared/core-utils/common/miscs.ts
@@ -20,14 +20,6 @@ function compareSemVer (a: string, b: string) {
20 return segmentsA.length - segmentsB.length 20 return segmentsA.length - segmentsB.length
21} 21}
22 22
23function isPromise (value: any) {
24 return value && typeof value.then === 'function'
25}
26
27function isCatchable (value: any) {
28 return value && typeof value.catch === 'function'
29}
30
31function sortObjectComparator (key: string, order: 'asc' | 'desc') { 23function sortObjectComparator (key: string, order: 'asc' | 'desc') {
32 return (a: any, b: any) => { 24 return (a: any, b: any) => {
33 if (a[key] < b[key]) { 25 if (a[key] < b[key]) {
@@ -45,7 +37,5 @@ function sortObjectComparator (key: string, order: 'asc' | 'desc') {
45export { 37export {
46 randomInt, 38 randomInt,
47 compareSemVer, 39 compareSemVer,
48 isPromise,
49 isCatchable,
50 sortObjectComparator 40 sortObjectComparator
51} 41}
diff --git a/shared/core-utils/common/promises.ts b/shared/core-utils/common/promises.ts
new file mode 100644
index 000000000..7ef9d60b6
--- /dev/null
+++ b/shared/core-utils/common/promises.ts
@@ -0,0 +1,12 @@
1function isPromise (value: any) {
2 return value && typeof value.then === 'function'
3}
4
5function isCatchable (value: any) {
6 return value && typeof value.catch === 'function'
7}
8
9export {
10 isPromise,
11 isCatchable
12}
diff --git a/shared/core-utils/common/regexp.ts b/shared/core-utils/common/regexp.ts
new file mode 100644
index 000000000..59eb87eb6
--- /dev/null
+++ b/shared/core-utils/common/regexp.ts
@@ -0,0 +1,5 @@
1export const uuidRegex = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
2
3export function removeFragmentedMP4Ext (path: string) {
4 return path.replace(/-fragmented.mp4$/i, '')
5}
diff --git a/shared/core-utils/miscs/types.ts b/shared/core-utils/common/types.ts
index bd2a97b98..bd2a97b98 100644
--- a/shared/core-utils/miscs/types.ts
+++ b/shared/core-utils/common/types.ts
diff --git a/shared/core-utils/common/url.ts b/shared/core-utils/common/url.ts
new file mode 100644
index 000000000..52ed247c4
--- /dev/null
+++ b/shared/core-utils/common/url.ts
@@ -0,0 +1,130 @@
1import { Video, VideoPlaylist } from '../../models'
2import { secondsToTime } from './date'
3
4function buildPlaylistLink (playlist: Pick<VideoPlaylist, 'shortUUID'>, base?: string) {
5 return (base ?? window.location.origin) + buildPlaylistWatchPath(playlist)
6}
7
8function buildPlaylistWatchPath (playlist: Pick<VideoPlaylist, 'shortUUID'>) {
9 return '/w/p/' + playlist.shortUUID
10}
11
12function buildVideoWatchPath (video: Pick<Video, 'shortUUID'>) {
13 return '/w/' + video.shortUUID
14}
15
16function buildVideoLink (video: Pick<Video, 'shortUUID'>, base?: string) {
17 return (base ?? window.location.origin) + buildVideoWatchPath(video)
18}
19
20function buildPlaylistEmbedPath (playlist: Pick<VideoPlaylist, 'uuid'>) {
21 return '/video-playlists/embed/' + playlist.uuid
22}
23
24function buildPlaylistEmbedLink (playlist: Pick<VideoPlaylist, 'uuid'>, base?: string) {
25 return (base ?? window.location.origin) + buildPlaylistEmbedPath(playlist)
26}
27
28function buildVideoEmbedPath (video: Pick<Video, 'uuid'>) {
29 return '/videos/embed/' + video.uuid
30}
31
32function buildVideoEmbedLink (video: Pick<Video, 'uuid'>, base?: string) {
33 return (base ?? window.location.origin) + buildVideoEmbedPath(video)
34}
35
36function decorateVideoLink (options: {
37 url: string
38
39 startTime?: number
40 stopTime?: number
41
42 subtitle?: string
43
44 loop?: boolean
45 autoplay?: boolean
46 muted?: boolean
47
48 // Embed options
49 title?: boolean
50 warningTitle?: boolean
51 controls?: boolean
52 peertubeLink?: boolean
53}) {
54 const { url } = options
55
56 const params = generateParams(window.location.search)
57
58 if (options.startTime !== undefined && options.startTime !== null) {
59 const startTimeInt = Math.floor(options.startTime)
60 params.set('start', secondsToTime(startTimeInt))
61 }
62
63 if (options.stopTime) {
64 const stopTimeInt = Math.floor(options.stopTime)
65 params.set('stop', secondsToTime(stopTimeInt))
66 }
67
68 if (options.subtitle) params.set('subtitle', options.subtitle)
69
70 if (options.loop === true) params.set('loop', '1')
71 if (options.autoplay === true) params.set('autoplay', '1')
72 if (options.muted === true) params.set('muted', '1')
73 if (options.title === false) params.set('title', '0')
74 if (options.warningTitle === false) params.set('warningTitle', '0')
75 if (options.controls === false) params.set('controls', '0')
76 if (options.peertubeLink === false) params.set('peertubeLink', '0')
77
78 return buildUrl(url, params)
79}
80
81function decoratePlaylistLink (options: {
82 url: string
83
84 playlistPosition?: number
85}) {
86 const { url } = options
87
88 const params = generateParams(window.location.search)
89
90 if (options.playlistPosition) params.set('playlistPosition', '' + options.playlistPosition)
91
92 return buildUrl(url, params)
93}
94
95// ---------------------------------------------------------------------------
96
97export {
98 buildPlaylistLink,
99 buildVideoLink,
100
101 buildVideoWatchPath,
102 buildPlaylistWatchPath,
103
104 buildPlaylistEmbedPath,
105 buildVideoEmbedPath,
106
107 buildPlaylistEmbedLink,
108 buildVideoEmbedLink,
109
110 decorateVideoLink,
111 decoratePlaylistLink
112}
113
114function buildUrl (url: string, params: URLSearchParams) {
115 let hasParams = false
116 params.forEach(() => { hasParams = true })
117
118 if (hasParams) return url + '?' + params.toString()
119
120 return url
121}
122
123function generateParams (url: string) {
124 const params = new URLSearchParams(window.location.search)
125 // Unused parameters in embed
126 params.delete('videoId')
127 params.delete('resume')
128
129 return params
130}
diff --git a/shared/core-utils/index.ts b/shared/core-utils/index.ts
index 42d7cab1d..2a7d4d982 100644
--- a/shared/core-utils/index.ts
+++ b/shared/core-utils/index.ts
@@ -1,7 +1,7 @@
1export * from './abuse' 1export * from './abuse'
2export * from './common'
2export * from './i18n' 3export * from './i18n'
3export * from './logs'
4export * from './miscs'
5export * from './plugins' 4export * from './plugins'
6export * from './renderer' 5export * from './renderer'
7export * from './users' 6export * from './users'
7export * from './utils'
diff --git a/shared/core-utils/logs/index.ts b/shared/core-utils/logs/index.ts
deleted file mode 100644
index ceb5d7a7f..000000000
--- a/shared/core-utils/logs/index.ts
+++ /dev/null
@@ -1 +0,0 @@
1export * from './logs'
diff --git a/shared/core-utils/logs/logs.ts b/shared/core-utils/logs/logs.ts
deleted file mode 100644
index d0996cf55..000000000
--- a/shared/core-utils/logs/logs.ts
+++ /dev/null
@@ -1,25 +0,0 @@
1import { stat } from 'fs-extra'
2
3async function mtimeSortFilesDesc (files: string[], basePath: string) {
4 const promises = []
5 const out: { file: string, mtime: number }[] = []
6
7 for (const file of files) {
8 const p = stat(basePath + '/' + file)
9 .then(stats => {
10 if (stats.isFile()) out.push({ file, mtime: stats.mtime.getTime() })
11 })
12
13 promises.push(p)
14 }
15
16 await Promise.all(promises)
17
18 out.sort((a, b) => b.mtime - a.mtime)
19
20 return out
21}
22
23export {
24 mtimeSortFilesDesc
25}
diff --git a/shared/core-utils/miscs/index.ts b/shared/core-utils/miscs/index.ts
deleted file mode 100644
index 251df1de2..000000000
--- a/shared/core-utils/miscs/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
1export * from './date'
2export * from './miscs'
3export * from './types'
4export * from './http-error-codes'
5export * from './http-methods'
diff --git a/shared/core-utils/plugins/hooks.ts b/shared/core-utils/plugins/hooks.ts
index 5405e0529..92cb5ad68 100644
--- a/shared/core-utils/plugins/hooks.ts
+++ b/shared/core-utils/plugins/hooks.ts
@@ -1,5 +1,5 @@
1import { HookType } from '../../models/plugins/hook-type.enum' 1import { HookType } from '../../models/plugins/hook-type.enum'
2import { isCatchable, isPromise } from '../miscs/miscs' 2import { isCatchable, isPromise } from '../common/promises'
3 3
4function getHookType (hookName: string) { 4function getHookType (hookName: string) {
5 if (hookName.startsWith('filter:')) return HookType.FILTER 5 if (hookName.startsWith('filter:')) return HookType.FILTER
diff --git a/shared/core-utils/utils/index.ts b/shared/core-utils/utils/index.ts
new file mode 100644
index 000000000..a71977d88
--- /dev/null
+++ b/shared/core-utils/utils/index.ts
@@ -0,0 +1 @@
export * from './object'
diff --git a/shared/core-utils/utils/object.ts b/shared/core-utils/utils/object.ts
new file mode 100644
index 000000000..9a8a98f9b
--- /dev/null
+++ b/shared/core-utils/utils/object.ts
@@ -0,0 +1,15 @@
1function pick <O extends object, K extends keyof O> (object: O, keys: K[]): Pick<O, K> {
2 const result: any = {}
3
4 for (const key of keys) {
5 if (Object.prototype.hasOwnProperty.call(object, key)) {
6 result[key] = object[key]
7 }
8 }
9
10 return result
11}
12
13export {
14 pick
15}
diff --git a/shared/extra-utils/bulk/bulk-command.ts b/shared/extra-utils/bulk/bulk-command.ts
new file mode 100644
index 000000000..b5c5673ce
--- /dev/null
+++ b/shared/extra-utils/bulk/bulk-command.ts
@@ -0,0 +1,20 @@
1import { BulkRemoveCommentsOfBody, HttpStatusCode } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class BulkCommand extends AbstractCommand {
5
6 removeCommentsOf (options: OverrideCommandOptions & {
7 attributes: BulkRemoveCommentsOfBody
8 }) {
9 const { attributes } = options
10
11 return this.postBodyRequest({
12 ...options,
13
14 path: '/api/v1/bulk/remove-comments-of',
15 fields: attributes,
16 implicitToken: true,
17 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
18 })
19 }
20}
diff --git a/shared/extra-utils/bulk/bulk.ts b/shared/extra-utils/bulk/bulk.ts
deleted file mode 100644
index b6f437b8b..000000000
--- a/shared/extra-utils/bulk/bulk.ts
+++ /dev/null
@@ -1,25 +0,0 @@
1import { BulkRemoveCommentsOfBody } from "@shared/models/bulk/bulk-remove-comments-of-body.model"
2import { makePostBodyRequest } from "../requests/requests"
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4
5function bulkRemoveCommentsOf (options: {
6 url: string
7 token: string
8 attributes: BulkRemoveCommentsOfBody
9 expectedStatus?: number
10}) {
11 const { url, token, attributes, expectedStatus } = options
12 const path = '/api/v1/bulk/remove-comments-of'
13
14 return makePostBodyRequest({
15 url,
16 path,
17 token,
18 fields: attributes,
19 statusCodeExpected: expectedStatus || HttpStatusCode.NO_CONTENT_204
20 })
21}
22
23export {
24 bulkRemoveCommentsOf
25}
diff --git a/shared/extra-utils/bulk/index.ts b/shared/extra-utils/bulk/index.ts
new file mode 100644
index 000000000..391597243
--- /dev/null
+++ b/shared/extra-utils/bulk/index.ts
@@ -0,0 +1 @@
export * from './bulk-command'
diff --git a/shared/extra-utils/cli/cli-command.ts b/shared/extra-utils/cli/cli-command.ts
new file mode 100644
index 000000000..bc1dddc68
--- /dev/null
+++ b/shared/extra-utils/cli/cli-command.ts
@@ -0,0 +1,23 @@
1import { exec } from 'child_process'
2import { AbstractCommand } from '../shared'
3
4export class CLICommand extends AbstractCommand {
5
6 static exec (command: string) {
7 return new Promise<string>((res, rej) => {
8 exec(command, (err, stdout, _stderr) => {
9 if (err) return rej(err)
10
11 return res(stdout)
12 })
13 })
14 }
15
16 getEnv () {
17 return `NODE_ENV=test NODE_APP_INSTANCE=${this.server.internalServerNumber}`
18 }
19
20 async execWithEnv (command: string) {
21 return CLICommand.exec(`${this.getEnv()} ${command}`)
22 }
23}
diff --git a/shared/extra-utils/cli/cli.ts b/shared/extra-utils/cli/cli.ts
deleted file mode 100644
index c62e170bb..000000000
--- a/shared/extra-utils/cli/cli.ts
+++ /dev/null
@@ -1,24 +0,0 @@
1import { exec } from 'child_process'
2
3import { ServerInfo } from '../server/servers'
4
5function getEnvCli (server?: ServerInfo) {
6 return `NODE_ENV=test NODE_APP_INSTANCE=${server.internalServerNumber}`
7}
8
9async function execCLI (command: string) {
10 return new Promise<string>((res, rej) => {
11 exec(command, (err, stdout, stderr) => {
12 if (err) return rej(err)
13
14 return res(stdout)
15 })
16 })
17}
18
19// ---------------------------------------------------------------------------
20
21export {
22 execCLI,
23 getEnvCli
24}
diff --git a/shared/extra-utils/cli/index.ts b/shared/extra-utils/cli/index.ts
new file mode 100644
index 000000000..91b5abfbe
--- /dev/null
+++ b/shared/extra-utils/cli/index.ts
@@ -0,0 +1 @@
export * from './cli-command'
diff --git a/shared/extra-utils/custom-pages/custom-pages-command.ts b/shared/extra-utils/custom-pages/custom-pages-command.ts
new file mode 100644
index 000000000..cd869a8de
--- /dev/null
+++ b/shared/extra-utils/custom-pages/custom-pages-command.ts
@@ -0,0 +1,33 @@
1import { CustomPage, HttpStatusCode } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class CustomPagesCommand extends AbstractCommand {
5
6 getInstanceHomepage (options: OverrideCommandOptions = {}) {
7 const path = '/api/v1/custom-pages/homepage/instance'
8
9 return this.getRequestBody<CustomPage>({
10 ...options,
11
12 path,
13 implicitToken: false,
14 defaultExpectedStatus: HttpStatusCode.OK_200
15 })
16 }
17
18 updateInstanceHomepage (options: OverrideCommandOptions & {
19 content: string
20 }) {
21 const { content } = options
22 const path = '/api/v1/custom-pages/homepage/instance'
23
24 return this.putBodyRequest({
25 ...options,
26
27 path,
28 fields: { content },
29 implicitToken: true,
30 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
31 })
32 }
33}
diff --git a/shared/extra-utils/custom-pages/custom-pages.ts b/shared/extra-utils/custom-pages/custom-pages.ts
deleted file mode 100644
index bf2d16c70..000000000
--- a/shared/extra-utils/custom-pages/custom-pages.ts
+++ /dev/null
@@ -1,31 +0,0 @@
1import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
2import { makeGetRequest, makePutBodyRequest } from '../requests/requests'
3
4function getInstanceHomepage (url: string, statusCodeExpected = HttpStatusCode.OK_200) {
5 const path = '/api/v1/custom-pages/homepage/instance'
6
7 return makeGetRequest({
8 url,
9 path,
10 statusCodeExpected
11 })
12}
13
14function updateInstanceHomepage (url: string, token: string, content: string) {
15 const path = '/api/v1/custom-pages/homepage/instance'
16
17 return makePutBodyRequest({
18 url,
19 path,
20 token,
21 fields: { content },
22 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
23 })
24}
25
26// ---------------------------------------------------------------------------
27
28export {
29 getInstanceHomepage,
30 updateInstanceHomepage
31}
diff --git a/shared/extra-utils/custom-pages/index.ts b/shared/extra-utils/custom-pages/index.ts
new file mode 100644
index 000000000..58aed04f2
--- /dev/null
+++ b/shared/extra-utils/custom-pages/index.ts
@@ -0,0 +1 @@
export * from './custom-pages-command'
diff --git a/shared/extra-utils/feeds/feeds-command.ts b/shared/extra-utils/feeds/feeds-command.ts
new file mode 100644
index 000000000..3c95f9536
--- /dev/null
+++ b/shared/extra-utils/feeds/feeds-command.ts
@@ -0,0 +1,44 @@
1
2import { HttpStatusCode } from '@shared/models'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5type FeedType = 'videos' | 'video-comments' | 'subscriptions'
6
7export class FeedCommand extends AbstractCommand {
8
9 getXML (options: OverrideCommandOptions & {
10 feed: FeedType
11 format?: string
12 }) {
13 const { feed, format } = options
14 const path = '/feeds/' + feed + '.xml'
15
16 return this.getRequestText({
17 ...options,
18
19 path,
20 query: format ? { format } : undefined,
21 accept: 'application/xml',
22 implicitToken: false,
23 defaultExpectedStatus: HttpStatusCode.OK_200
24 })
25 }
26
27 getJSON (options: OverrideCommandOptions & {
28 feed: FeedType
29 query?: { [ id: string ]: any }
30 }) {
31 const { feed, query } = options
32 const path = '/feeds/' + feed + '.json'
33
34 return this.getRequestText({
35 ...options,
36
37 path,
38 query,
39 accept: 'application/json',
40 implicitToken: false,
41 defaultExpectedStatus: HttpStatusCode.OK_200
42 })
43 }
44}
diff --git a/shared/extra-utils/feeds/feeds.ts b/shared/extra-utils/feeds/feeds.ts
deleted file mode 100644
index ce0a98c6d..000000000
--- a/shared/extra-utils/feeds/feeds.ts
+++ /dev/null
@@ -1,33 +0,0 @@
1import * as request from 'supertest'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4type FeedType = 'videos' | 'video-comments' | 'subscriptions'
5
6function getXMLfeed (url: string, feed: FeedType, format?: string) {
7 const path = '/feeds/' + feed + '.xml'
8
9 return request(url)
10 .get(path)
11 .query((format) ? { format: format } : {})
12 .set('Accept', 'application/xml')
13 .expect(HttpStatusCode.OK_200)
14 .expect('Content-Type', /xml/)
15}
16
17function getJSONfeed (url: string, feed: FeedType, query: any = {}, statusCodeExpected = HttpStatusCode.OK_200) {
18 const path = '/feeds/' + feed + '.json'
19
20 return request(url)
21 .get(path)
22 .query(query)
23 .set('Accept', 'application/json')
24 .expect(statusCodeExpected)
25 .expect('Content-Type', /json/)
26}
27
28// ---------------------------------------------------------------------------
29
30export {
31 getXMLfeed,
32 getJSONfeed
33}
diff --git a/shared/extra-utils/feeds/index.ts b/shared/extra-utils/feeds/index.ts
new file mode 100644
index 000000000..662a22b6f
--- /dev/null
+++ b/shared/extra-utils/feeds/index.ts
@@ -0,0 +1 @@
export * from './feeds-command'
diff --git a/shared/extra-utils/index.ts b/shared/extra-utils/index.ts
index 87ee8abba..4b3636d06 100644
--- a/shared/extra-utils/index.ts
+++ b/shared/extra-utils/index.ts
@@ -1,51 +1,15 @@
1export * from './bulk/bulk' 1export * from './bulk'
2 2export * from './cli'
3export * from './cli/cli' 3export * from './custom-pages'
4 4export * from './feeds'
5export * from './custom-pages/custom-pages' 5export * from './logs'
6 6export * from './miscs'
7export * from './feeds/feeds' 7export * from './mock-servers'
8 8export * from './moderation'
9export * from './mock-servers/mock-instances-index' 9export * from './overviews'
10 10export * from './requests'
11export * from './miscs/email' 11export * from './search'
12export * from './miscs/sql' 12export * from './server'
13export * from './miscs/miscs' 13export * from './socket'
14export * from './miscs/stubs' 14export * from './users'
15 15export * from './videos'
16export * from './moderation/abuses'
17export * from './plugins/mock-blocklist'
18
19export * from './requests/check-api-params'
20export * from './requests/requests'
21
22export * from './search/video-channels'
23export * from './search/video-playlists'
24export * from './search/videos'
25
26export * from './server/activitypub'
27export * from './server/clients'
28export * from './server/config'
29export * from './server/debug'
30export * from './server/follows'
31export * from './server/jobs'
32export * from './server/plugins'
33export * from './server/servers'
34
35export * from './users/accounts'
36export * from './users/blocklist'
37export * from './users/login'
38export * from './users/user-notifications'
39export * from './users/user-subscriptions'
40export * from './users/users'
41
42export * from './videos/live'
43export * from './videos/services'
44export * from './videos/video-blacklist'
45export * from './videos/video-captions'
46export * from './videos/video-change-ownership'
47export * from './videos/video-channels'
48export * from './videos/video-comments'
49export * from './videos/video-playlists'
50export * from './videos/video-streaming-playlists'
51export * from './videos/videos'
diff --git a/shared/extra-utils/logs/index.ts b/shared/extra-utils/logs/index.ts
new file mode 100644
index 000000000..69452d7f0
--- /dev/null
+++ b/shared/extra-utils/logs/index.ts
@@ -0,0 +1 @@
export * from './logs-command'
diff --git a/shared/extra-utils/logs/logs-command.ts b/shared/extra-utils/logs/logs-command.ts
new file mode 100644
index 000000000..5912e814f
--- /dev/null
+++ b/shared/extra-utils/logs/logs-command.ts
@@ -0,0 +1,43 @@
1import { HttpStatusCode } from '@shared/models'
2import { LogLevel } from '../../models/server/log-level.type'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class LogsCommand extends AbstractCommand {
6
7 getLogs (options: OverrideCommandOptions & {
8 startDate: Date
9 endDate?: Date
10 level?: LogLevel
11 }) {
12 const { startDate, endDate, level } = options
13 const path = '/api/v1/server/logs'
14
15 return this.getRequestBody({
16 ...options,
17
18 path,
19 query: { startDate, endDate, level },
20 implicitToken: true,
21 defaultExpectedStatus: HttpStatusCode.OK_200
22 })
23 }
24
25 getAuditLogs (options: OverrideCommandOptions & {
26 startDate: Date
27 endDate?: Date
28 }) {
29 const { startDate, endDate } = options
30
31 const path = '/api/v1/server/audit-logs'
32
33 return this.getRequestBody({
34 ...options,
35
36 path,
37 query: { startDate, endDate },
38 implicitToken: true,
39 defaultExpectedStatus: HttpStatusCode.OK_200
40 })
41 }
42
43}
diff --git a/shared/extra-utils/logs/logs.ts b/shared/extra-utils/logs/logs.ts
deleted file mode 100644
index 8d741276c..000000000
--- a/shared/extra-utils/logs/logs.ts
+++ /dev/null
@@ -1,32 +0,0 @@
1import { makeGetRequest } from '../requests/requests'
2import { LogLevel } from '../../models/server/log-level.type'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4
5function getLogs (url: string, accessToken: string, startDate: Date, endDate?: Date, level?: LogLevel) {
6 const path = '/api/v1/server/logs'
7
8 return makeGetRequest({
9 url,
10 path,
11 token: accessToken,
12 query: { startDate, endDate, level },
13 statusCodeExpected: HttpStatusCode.OK_200
14 })
15}
16
17function getAuditLogs (url: string, accessToken: string, startDate: Date, endDate?: Date) {
18 const path = '/api/v1/server/audit-logs'
19
20 return makeGetRequest({
21 url,
22 path,
23 token: accessToken,
24 query: { startDate, endDate },
25 statusCodeExpected: HttpStatusCode.OK_200
26 })
27}
28
29export {
30 getLogs,
31 getAuditLogs
32}
diff --git a/shared/extra-utils/miscs/checks.ts b/shared/extra-utils/miscs/checks.ts
new file mode 100644
index 000000000..7fc92f804
--- /dev/null
+++ b/shared/extra-utils/miscs/checks.ts
@@ -0,0 +1,46 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */
2
3import { expect } from 'chai'
4import { pathExists, readFile } from 'fs-extra'
5import { join } from 'path'
6import { root } from '@server/helpers/core-utils'
7import { HttpStatusCode } from '@shared/models'
8import { makeGetRequest } from '../requests'
9import { PeerTubeServer } from '../server'
10
11// Default interval -> 5 minutes
12function dateIsValid (dateString: string, interval = 300000) {
13 const dateToCheck = new Date(dateString)
14 const now = new Date()
15
16 return Math.abs(now.getTime() - dateToCheck.getTime()) <= interval
17}
18
19async function testImage (url: string, imageName: string, imagePath: string, extension = '.jpg') {
20 const res = await makeGetRequest({
21 url,
22 path: imagePath,
23 expectedStatus: HttpStatusCode.OK_200
24 })
25
26 const body = res.body
27
28 const data = await readFile(join(root(), 'server', 'tests', 'fixtures', imageName + extension))
29 const minLength = body.length - ((30 * body.length) / 100)
30 const maxLength = body.length + ((30 * body.length) / 100)
31
32 expect(data.length).to.be.above(minLength, 'the generated image is way smaller than the recorded fixture')
33 expect(data.length).to.be.below(maxLength, 'the generated image is way larger than the recorded fixture')
34}
35
36async function testFileExistsOrNot (server: PeerTubeServer, directory: string, filePath: string, exist: boolean) {
37 const base = server.servers.buildDirectory(directory)
38
39 expect(await pathExists(join(base, filePath))).to.equal(exist)
40}
41
42export {
43 dateIsValid,
44 testImage,
45 testFileExistsOrNot
46}
diff --git a/shared/extra-utils/miscs/generate.ts b/shared/extra-utils/miscs/generate.ts
new file mode 100644
index 000000000..8d6435481
--- /dev/null
+++ b/shared/extra-utils/miscs/generate.ts
@@ -0,0 +1,61 @@
1import * as ffmpeg from 'fluent-ffmpeg'
2import { ensureDir, pathExists } from 'fs-extra'
3import { dirname } from 'path'
4import { buildAbsoluteFixturePath } from './tests'
5
6async function generateHighBitrateVideo () {
7 const tempFixturePath = buildAbsoluteFixturePath('video_high_bitrate_1080p.mp4', true)
8
9 await ensureDir(dirname(tempFixturePath))
10
11 const exists = await pathExists(tempFixturePath)
12 if (!exists) {
13 console.log('Generating high bitrate video.')
14
15 // Generate a random, high bitrate video on the fly, so we don't have to include
16 // a large file in the repo. The video needs to have a certain minimum length so
17 // that FFmpeg properly applies bitrate limits.
18 // https://stackoverflow.com/a/15795112
19 return new Promise<string>((res, rej) => {
20 ffmpeg()
21 .outputOptions([ '-f rawvideo', '-video_size 1920x1080', '-i /dev/urandom' ])
22 .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
23 .outputOptions([ '-maxrate 10M', '-bufsize 10M' ])
24 .output(tempFixturePath)
25 .on('error', rej)
26 .on('end', () => res(tempFixturePath))
27 .run()
28 })
29 }
30
31 return tempFixturePath
32}
33
34async function generateVideoWithFramerate (fps = 60) {
35 const tempFixturePath = buildAbsoluteFixturePath(`video_${fps}fps.mp4`, true)
36
37 await ensureDir(dirname(tempFixturePath))
38
39 const exists = await pathExists(tempFixturePath)
40 if (!exists) {
41 console.log('Generating video with framerate %d.', fps)
42
43 return new Promise<string>((res, rej) => {
44 ffmpeg()
45 .outputOptions([ '-f rawvideo', '-video_size 1280x720', '-i /dev/urandom' ])
46 .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
47 .outputOptions([ `-r ${fps}` ])
48 .output(tempFixturePath)
49 .on('error', rej)
50 .on('end', () => res(tempFixturePath))
51 .run()
52 })
53 }
54
55 return tempFixturePath
56}
57
58export {
59 generateHighBitrateVideo,
60 generateVideoWithFramerate
61}
diff --git a/shared/extra-utils/miscs/index.ts b/shared/extra-utils/miscs/index.ts
new file mode 100644
index 000000000..4474661de
--- /dev/null
+++ b/shared/extra-utils/miscs/index.ts
@@ -0,0 +1,5 @@
1export * from './checks'
2export * from './generate'
3export * from './sql-command'
4export * from './tests'
5export * from './webtorrent'
diff --git a/shared/extra-utils/miscs/miscs.ts b/shared/extra-utils/miscs/miscs.ts
deleted file mode 100644
index 462b914d4..000000000
--- a/shared/extra-utils/miscs/miscs.ts
+++ /dev/null
@@ -1,170 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import * as chai from 'chai'
4import * as ffmpeg from 'fluent-ffmpeg'
5import { ensureDir, pathExists, readFile, stat } from 'fs-extra'
6import { basename, dirname, isAbsolute, join, resolve } from 'path'
7import * as request from 'supertest'
8import * as WebTorrent from 'webtorrent'
9import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
10
11const expect = chai.expect
12let webtorrent: WebTorrent.Instance
13
14function immutableAssign<T, U> (target: T, source: U) {
15 return Object.assign<{}, T, U>({}, target, source)
16}
17
18// Default interval -> 5 minutes
19function dateIsValid (dateString: string, interval = 300000) {
20 const dateToCheck = new Date(dateString)
21 const now = new Date()
22
23 return Math.abs(now.getTime() - dateToCheck.getTime()) <= interval
24}
25
26function wait (milliseconds: number) {
27 return new Promise(resolve => setTimeout(resolve, milliseconds))
28}
29
30function webtorrentAdd (torrent: string, refreshWebTorrent = false) {
31 const WebTorrent = require('webtorrent')
32
33 if (!webtorrent) webtorrent = new WebTorrent()
34 if (refreshWebTorrent === true) webtorrent = new WebTorrent()
35
36 return new Promise<WebTorrent.Torrent>(res => webtorrent.add(torrent, res))
37}
38
39function root () {
40 // We are in /miscs
41 let root = join(__dirname, '..', '..', '..')
42
43 if (basename(root) === 'dist') root = resolve(root, '..')
44
45 return root
46}
47
48function buildServerDirectory (server: { internalServerNumber: number }, directory: string) {
49 return join(root(), 'test' + server.internalServerNumber, directory)
50}
51
52async function testImage (url: string, imageName: string, imagePath: string, extension = '.jpg') {
53 const res = await request(url)
54 .get(imagePath)
55 .expect(HttpStatusCode.OK_200)
56
57 const body = res.body
58
59 const data = await readFile(join(root(), 'server', 'tests', 'fixtures', imageName + extension))
60 const minLength = body.length - ((30 * body.length) / 100)
61 const maxLength = body.length + ((30 * body.length) / 100)
62
63 expect(data.length).to.be.above(minLength, 'the generated image is way smaller than the recorded fixture')
64 expect(data.length).to.be.below(maxLength, 'the generated image is way larger than the recorded fixture')
65}
66
67async function testFileExistsOrNot (server: { internalServerNumber: number }, directory: string, filePath: string, exist: boolean) {
68 const base = buildServerDirectory(server, directory)
69
70 expect(await pathExists(join(base, filePath))).to.equal(exist)
71}
72
73function isGithubCI () {
74 return !!process.env.GITHUB_WORKSPACE
75}
76
77function buildAbsoluteFixturePath (path: string, customCIPath = false) {
78 if (isAbsolute(path)) return path
79
80 if (customCIPath && process.env.GITHUB_WORKSPACE) {
81 return join(process.env.GITHUB_WORKSPACE, 'fixtures', path)
82 }
83
84 return join(root(), 'server', 'tests', 'fixtures', path)
85}
86
87function areHttpImportTestsDisabled () {
88 const disabled = process.env.DISABLE_HTTP_IMPORT_TESTS === 'true'
89
90 if (disabled) console.log('Import tests are disabled')
91
92 return disabled
93}
94
95async function generateHighBitrateVideo () {
96 const tempFixturePath = buildAbsoluteFixturePath('video_high_bitrate_1080p.mp4', true)
97
98 await ensureDir(dirname(tempFixturePath))
99
100 const exists = await pathExists(tempFixturePath)
101 if (!exists) {
102 console.log('Generating high bitrate video.')
103
104 // Generate a random, high bitrate video on the fly, so we don't have to include
105 // a large file in the repo. The video needs to have a certain minimum length so
106 // that FFmpeg properly applies bitrate limits.
107 // https://stackoverflow.com/a/15795112
108 return new Promise<string>((res, rej) => {
109 ffmpeg()
110 .outputOptions([ '-f rawvideo', '-video_size 1920x1080', '-i /dev/urandom' ])
111 .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
112 .outputOptions([ '-maxrate 10M', '-bufsize 10M' ])
113 .output(tempFixturePath)
114 .on('error', rej)
115 .on('end', () => res(tempFixturePath))
116 .run()
117 })
118 }
119
120 return tempFixturePath
121}
122
123async function generateVideoWithFramerate (fps = 60) {
124 const tempFixturePath = buildAbsoluteFixturePath(`video_${fps}fps.mp4`, true)
125
126 await ensureDir(dirname(tempFixturePath))
127
128 const exists = await pathExists(tempFixturePath)
129 if (!exists) {
130 console.log('Generating video with framerate %d.', fps)
131
132 return new Promise<string>((res, rej) => {
133 ffmpeg()
134 .outputOptions([ '-f rawvideo', '-video_size 1280x720', '-i /dev/urandom' ])
135 .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
136 .outputOptions([ `-r ${fps}` ])
137 .output(tempFixturePath)
138 .on('error', rej)
139 .on('end', () => res(tempFixturePath))
140 .run()
141 })
142 }
143
144 return tempFixturePath
145}
146
147async function getFileSize (path: string) {
148 const stats = await stat(path)
149
150 return stats.size
151}
152
153// ---------------------------------------------------------------------------
154
155export {
156 dateIsValid,
157 wait,
158 areHttpImportTestsDisabled,
159 buildServerDirectory,
160 webtorrentAdd,
161 getFileSize,
162 immutableAssign,
163 testImage,
164 isGithubCI,
165 buildAbsoluteFixturePath,
166 testFileExistsOrNot,
167 root,
168 generateHighBitrateVideo,
169 generateVideoWithFramerate
170}
diff --git a/shared/extra-utils/miscs/sql-command.ts b/shared/extra-utils/miscs/sql-command.ts
new file mode 100644
index 000000000..80c8cd271
--- /dev/null
+++ b/shared/extra-utils/miscs/sql-command.ts
@@ -0,0 +1,142 @@
1import { QueryTypes, Sequelize } from 'sequelize'
2import { AbstractCommand } from '../shared/abstract-command'
3
4export class SQLCommand extends AbstractCommand {
5 private sequelize: Sequelize
6
7 deleteAll (table: string) {
8 const seq = this.getSequelize()
9
10 const options = { type: QueryTypes.DELETE }
11
12 return seq.query(`DELETE FROM "${table}"`, options)
13 }
14
15 async getCount (table: string) {
16 const seq = this.getSequelize()
17
18 const options = { type: QueryTypes.SELECT as QueryTypes.SELECT }
19
20 const [ { total } ] = await seq.query<{ total: string }>(`SELECT COUNT(*) as total FROM "${table}"`, options)
21 if (total === null) return 0
22
23 return parseInt(total, 10)
24 }
25
26 setActorField (to: string, field: string, value: string) {
27 const seq = this.getSequelize()
28
29 const options = { type: QueryTypes.UPDATE }
30
31 return seq.query(`UPDATE actor SET "${field}" = '${value}' WHERE url = '${to}'`, options)
32 }
33
34 setVideoField (uuid: string, field: string, value: string) {
35 const seq = this.getSequelize()
36
37 const options = { type: QueryTypes.UPDATE }
38
39 return seq.query(`UPDATE video SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
40 }
41
42 setPlaylistField (uuid: string, field: string, value: string) {
43 const seq = this.getSequelize()
44
45 const options = { type: QueryTypes.UPDATE }
46
47 return seq.query(`UPDATE "videoPlaylist" SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
48 }
49
50 async countVideoViewsOf (uuid: string) {
51 const seq = this.getSequelize()
52
53 // tslint:disable
54 const query = 'SELECT SUM("videoView"."views") AS "total" FROM "videoView" ' +
55 `INNER JOIN "video" ON "video"."id" = "videoView"."videoId" WHERE "video"."uuid" = '${uuid}'`
56
57 const options = { type: QueryTypes.SELECT as QueryTypes.SELECT }
58 const [ { total } ] = await seq.query<{ total: number }>(query, options)
59
60 if (!total) return 0
61
62 return parseInt(total + '', 10)
63 }
64
65 getActorImage (filename: string) {
66 return this.selectQuery(`SELECT * FROM "actorImage" WHERE filename = '${filename}'`)
67 .then(rows => rows[0])
68 }
69
70 selectQuery (query: string) {
71 const seq = this.getSequelize()
72 const options = { type: QueryTypes.SELECT as QueryTypes.SELECT }
73
74 return seq.query<any>(query, options)
75 }
76
77 updateQuery (query: string) {
78 const seq = this.getSequelize()
79 const options = { type: QueryTypes.UPDATE as QueryTypes.UPDATE }
80
81 return seq.query(query, options)
82 }
83
84 setPluginField (pluginName: string, field: string, value: string) {
85 const seq = this.getSequelize()
86
87 const options = { type: QueryTypes.UPDATE }
88
89 return seq.query(`UPDATE "plugin" SET "${field}" = '${value}' WHERE "name" = '${pluginName}'`, options)
90 }
91
92 setPluginVersion (pluginName: string, newVersion: string) {
93 return this.setPluginField(pluginName, 'version', newVersion)
94 }
95
96 setPluginLatestVersion (pluginName: string, newVersion: string) {
97 return this.setPluginField(pluginName, 'latestVersion', newVersion)
98 }
99
100 setActorFollowScores (newScore: number) {
101 const seq = this.getSequelize()
102
103 const options = { type: QueryTypes.UPDATE }
104
105 return seq.query(`UPDATE "actorFollow" SET "score" = ${newScore}`, options)
106 }
107
108 setTokenField (accessToken: string, field: string, value: string) {
109 const seq = this.getSequelize()
110
111 const options = { type: QueryTypes.UPDATE }
112
113 return seq.query(`UPDATE "oAuthToken" SET "${field}" = '${value}' WHERE "accessToken" = '${accessToken}'`, options)
114 }
115
116 async cleanup () {
117 if (!this.sequelize) return
118
119 await this.sequelize.close()
120 this.sequelize = undefined
121 }
122
123 private getSequelize () {
124 if (this.sequelize) return this.sequelize
125
126 const dbname = 'peertube_test' + this.server.internalServerNumber
127 const username = 'peertube'
128 const password = 'peertube'
129 const host = 'localhost'
130 const port = 5432
131
132 this.sequelize = new Sequelize(dbname, username, password, {
133 dialect: 'postgres',
134 host,
135 port,
136 logging: false
137 })
138
139 return this.sequelize
140 }
141
142}
diff --git a/shared/extra-utils/miscs/sql.ts b/shared/extra-utils/miscs/sql.ts
deleted file mode 100644
index 65a0aa5fe..000000000
--- a/shared/extra-utils/miscs/sql.ts
+++ /dev/null
@@ -1,161 +0,0 @@
1import { QueryTypes, Sequelize } from 'sequelize'
2import { ServerInfo } from '../server/servers'
3
4const sequelizes: { [ id: number ]: Sequelize } = {}
5
6function getSequelize (internalServerNumber: number) {
7 if (sequelizes[internalServerNumber]) return sequelizes[internalServerNumber]
8
9 const dbname = 'peertube_test' + internalServerNumber
10 const username = 'peertube'
11 const password = 'peertube'
12 const host = 'localhost'
13 const port = 5432
14
15 const seq = new Sequelize(dbname, username, password, {
16 dialect: 'postgres',
17 host,
18 port,
19 logging: false
20 })
21
22 sequelizes[internalServerNumber] = seq
23
24 return seq
25}
26
27function deleteAll (internalServerNumber: number, table: string) {
28 const seq = getSequelize(internalServerNumber)
29
30 const options = { type: QueryTypes.DELETE }
31
32 return seq.query(`DELETE FROM "${table}"`, options)
33}
34
35async function getCount (internalServerNumber: number, table: string) {
36 const seq = getSequelize(internalServerNumber)
37
38 const options = { type: QueryTypes.SELECT as QueryTypes.SELECT }
39
40 const [ { total } ] = await seq.query<{ total: string }>(`SELECT COUNT(*) as total FROM "${table}"`, options)
41 if (total === null) return 0
42
43 return parseInt(total, 10)
44}
45
46function setActorField (internalServerNumber: number, to: string, field: string, value: string) {
47 const seq = getSequelize(internalServerNumber)
48
49 const options = { type: QueryTypes.UPDATE }
50
51 return seq.query(`UPDATE actor SET "${field}" = '${value}' WHERE url = '${to}'`, options)
52}
53
54function setVideoField (internalServerNumber: number, uuid: string, field: string, value: string) {
55 const seq = getSequelize(internalServerNumber)
56
57 const options = { type: QueryTypes.UPDATE }
58
59 return seq.query(`UPDATE video SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
60}
61
62function setPlaylistField (internalServerNumber: number, uuid: string, field: string, value: string) {
63 const seq = getSequelize(internalServerNumber)
64
65 const options = { type: QueryTypes.UPDATE }
66
67 return seq.query(`UPDATE "videoPlaylist" SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
68}
69
70async function countVideoViewsOf (internalServerNumber: number, uuid: string) {
71 const seq = getSequelize(internalServerNumber)
72
73 // tslint:disable
74 const query = 'SELECT SUM("videoView"."views") AS "total" FROM "videoView" ' +
75 `INNER JOIN "video" ON "video"."id" = "videoView"."videoId" WHERE "video"."uuid" = '${uuid}'`
76
77 const options = { type: QueryTypes.SELECT as QueryTypes.SELECT }
78 const [ { total } ] = await seq.query<{ total: number }>(query, options)
79
80 if (!total) return 0
81
82 return parseInt(total + '', 10)
83}
84
85function getActorImage (internalServerNumber: number, filename: string) {
86 return selectQuery(internalServerNumber, `SELECT * FROM "actorImage" WHERE filename = '${filename}'`)
87 .then(rows => rows[0])
88}
89
90function selectQuery (internalServerNumber: number, query: string) {
91 const seq = getSequelize(internalServerNumber)
92 const options = { type: QueryTypes.SELECT as QueryTypes.SELECT }
93
94 return seq.query<any>(query, options)
95}
96
97function updateQuery (internalServerNumber: number, query: string) {
98 const seq = getSequelize(internalServerNumber)
99 const options = { type: QueryTypes.UPDATE as QueryTypes.UPDATE }
100
101 return seq.query(query, options)
102}
103
104async function closeAllSequelize (servers: ServerInfo[]) {
105 for (const server of servers) {
106 if (sequelizes[server.internalServerNumber]) {
107 await sequelizes[server.internalServerNumber].close()
108 // eslint-disable-next-line
109 delete sequelizes[server.internalServerNumber]
110 }
111 }
112}
113
114function setPluginField (internalServerNumber: number, pluginName: string, field: string, value: string) {
115 const seq = getSequelize(internalServerNumber)
116
117 const options = { type: QueryTypes.UPDATE }
118
119 return seq.query(`UPDATE "plugin" SET "${field}" = '${value}' WHERE "name" = '${pluginName}'`, options)
120}
121
122function setPluginVersion (internalServerNumber: number, pluginName: string, newVersion: string) {
123 return setPluginField(internalServerNumber, pluginName, 'version', newVersion)
124}
125
126function setPluginLatestVersion (internalServerNumber: number, pluginName: string, newVersion: string) {
127 return setPluginField(internalServerNumber, pluginName, 'latestVersion', newVersion)
128}
129
130function setActorFollowScores (internalServerNumber: number, newScore: number) {
131 const seq = getSequelize(internalServerNumber)
132
133 const options = { type: QueryTypes.UPDATE }
134
135 return seq.query(`UPDATE "actorFollow" SET "score" = ${newScore}`, options)
136}
137
138function setTokenField (internalServerNumber: number, accessToken: string, field: string, value: string) {
139 const seq = getSequelize(internalServerNumber)
140
141 const options = { type: QueryTypes.UPDATE }
142
143 return seq.query(`UPDATE "oAuthToken" SET "${field}" = '${value}' WHERE "accessToken" = '${accessToken}'`, options)
144}
145
146export {
147 setVideoField,
148 setPlaylistField,
149 setActorField,
150 countVideoViewsOf,
151 setPluginVersion,
152 setPluginLatestVersion,
153 selectQuery,
154 getActorImage,
155 deleteAll,
156 setTokenField,
157 updateQuery,
158 setActorFollowScores,
159 closeAllSequelize,
160 getCount
161}
diff --git a/shared/extra-utils/miscs/stubs.ts b/shared/extra-utils/miscs/stubs.ts
deleted file mode 100644
index d1eb0e3b2..000000000
--- a/shared/extra-utils/miscs/stubs.ts
+++ /dev/null
@@ -1,14 +0,0 @@
1function buildRequestStub (): any {
2 return { }
3}
4
5function buildResponseStub (): any {
6 return {
7 locals: {}
8 }
9}
10
11export {
12 buildResponseStub,
13 buildRequestStub
14}
diff --git a/shared/extra-utils/miscs/tests.ts b/shared/extra-utils/miscs/tests.ts
new file mode 100644
index 000000000..3dfb2487e
--- /dev/null
+++ b/shared/extra-utils/miscs/tests.ts
@@ -0,0 +1,94 @@
1import { stat } from 'fs-extra'
2import { basename, isAbsolute, join, resolve } from 'path'
3
4const FIXTURE_URLS = {
5 youtube: 'https://www.youtube.com/watch?v=msX3jv1XdvM',
6
7 /**
8 * The video is used to check format-selection correctness wrt. HDR,
9 * which brings its own set of oddities outside of a MediaSource.
10 * FIXME: refactor once HDR is supported at playback
11 *
12 * The video needs to have the following format_ids:
13 * (which you can check by using `youtube-dl <url> -F`):
14 * - 303 (1080p webm vp9)
15 * - 299 (1080p mp4 avc1)
16 * - 335 (1080p webm vp9.2 HDR)
17 *
18 * 15 jan. 2021: TEST VIDEO NOT CURRENTLY PROVIDING
19 * - 400 (1080p mp4 av01)
20 * - 315 (2160p webm vp9 HDR)
21 * - 337 (2160p webm vp9.2 HDR)
22 * - 401 (2160p mp4 av01 HDR)
23 */
24 youtubeHDR: 'https://www.youtube.com/watch?v=qR5vOXbZsI4',
25
26 // eslint-disable-next-line max-len
27 magnet: 'magnet:?xs=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Ftorrents%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.torrent&xt=urn:btih:0f498834733e8057ed5c6f2ee2b4efd8d84a76ee&dn=super+peertube2+video&tr=wss%3A%2F%2Fpeertube2.cpy.re%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube2.cpy.re%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fwebseed%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.mp4',
28
29 badVideo: 'https://download.cpy.re/peertube/bad_video.mp4',
30 goodVideo: 'https://download.cpy.re/peertube/good_video.mp4',
31 video4K: 'https://download.cpy.re/peertube/4k_file.txt'
32}
33
34function parallelTests () {
35 return process.env.MOCHA_PARALLEL === 'true'
36}
37
38function isGithubCI () {
39 return !!process.env.GITHUB_WORKSPACE
40}
41
42function areHttpImportTestsDisabled () {
43 const disabled = process.env.DISABLE_HTTP_IMPORT_TESTS === 'true'
44
45 if (disabled) console.log('Import tests are disabled')
46
47 return disabled
48}
49
50function buildAbsoluteFixturePath (path: string, customCIPath = false) {
51 if (isAbsolute(path)) return path
52
53 if (customCIPath && process.env.GITHUB_WORKSPACE) {
54 return join(process.env.GITHUB_WORKSPACE, 'fixtures', path)
55 }
56
57 return join(root(), 'server', 'tests', 'fixtures', path)
58}
59
60function root () {
61 // We are in /miscs
62 let root = join(__dirname, '..', '..', '..')
63
64 if (basename(root) === 'dist') root = resolve(root, '..')
65
66 return root
67}
68
69function wait (milliseconds: number) {
70 return new Promise(resolve => setTimeout(resolve, milliseconds))
71}
72
73async function getFileSize (path: string) {
74 const stats = await stat(path)
75
76 return stats.size
77}
78
79function buildRequestStub (): any {
80 return { }
81}
82
83export {
84 FIXTURE_URLS,
85
86 parallelTests,
87 isGithubCI,
88 areHttpImportTestsDisabled,
89 buildAbsoluteFixturePath,
90 getFileSize,
91 buildRequestStub,
92 wait,
93 root
94}
diff --git a/shared/extra-utils/miscs/webtorrent.ts b/shared/extra-utils/miscs/webtorrent.ts
new file mode 100644
index 000000000..a1097effe
--- /dev/null
+++ b/shared/extra-utils/miscs/webtorrent.ts
@@ -0,0 +1,31 @@
1import { readFile } from 'fs-extra'
2import * as parseTorrent from 'parse-torrent'
3import { basename, join } from 'path'
4import * as WebTorrent from 'webtorrent'
5import { VideoFile } from '@shared/models'
6import { PeerTubeServer } from '../server'
7
8let webtorrent: WebTorrent.Instance
9
10function webtorrentAdd (torrent: string, refreshWebTorrent = false) {
11 const WebTorrent = require('webtorrent')
12
13 if (!webtorrent) webtorrent = new WebTorrent()
14 if (refreshWebTorrent === true) webtorrent = new WebTorrent()
15
16 return new Promise<WebTorrent.Torrent>(res => webtorrent.add(torrent, res))
17}
18
19async function parseTorrentVideo (server: PeerTubeServer, file: VideoFile) {
20 const torrentName = basename(file.torrentUrl)
21 const torrentPath = server.servers.buildDirectory(join('torrents', torrentName))
22
23 const data = await readFile(torrentPath)
24
25 return parseTorrent(data)
26}
27
28export {
29 webtorrentAdd,
30 parseTorrentVideo
31}
diff --git a/shared/extra-utils/mock-servers/index.ts b/shared/extra-utils/mock-servers/index.ts
new file mode 100644
index 000000000..0ec07f685
--- /dev/null
+++ b/shared/extra-utils/mock-servers/index.ts
@@ -0,0 +1,4 @@
1export * from './mock-email'
2export * from './mock-instances-index'
3export * from './mock-joinpeertube-versions'
4export * from './mock-plugin-blocklist'
diff --git a/shared/extra-utils/miscs/email.ts b/shared/extra-utils/mock-servers/mock-email.ts
index 9fc9a5ad0..ffd62e325 100644
--- a/shared/extra-utils/miscs/email.ts
+++ b/shared/extra-utils/mock-servers/mock-email.ts
@@ -1,6 +1,6 @@
1import { ChildProcess } from 'child_process' 1import { ChildProcess } from 'child_process'
2import { randomInt } from '../../core-utils/miscs/miscs' 2import { randomInt } from '@shared/core-utils'
3import { parallelTests } from '../server/servers' 3import { parallelTests } from '../miscs'
4 4
5const MailDev = require('maildev') 5const MailDev = require('maildev')
6 6
diff --git a/shared/extra-utils/mock-servers/joinpeertube-versions.ts b/shared/extra-utils/mock-servers/mock-joinpeertube-versions.ts
index 5ea432ecf..5ea432ecf 100644
--- a/shared/extra-utils/mock-servers/joinpeertube-versions.ts
+++ b/shared/extra-utils/mock-servers/mock-joinpeertube-versions.ts
diff --git a/shared/extra-utils/plugins/mock-blocklist.ts b/shared/extra-utils/mock-servers/mock-plugin-blocklist.ts
index d18f8224f..d18f8224f 100644
--- a/shared/extra-utils/plugins/mock-blocklist.ts
+++ b/shared/extra-utils/mock-servers/mock-plugin-blocklist.ts
diff --git a/shared/extra-utils/moderation/abuses-command.ts b/shared/extra-utils/moderation/abuses-command.ts
new file mode 100644
index 000000000..0db32ba46
--- /dev/null
+++ b/shared/extra-utils/moderation/abuses-command.ts
@@ -0,0 +1,228 @@
1import { pick } from '@shared/core-utils'
2import {
3 AbuseFilter,
4 AbuseMessage,
5 AbusePredefinedReasonsString,
6 AbuseState,
7 AbuseUpdate,
8 AbuseVideoIs,
9 AdminAbuse,
10 HttpStatusCode,
11 ResultList,
12 UserAbuse
13} from '@shared/models'
14import { unwrapBody } from '../requests/requests'
15import { AbstractCommand, OverrideCommandOptions } from '../shared'
16
17export class AbusesCommand extends AbstractCommand {
18
19 report (options: OverrideCommandOptions & {
20 reason: string
21
22 accountId?: number
23 videoId?: number
24 commentId?: number
25
26 predefinedReasons?: AbusePredefinedReasonsString[]
27
28 startAt?: number
29 endAt?: number
30 }) {
31 const path = '/api/v1/abuses'
32
33 const video = options.videoId
34 ? {
35 id: options.videoId,
36 startAt: options.startAt,
37 endAt: options.endAt
38 }
39 : undefined
40
41 const comment = options.commentId
42 ? { id: options.commentId }
43 : undefined
44
45 const account = options.accountId
46 ? { id: options.accountId }
47 : undefined
48
49 const body = {
50 account,
51 video,
52 comment,
53
54 reason: options.reason,
55 predefinedReasons: options.predefinedReasons
56 }
57
58 return unwrapBody<{ abuse: { id: number } }>(this.postBodyRequest({
59 ...options,
60
61 path,
62 fields: body,
63 implicitToken: true,
64 defaultExpectedStatus: HttpStatusCode.OK_200
65 }))
66 }
67
68 getAdminList (options: OverrideCommandOptions & {
69 start?: number
70 count?: number
71 sort?: string
72
73 id?: number
74 predefinedReason?: AbusePredefinedReasonsString
75 search?: string
76 filter?: AbuseFilter
77 state?: AbuseState
78 videoIs?: AbuseVideoIs
79 searchReporter?: string
80 searchReportee?: string
81 searchVideo?: string
82 searchVideoChannel?: string
83 } = {}) {
84 const toPick: (keyof typeof options)[] = [
85 'count',
86 'filter',
87 'id',
88 'predefinedReason',
89 'search',
90 'searchReportee',
91 'searchReporter',
92 'searchVideo',
93 'searchVideoChannel',
94 'sort',
95 'start',
96 'state',
97 'videoIs'
98 ]
99
100 const path = '/api/v1/abuses'
101
102 const defaultQuery = { sort: 'createdAt' }
103 const query = { ...defaultQuery, ...pick(options, toPick) }
104
105 return this.getRequestBody<ResultList<AdminAbuse>>({
106 ...options,
107
108 path,
109 query,
110 implicitToken: true,
111 defaultExpectedStatus: HttpStatusCode.OK_200
112 })
113 }
114
115 getUserList (options: OverrideCommandOptions & {
116 start?: number
117 count?: number
118 sort?: string
119
120 id?: number
121 search?: string
122 state?: AbuseState
123 }) {
124 const toPick: (keyof typeof options)[] = [
125 'id',
126 'search',
127 'state',
128 'start',
129 'count',
130 'sort'
131 ]
132
133 const path = '/api/v1/users/me/abuses'
134
135 const defaultQuery = { sort: 'createdAt' }
136 const query = { ...defaultQuery, ...pick(options, toPick) }
137
138 return this.getRequestBody<ResultList<UserAbuse>>({
139 ...options,
140
141 path,
142 query,
143 implicitToken: true,
144 defaultExpectedStatus: HttpStatusCode.OK_200
145 })
146 }
147
148 update (options: OverrideCommandOptions & {
149 abuseId: number
150 body: AbuseUpdate
151 }) {
152 const { abuseId, body } = options
153 const path = '/api/v1/abuses/' + abuseId
154
155 return this.putBodyRequest({
156 ...options,
157
158 path,
159 fields: body,
160 implicitToken: true,
161 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
162 })
163 }
164
165 delete (options: OverrideCommandOptions & {
166 abuseId: number
167 }) {
168 const { abuseId } = options
169 const path = '/api/v1/abuses/' + abuseId
170
171 return this.deleteRequest({
172 ...options,
173
174 path,
175 implicitToken: true,
176 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
177 })
178 }
179
180 listMessages (options: OverrideCommandOptions & {
181 abuseId: number
182 }) {
183 const { abuseId } = options
184 const path = '/api/v1/abuses/' + abuseId + '/messages'
185
186 return this.getRequestBody<ResultList<AbuseMessage>>({
187 ...options,
188
189 path,
190 implicitToken: true,
191 defaultExpectedStatus: HttpStatusCode.OK_200
192 })
193 }
194
195 deleteMessage (options: OverrideCommandOptions & {
196 abuseId: number
197 messageId: number
198 }) {
199 const { abuseId, messageId } = options
200 const path = '/api/v1/abuses/' + abuseId + '/messages/' + messageId
201
202 return this.deleteRequest({
203 ...options,
204
205 path,
206 implicitToken: true,
207 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
208 })
209 }
210
211 addMessage (options: OverrideCommandOptions & {
212 abuseId: number
213 message: string
214 }) {
215 const { abuseId, message } = options
216 const path = '/api/v1/abuses/' + abuseId + '/messages'
217
218 return this.postBodyRequest({
219 ...options,
220
221 path,
222 fields: { message },
223 implicitToken: true,
224 defaultExpectedStatus: HttpStatusCode.OK_200
225 })
226 }
227
228}
diff --git a/shared/extra-utils/moderation/abuses.ts b/shared/extra-utils/moderation/abuses.ts
deleted file mode 100644
index c0fda722f..000000000
--- a/shared/extra-utils/moderation/abuses.ts
+++ /dev/null
@@ -1,244 +0,0 @@
1import { AbuseFilter, AbusePredefinedReasonsString, AbuseState, AbuseUpdate, AbuseVideoIs } from '@shared/models'
2import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4
5function reportAbuse (options: {
6 url: string
7 token: string
8
9 reason: string
10
11 accountId?: number
12 videoId?: number
13 commentId?: number
14
15 predefinedReasons?: AbusePredefinedReasonsString[]
16
17 startAt?: number
18 endAt?: number
19
20 statusCodeExpected?: number
21}) {
22 const path = '/api/v1/abuses'
23
24 const video = options.videoId
25 ? {
26 id: options.videoId,
27 startAt: options.startAt,
28 endAt: options.endAt
29 }
30 : undefined
31
32 const comment = options.commentId
33 ? { id: options.commentId }
34 : undefined
35
36 const account = options.accountId
37 ? { id: options.accountId }
38 : undefined
39
40 const body = {
41 account,
42 video,
43 comment,
44
45 reason: options.reason,
46 predefinedReasons: options.predefinedReasons
47 }
48
49 return makePostBodyRequest({
50 url: options.url,
51 path,
52 token: options.token,
53
54 fields: body,
55 statusCodeExpected: options.statusCodeExpected || HttpStatusCode.OK_200
56 })
57}
58
59function getAdminAbusesList (options: {
60 url: string
61 token: string
62
63 start?: number
64 count?: number
65 sort?: string
66
67 id?: number
68 predefinedReason?: AbusePredefinedReasonsString
69 search?: string
70 filter?: AbuseFilter
71 state?: AbuseState
72 videoIs?: AbuseVideoIs
73 searchReporter?: string
74 searchReportee?: string
75 searchVideo?: string
76 searchVideoChannel?: string
77}) {
78 const {
79 url,
80 token,
81 start,
82 count,
83 sort,
84 id,
85 predefinedReason,
86 search,
87 filter,
88 state,
89 videoIs,
90 searchReporter,
91 searchReportee,
92 searchVideo,
93 searchVideoChannel
94 } = options
95 const path = '/api/v1/abuses'
96
97 const query = {
98 id,
99 predefinedReason,
100 search,
101 state,
102 filter,
103 videoIs,
104 start,
105 count,
106 sort: sort || 'createdAt',
107 searchReporter,
108 searchReportee,
109 searchVideo,
110 searchVideoChannel
111 }
112
113 return makeGetRequest({
114 url,
115 path,
116 token,
117 query,
118 statusCodeExpected: HttpStatusCode.OK_200
119 })
120}
121
122function getUserAbusesList (options: {
123 url: string
124 token: string
125
126 start?: number
127 count?: number
128 sort?: string
129
130 id?: number
131 search?: string
132 state?: AbuseState
133}) {
134 const {
135 url,
136 token,
137 start,
138 count,
139 sort,
140 id,
141 search,
142 state
143 } = options
144 const path = '/api/v1/users/me/abuses'
145
146 const query = {
147 id,
148 search,
149 state,
150 start,
151 count,
152 sort: sort || 'createdAt'
153 }
154
155 return makeGetRequest({
156 url,
157 path,
158 token,
159 query,
160 statusCodeExpected: HttpStatusCode.OK_200
161 })
162}
163
164function updateAbuse (
165 url: string,
166 token: string,
167 abuseId: number,
168 body: AbuseUpdate,
169 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
170) {
171 const path = '/api/v1/abuses/' + abuseId
172
173 return makePutBodyRequest({
174 url,
175 token,
176 path,
177 fields: body,
178 statusCodeExpected
179 })
180}
181
182function deleteAbuse (url: string, token: string, abuseId: number, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
183 const path = '/api/v1/abuses/' + abuseId
184
185 return makeDeleteRequest({
186 url,
187 token,
188 path,
189 statusCodeExpected
190 })
191}
192
193function listAbuseMessages (url: string, token: string, abuseId: number, statusCodeExpected = HttpStatusCode.OK_200) {
194 const path = '/api/v1/abuses/' + abuseId + '/messages'
195
196 return makeGetRequest({
197 url,
198 token,
199 path,
200 statusCodeExpected
201 })
202}
203
204function deleteAbuseMessage (
205 url: string,
206 token: string,
207 abuseId: number,
208 messageId: number,
209 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
210) {
211 const path = '/api/v1/abuses/' + abuseId + '/messages/' + messageId
212
213 return makeDeleteRequest({
214 url,
215 token,
216 path,
217 statusCodeExpected
218 })
219}
220
221function addAbuseMessage (url: string, token: string, abuseId: number, message: string, statusCodeExpected = HttpStatusCode.OK_200) {
222 const path = '/api/v1/abuses/' + abuseId + '/messages'
223
224 return makePostBodyRequest({
225 url,
226 token,
227 path,
228 fields: { message },
229 statusCodeExpected
230 })
231}
232
233// ---------------------------------------------------------------------------
234
235export {
236 reportAbuse,
237 getAdminAbusesList,
238 updateAbuse,
239 deleteAbuse,
240 getUserAbusesList,
241 listAbuseMessages,
242 deleteAbuseMessage,
243 addAbuseMessage
244}
diff --git a/shared/extra-utils/moderation/index.ts b/shared/extra-utils/moderation/index.ts
new file mode 100644
index 000000000..b37643956
--- /dev/null
+++ b/shared/extra-utils/moderation/index.ts
@@ -0,0 +1 @@
export * from './abuses-command'
diff --git a/shared/extra-utils/overviews/index.ts b/shared/extra-utils/overviews/index.ts
new file mode 100644
index 000000000..e19551907
--- /dev/null
+++ b/shared/extra-utils/overviews/index.ts
@@ -0,0 +1 @@
export * from './overviews-command'
diff --git a/shared/extra-utils/overviews/overviews-command.ts b/shared/extra-utils/overviews/overviews-command.ts
new file mode 100644
index 000000000..06b4892d2
--- /dev/null
+++ b/shared/extra-utils/overviews/overviews-command.ts
@@ -0,0 +1,23 @@
1import { HttpStatusCode, VideosOverview } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class OverviewsCommand extends AbstractCommand {
5
6 getVideos (options: OverrideCommandOptions & {
7 page: number
8 }) {
9 const { page } = options
10 const path = '/api/v1/overviews/videos'
11
12 const query = { page }
13
14 return this.getRequestBody<VideosOverview>({
15 ...options,
16
17 path,
18 query,
19 implicitToken: false,
20 defaultExpectedStatus: HttpStatusCode.OK_200
21 })
22 }
23}
diff --git a/shared/extra-utils/overviews/overviews.ts b/shared/extra-utils/overviews/overviews.ts
deleted file mode 100644
index 5e1a13e5e..000000000
--- a/shared/extra-utils/overviews/overviews.ts
+++ /dev/null
@@ -1,34 +0,0 @@
1import { makeGetRequest } from '../requests/requests'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function getVideosOverview (url: string, page: number, statusCodeExpected = HttpStatusCode.OK_200) {
5 const path = '/api/v1/overviews/videos'
6
7 const query = { page }
8
9 return makeGetRequest({
10 url,
11 path,
12 query,
13 statusCodeExpected
14 })
15}
16
17function getVideosOverviewWithToken (url: string, page: number, token: string, statusCodeExpected = HttpStatusCode.OK_200) {
18 const path = '/api/v1/overviews/videos'
19
20 const query = { page }
21
22 return makeGetRequest({
23 url,
24 path,
25 query,
26 token,
27 statusCodeExpected
28 })
29}
30
31export {
32 getVideosOverview,
33 getVideosOverviewWithToken
34}
diff --git a/shared/extra-utils/requests/activitypub.ts b/shared/extra-utils/requests/activitypub.ts
index ecd8ce823..4ae878384 100644
--- a/shared/extra-utils/requests/activitypub.ts
+++ b/shared/extra-utils/requests/activitypub.ts
@@ -1,7 +1,7 @@
1import { activityPubContextify } from '../../../server/helpers/activitypub'
1import { doRequest } from '../../../server/helpers/requests' 2import { doRequest } from '../../../server/helpers/requests'
2import { HTTP_SIGNATURE } from '../../../server/initializers/constants' 3import { HTTP_SIGNATURE } from '../../../server/initializers/constants'
3import { buildGlobalHeaders } from '../../../server/lib/job-queue/handlers/utils/activitypub-http-utils' 4import { buildGlobalHeaders } from '../../../server/lib/job-queue/handlers/utils/activitypub-http-utils'
4import { activityPubContextify } from '../../../server/helpers/activitypub'
5 5
6function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) { 6function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) {
7 const options = { 7 const options = {
diff --git a/shared/extra-utils/requests/check-api-params.ts b/shared/extra-utils/requests/check-api-params.ts
index 7f5ff775c..26ba1e913 100644
--- a/shared/extra-utils/requests/check-api-params.ts
+++ b/shared/extra-utils/requests/check-api-params.ts
@@ -1,14 +1,13 @@
1import { HttpStatusCode } from '@shared/models'
1import { makeGetRequest } from './requests' 2import { makeGetRequest } from './requests'
2import { immutableAssign } from '../miscs/miscs'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4 3
5function checkBadStartPagination (url: string, path: string, token?: string, query = {}) { 4function checkBadStartPagination (url: string, path: string, token?: string, query = {}) {
6 return makeGetRequest({ 5 return makeGetRequest({
7 url, 6 url,
8 path, 7 path,
9 token, 8 token,
10 query: immutableAssign(query, { start: 'hello' }), 9 query: { ...query, start: 'hello' },
11 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 10 expectedStatus: HttpStatusCode.BAD_REQUEST_400
12 }) 11 })
13} 12}
14 13
@@ -17,16 +16,16 @@ async function checkBadCountPagination (url: string, path: string, token?: strin
17 url, 16 url,
18 path, 17 path,
19 token, 18 token,
20 query: immutableAssign(query, { count: 'hello' }), 19 query: { ...query, count: 'hello' },
21 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 20 expectedStatus: HttpStatusCode.BAD_REQUEST_400
22 }) 21 })
23 22
24 await makeGetRequest({ 23 await makeGetRequest({
25 url, 24 url,
26 path, 25 path,
27 token, 26 token,
28 query: immutableAssign(query, { count: 2000 }), 27 query: { ...query, count: 2000 },
29 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 28 expectedStatus: HttpStatusCode.BAD_REQUEST_400
30 }) 29 })
31} 30}
32 31
@@ -35,8 +34,8 @@ function checkBadSortPagination (url: string, path: string, token?: string, quer
35 url, 34 url,
36 path, 35 path,
37 token, 36 token,
38 query: immutableAssign(query, { sort: 'hello' }), 37 query: { ...query, sort: 'hello' },
39 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 38 expectedStatus: HttpStatusCode.BAD_REQUEST_400
40 }) 39 })
41} 40}
42 41
diff --git a/shared/extra-utils/requests/index.ts b/shared/extra-utils/requests/index.ts
new file mode 100644
index 000000000..501163f92
--- /dev/null
+++ b/shared/extra-utils/requests/index.ts
@@ -0,0 +1,3 @@
1// Don't include activitypub that import stuff from server
2export * from './check-api-params'
3export * from './requests'
diff --git a/shared/extra-utils/requests/requests.ts b/shared/extra-utils/requests/requests.ts
index 38e24d897..70f790222 100644
--- a/shared/extra-utils/requests/requests.ts
+++ b/shared/extra-utils/requests/requests.ts
@@ -1,103 +1,82 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ 1/* eslint-disable @typescript-eslint/no-floating-promises */
2 2
3import { decode } from 'querystring'
3import * as request from 'supertest' 4import * as request from 'supertest'
4import { buildAbsoluteFixturePath, root } from '../miscs/miscs'
5import { isAbsolute, join } from 'path'
6import { URL } from 'url' 5import { URL } from 'url'
7import { decode } from 'querystring' 6import { HttpStatusCode } from '@shared/models'
8import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 7import { buildAbsoluteFixturePath } from '../miscs/tests'
9 8
10function get4KFileUrl () { 9export type CommonRequestParams = {
11 return 'https://download.cpy.re/peertube/4k_file.txt' 10 url: string
11 path?: string
12 contentType?: string
13 range?: string
14 redirects?: number
15 accept?: string
16 host?: string
17 token?: string
18 headers?: { [ name: string ]: string }
19 type?: string
20 xForwardedFor?: string
21 expectedStatus?: HttpStatusCode
12} 22}
13 23
14function makeRawRequest (url: string, statusCodeExpected?: HttpStatusCode, range?: string) { 24function makeRawRequest (url: string, expectedStatus?: HttpStatusCode, range?: string) {
15 const { host, protocol, pathname } = new URL(url) 25 const { host, protocol, pathname } = new URL(url)
16 26
17 return makeGetRequest({ url: `${protocol}//${host}`, path: pathname, statusCodeExpected, range }) 27 return makeGetRequest({ url: `${protocol}//${host}`, path: pathname, expectedStatus, range })
18} 28}
19 29
20function makeGetRequest (options: { 30function makeGetRequest (options: CommonRequestParams & {
21 url: string
22 path?: string
23 query?: any 31 query?: any
24 token?: string
25 statusCodeExpected?: HttpStatusCode
26 contentType?: string
27 range?: string
28 redirects?: number
29 accept?: string
30}) { 32}) {
31 if (!options.statusCodeExpected) options.statusCodeExpected = HttpStatusCode.BAD_REQUEST_400
32 if (options.contentType === undefined) options.contentType = 'application/json'
33
34 const req = request(options.url).get(options.path) 33 const req = request(options.url).get(options.path)
34 .query(options.query)
35 35
36 if (options.contentType) req.set('Accept', options.contentType) 36 return buildRequest(req, { contentType: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
37 if (options.token) req.set('Authorization', 'Bearer ' + options.token)
38 if (options.query) req.query(options.query)
39 if (options.range) req.set('Range', options.range)
40 if (options.accept) req.set('Accept', options.accept)
41 if (options.redirects) req.redirects(options.redirects)
42
43 return req.expect(options.statusCodeExpected)
44} 37}
45 38
46function makeDeleteRequest (options: { 39function makeHTMLRequest (url: string, path: string) {
47 url: string 40 return makeGetRequest({
48 path: string 41 url,
49 token?: string 42 path,
50 statusCodeExpected?: HttpStatusCode 43 accept: 'text/html',
51}) { 44 expectedStatus: HttpStatusCode.OK_200
52 if (!options.statusCodeExpected) options.statusCodeExpected = HttpStatusCode.BAD_REQUEST_400 45 })
46}
53 47
54 const req = request(options.url) 48function makeActivityPubGetRequest (url: string, path: string, expectedStatus = HttpStatusCode.OK_200) {
55 .delete(options.path) 49 return makeGetRequest({
56 .set('Accept', 'application/json') 50 url,
51 path,
52 expectedStatus: expectedStatus,
53 accept: 'application/activity+json,text/html;q=0.9,\\*/\\*;q=0.8'
54 })
55}
57 56
58 if (options.token) req.set('Authorization', 'Bearer ' + options.token) 57function makeDeleteRequest (options: CommonRequestParams) {
58 const req = request(options.url).delete(options.path)
59 59
60 return req.expect(options.statusCodeExpected) 60 return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
61} 61}
62 62
63function makeUploadRequest (options: { 63function makeUploadRequest (options: CommonRequestParams & {
64 url: string
65 method?: 'POST' | 'PUT' 64 method?: 'POST' | 'PUT'
66 path: string 65
67 token?: string
68 fields: { [ fieldName: string ]: any } 66 fields: { [ fieldName: string ]: any }
69 attaches?: { [ attachName: string ]: any | any[] } 67 attaches?: { [ attachName: string ]: any | any[] }
70 statusCodeExpected?: HttpStatusCode
71}) { 68}) {
72 if (!options.statusCodeExpected) options.statusCodeExpected = HttpStatusCode.BAD_REQUEST_400 69 let req = options.method === 'PUT'
73 70 ? request(options.url).put(options.path)
74 let req: request.Test 71 : request(options.url).post(options.path)
75 if (options.method === 'PUT') {
76 req = request(options.url).put(options.path)
77 } else {
78 req = request(options.url).post(options.path)
79 }
80
81 req.set('Accept', 'application/json')
82 72
83 if (options.token) req.set('Authorization', 'Bearer ' + options.token) 73 req = buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
84 74
85 Object.keys(options.fields).forEach(field => { 75 buildFields(req, options.fields)
86 const value = options.fields[field]
87
88 if (value === undefined) return
89
90 if (Array.isArray(value)) {
91 for (let i = 0; i < value.length; i++) {
92 req.field(field + '[' + i + ']', value[i])
93 }
94 } else {
95 req.field(field, value)
96 }
97 })
98 76
99 Object.keys(options.attaches || {}).forEach(attach => { 77 Object.keys(options.attaches || {}).forEach(attach => {
100 const value = options.attaches[attach] 78 const value = options.attaches[attach]
79
101 if (Array.isArray(value)) { 80 if (Array.isArray(value)) {
102 req.attach(attach, buildAbsoluteFixturePath(value[0]), value[1]) 81 req.attach(attach, buildAbsoluteFixturePath(value[0]), value[1])
103 } else { 82 } else {
@@ -105,27 +84,16 @@ function makeUploadRequest (options: {
105 } 84 }
106 }) 85 })
107 86
108 return req.expect(options.statusCodeExpected) 87 return req
109} 88}
110 89
111function makePostBodyRequest (options: { 90function makePostBodyRequest (options: CommonRequestParams & {
112 url: string
113 path: string
114 token?: string
115 fields?: { [ fieldName: string ]: any } 91 fields?: { [ fieldName: string ]: any }
116 statusCodeExpected?: HttpStatusCode
117}) { 92}) {
118 if (!options.fields) options.fields = {} 93 const req = request(options.url).post(options.path)
119 if (!options.statusCodeExpected) options.statusCodeExpected = HttpStatusCode.BAD_REQUEST_400 94 .send(options.fields)
120
121 const req = request(options.url)
122 .post(options.path)
123 .set('Accept', 'application/json')
124 95
125 if (options.token) req.set('Authorization', 'Bearer ' + options.token) 96 return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
126
127 return req.send(options.fields)
128 .expect(options.statusCodeExpected)
129} 97}
130 98
131function makePutBodyRequest (options: { 99function makePutBodyRequest (options: {
@@ -133,59 +101,29 @@ function makePutBodyRequest (options: {
133 path: string 101 path: string
134 token?: string 102 token?: string
135 fields: { [ fieldName: string ]: any } 103 fields: { [ fieldName: string ]: any }
136 statusCodeExpected?: HttpStatusCode 104 expectedStatus?: HttpStatusCode
137}) { 105}) {
138 if (!options.statusCodeExpected) options.statusCodeExpected = HttpStatusCode.BAD_REQUEST_400 106 const req = request(options.url).put(options.path)
139 107 .send(options.fields)
140 const req = request(options.url)
141 .put(options.path)
142 .set('Accept', 'application/json')
143 108
144 if (options.token) req.set('Authorization', 'Bearer ' + options.token) 109 return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
145
146 return req.send(options.fields)
147 .expect(options.statusCodeExpected)
148} 110}
149 111
150function makeHTMLRequest (url: string, path: string) { 112function decodeQueryString (path: string) {
151 return request(url) 113 return decode(path.split('?')[1])
152 .get(path)
153 .set('Accept', 'text/html')
154 .expect(HttpStatusCode.OK_200)
155} 114}
156 115
157function updateImageRequest (options: { 116function unwrapBody <T> (test: request.Test): Promise<T> {
158 url: string 117 return test.then(res => res.body)
159 path: string
160 accessToken: string
161 fixture: string
162 fieldname: string
163}) {
164 let filePath = ''
165 if (isAbsolute(options.fixture)) {
166 filePath = options.fixture
167 } else {
168 filePath = join(root(), 'server', 'tests', 'fixtures', options.fixture)
169 }
170
171 return makeUploadRequest({
172 url: options.url,
173 path: options.path,
174 token: options.accessToken,
175 fields: {},
176 attaches: { [options.fieldname]: filePath },
177 statusCodeExpected: HttpStatusCode.OK_200
178 })
179} 118}
180 119
181function decodeQueryString (path: string) { 120function unwrapText (test: request.Test): Promise<string> {
182 return decode(path.split('?')[1]) 121 return test.then(res => res.text)
183} 122}
184 123
185// --------------------------------------------------------------------------- 124// ---------------------------------------------------------------------------
186 125
187export { 126export {
188 get4KFileUrl,
189 makeHTMLRequest, 127 makeHTMLRequest,
190 makeGetRequest, 128 makeGetRequest,
191 decodeQueryString, 129 decodeQueryString,
@@ -194,5 +132,51 @@ export {
194 makePutBodyRequest, 132 makePutBodyRequest,
195 makeDeleteRequest, 133 makeDeleteRequest,
196 makeRawRequest, 134 makeRawRequest,
197 updateImageRequest 135 makeActivityPubGetRequest,
136 unwrapBody,
137 unwrapText
138}
139
140// ---------------------------------------------------------------------------
141
142function buildRequest (req: request.Test, options: CommonRequestParams) {
143 if (options.contentType) req.set('Accept', options.contentType)
144 if (options.token) req.set('Authorization', 'Bearer ' + options.token)
145 if (options.range) req.set('Range', options.range)
146 if (options.accept) req.set('Accept', options.accept)
147 if (options.host) req.set('Host', options.host)
148 if (options.redirects) req.redirects(options.redirects)
149 if (options.expectedStatus) req.expect(options.expectedStatus)
150 if (options.xForwardedFor) req.set('X-Forwarded-For', options.xForwardedFor)
151 if (options.type) req.type(options.type)
152
153 Object.keys(options.headers || {}).forEach(name => {
154 req.set(name, options.headers[name])
155 })
156
157 return req
158}
159
160function buildFields (req: request.Test, fields: { [ fieldName: string ]: any }, namespace?: string) {
161 if (!fields) return
162
163 let formKey: string
164
165 for (const key of Object.keys(fields)) {
166 if (namespace) formKey = `${namespace}[${key}]`
167 else formKey = key
168
169 if (fields[key] === undefined) continue
170
171 if (Array.isArray(fields[key]) && fields[key].length === 0) {
172 req.field(key, null)
173 continue
174 }
175
176 if (fields[key] !== null && typeof fields[key] === 'object') {
177 buildFields(req, fields[key], formKey)
178 } else {
179 req.field(formKey, fields[key])
180 }
181 }
198} 182}
diff --git a/shared/extra-utils/search/index.ts b/shared/extra-utils/search/index.ts
new file mode 100644
index 000000000..48dbe8ae9
--- /dev/null
+++ b/shared/extra-utils/search/index.ts
@@ -0,0 +1 @@
export * from './search-command'
diff --git a/shared/extra-utils/search/search-command.ts b/shared/extra-utils/search/search-command.ts
new file mode 100644
index 000000000..0fbbcd6ef
--- /dev/null
+++ b/shared/extra-utils/search/search-command.ts
@@ -0,0 +1,98 @@
1import {
2 HttpStatusCode,
3 ResultList,
4 Video,
5 VideoChannel,
6 VideoChannelsSearchQuery,
7 VideoPlaylist,
8 VideoPlaylistsSearchQuery,
9 VideosSearchQuery
10} from '@shared/models'
11import { AbstractCommand, OverrideCommandOptions } from '../shared'
12
13export class SearchCommand extends AbstractCommand {
14
15 searchChannels (options: OverrideCommandOptions & {
16 search: string
17 }) {
18 return this.advancedChannelSearch({
19 ...options,
20
21 search: { search: options.search }
22 })
23 }
24
25 advancedChannelSearch (options: OverrideCommandOptions & {
26 search: VideoChannelsSearchQuery
27 }) {
28 const { search } = options
29 const path = '/api/v1/search/video-channels'
30
31 return this.getRequestBody<ResultList<VideoChannel>>({
32 ...options,
33
34 path,
35 query: search,
36 implicitToken: false,
37 defaultExpectedStatus: HttpStatusCode.OK_200
38 })
39 }
40
41 searchPlaylists (options: OverrideCommandOptions & {
42 search: string
43 }) {
44 return this.advancedPlaylistSearch({
45 ...options,
46
47 search: { search: options.search }
48 })
49 }
50
51 advancedPlaylistSearch (options: OverrideCommandOptions & {
52 search: VideoPlaylistsSearchQuery
53 }) {
54 const { search } = options
55 const path = '/api/v1/search/video-playlists'
56
57 return this.getRequestBody<ResultList<VideoPlaylist>>({
58 ...options,
59
60 path,
61 query: search,
62 implicitToken: false,
63 defaultExpectedStatus: HttpStatusCode.OK_200
64 })
65 }
66
67 searchVideos (options: OverrideCommandOptions & {
68 search: string
69 sort?: string
70 }) {
71 const { search, sort } = options
72
73 return this.advancedVideoSearch({
74 ...options,
75
76 search: {
77 search: search,
78 sort: sort ?? '-publishedAt'
79 }
80 })
81 }
82
83 advancedVideoSearch (options: OverrideCommandOptions & {
84 search: VideosSearchQuery
85 }) {
86 const { search } = options
87 const path = '/api/v1/search/videos'
88
89 return this.getRequestBody<ResultList<Video>>({
90 ...options,
91
92 path,
93 query: search,
94 implicitToken: false,
95 defaultExpectedStatus: HttpStatusCode.OK_200
96 })
97 }
98}
diff --git a/shared/extra-utils/search/video-channels.ts b/shared/extra-utils/search/video-channels.ts
deleted file mode 100644
index 8e0f42578..000000000
--- a/shared/extra-utils/search/video-channels.ts
+++ /dev/null
@@ -1,36 +0,0 @@
1import { VideoChannelsSearchQuery } from '@shared/models'
2import { makeGetRequest } from '../requests/requests'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4
5function searchVideoChannel (url: string, search: string, token?: string, statusCodeExpected = HttpStatusCode.OK_200) {
6 const path = '/api/v1/search/video-channels'
7
8 return makeGetRequest({
9 url,
10 path,
11 query: {
12 sort: '-createdAt',
13 search
14 },
15 token,
16 statusCodeExpected
17 })
18}
19
20function advancedVideoChannelSearch (url: string, search: VideoChannelsSearchQuery) {
21 const path = '/api/v1/search/video-channels'
22
23 return makeGetRequest({
24 url,
25 path,
26 query: search,
27 statusCodeExpected: HttpStatusCode.OK_200
28 })
29}
30
31// ---------------------------------------------------------------------------
32
33export {
34 searchVideoChannel,
35 advancedVideoChannelSearch
36}
diff --git a/shared/extra-utils/search/video-playlists.ts b/shared/extra-utils/search/video-playlists.ts
deleted file mode 100644
index c22831df7..000000000
--- a/shared/extra-utils/search/video-playlists.ts
+++ /dev/null
@@ -1,36 +0,0 @@
1import { VideoPlaylistsSearchQuery } from '@shared/models'
2import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes'
3import { makeGetRequest } from '../requests/requests'
4
5function searchVideoPlaylists (url: string, search: string, token?: string, statusCodeExpected = HttpStatusCode.OK_200) {
6 const path = '/api/v1/search/video-playlists'
7
8 return makeGetRequest({
9 url,
10 path,
11 query: {
12 sort: '-createdAt',
13 search
14 },
15 token,
16 statusCodeExpected
17 })
18}
19
20function advancedVideoPlaylistSearch (url: string, search: VideoPlaylistsSearchQuery) {
21 const path = '/api/v1/search/video-playlists'
22
23 return makeGetRequest({
24 url,
25 path,
26 query: search,
27 statusCodeExpected: HttpStatusCode.OK_200
28 })
29}
30
31// ---------------------------------------------------------------------------
32
33export {
34 searchVideoPlaylists,
35 advancedVideoPlaylistSearch
36}
diff --git a/shared/extra-utils/search/videos.ts b/shared/extra-utils/search/videos.ts
deleted file mode 100644
index db6edbd58..000000000
--- a/shared/extra-utils/search/videos.ts
+++ /dev/null
@@ -1,64 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import * as request from 'supertest'
4import { VideosSearchQuery } from '../../models/search'
5import { immutableAssign } from '../miscs/miscs'
6import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
7
8function searchVideo (url: string, search: string, sort = '-publishedAt') {
9 const path = '/api/v1/search/videos'
10
11 const query = { sort, search: search }
12 const req = request(url)
13 .get(path)
14 .query(query)
15 .set('Accept', 'application/json')
16
17 return req.expect(HttpStatusCode.OK_200)
18 .expect('Content-Type', /json/)
19}
20
21function searchVideoWithToken (url: string, search: string, token: string, query: { nsfw?: boolean } = {}) {
22 const path = '/api/v1/search/videos'
23 const req = request(url)
24 .get(path)
25 .set('Authorization', 'Bearer ' + token)
26 .query(immutableAssign(query, { sort: '-publishedAt', search }))
27 .set('Accept', 'application/json')
28
29 return req.expect(HttpStatusCode.OK_200)
30 .expect('Content-Type', /json/)
31}
32
33function searchVideoWithSort (url: string, search: string, sort: string) {
34 const path = '/api/v1/search/videos'
35
36 const query = { search, sort }
37
38 return request(url)
39 .get(path)
40 .query(query)
41 .set('Accept', 'application/json')
42 .expect(HttpStatusCode.OK_200)
43 .expect('Content-Type', /json/)
44}
45
46function advancedVideosSearch (url: string, options: VideosSearchQuery) {
47 const path = '/api/v1/search/videos'
48
49 return request(url)
50 .get(path)
51 .query(options)
52 .set('Accept', 'application/json')
53 .expect(HttpStatusCode.OK_200)
54 .expect('Content-Type', /json/)
55}
56
57// ---------------------------------------------------------------------------
58
59export {
60 searchVideo,
61 advancedVideosSearch,
62 searchVideoWithToken,
63 searchVideoWithSort
64}
diff --git a/shared/extra-utils/server/activitypub.ts b/shared/extra-utils/server/activitypub.ts
deleted file mode 100644
index cf967ed7d..000000000
--- a/shared/extra-utils/server/activitypub.ts
+++ /dev/null
@@ -1,15 +0,0 @@
1import * as request from 'supertest'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function makeActivityPubGetRequest (url: string, path: string, expectedStatus = HttpStatusCode.OK_200) {
5 return request(url)
6 .get(path)
7 .set('Accept', 'application/activity+json,text/html;q=0.9,\\*/\\*;q=0.8')
8 .expect(expectedStatus)
9}
10
11// ---------------------------------------------------------------------------
12
13export {
14 makeActivityPubGetRequest
15}
diff --git a/shared/extra-utils/server/clients.ts b/shared/extra-utils/server/clients.ts
deleted file mode 100644
index 894fe4911..000000000
--- a/shared/extra-utils/server/clients.ts
+++ /dev/null
@@ -1,20 +0,0 @@
1import * as request from 'supertest'
2import { URL } from 'url'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4
5function getClient (url: string) {
6 const path = '/api/v1/oauth-clients/local'
7
8 return request(url)
9 .get(path)
10 .set('Host', new URL(url).host)
11 .set('Accept', 'application/json')
12 .expect(HttpStatusCode.OK_200)
13 .expect('Content-Type', /json/)
14}
15
16// ---------------------------------------------------------------------------
17
18export {
19 getClient
20}
diff --git a/shared/extra-utils/server/config-command.ts b/shared/extra-utils/server/config-command.ts
new file mode 100644
index 000000000..11148aa46
--- /dev/null
+++ b/shared/extra-utils/server/config-command.ts
@@ -0,0 +1,263 @@
1import { merge } from 'lodash'
2import { DeepPartial } from '@shared/core-utils'
3import { About, HttpStatusCode, ServerConfig } from '@shared/models'
4import { CustomConfig } from '../../models/server/custom-config.model'
5import { AbstractCommand, OverrideCommandOptions } from '../shared'
6
7export class ConfigCommand extends AbstractCommand {
8
9 static getCustomConfigResolutions (enabled: boolean) {
10 return {
11 '240p': enabled,
12 '360p': enabled,
13 '480p': enabled,
14 '720p': enabled,
15 '1080p': enabled,
16 '1440p': enabled,
17 '2160p': enabled
18 }
19 }
20
21 getConfig (options: OverrideCommandOptions = {}) {
22 const path = '/api/v1/config'
23
24 return this.getRequestBody<ServerConfig>({
25 ...options,
26
27 path,
28 implicitToken: false,
29 defaultExpectedStatus: HttpStatusCode.OK_200
30 })
31 }
32
33 getAbout (options: OverrideCommandOptions = {}) {
34 const path = '/api/v1/config/about'
35
36 return this.getRequestBody<About>({
37 ...options,
38
39 path,
40 implicitToken: false,
41 defaultExpectedStatus: HttpStatusCode.OK_200
42 })
43 }
44
45 getCustomConfig (options: OverrideCommandOptions = {}) {
46 const path = '/api/v1/config/custom'
47
48 return this.getRequestBody<CustomConfig>({
49 ...options,
50
51 path,
52 implicitToken: true,
53 defaultExpectedStatus: HttpStatusCode.OK_200
54 })
55 }
56
57 updateCustomConfig (options: OverrideCommandOptions & {
58 newCustomConfig: CustomConfig
59 }) {
60 const path = '/api/v1/config/custom'
61
62 return this.putBodyRequest({
63 ...options,
64
65 path,
66 fields: options.newCustomConfig,
67 implicitToken: true,
68 defaultExpectedStatus: HttpStatusCode.OK_200
69 })
70 }
71
72 deleteCustomConfig (options: OverrideCommandOptions = {}) {
73 const path = '/api/v1/config/custom'
74
75 return this.deleteRequest({
76 ...options,
77
78 path,
79 implicitToken: true,
80 defaultExpectedStatus: HttpStatusCode.OK_200
81 })
82 }
83
84 updateCustomSubConfig (options: OverrideCommandOptions & {
85 newConfig: DeepPartial<CustomConfig>
86 }) {
87 const newCustomConfig: CustomConfig = {
88 instance: {
89 name: 'PeerTube updated',
90 shortDescription: 'my short description',
91 description: 'my super description',
92 terms: 'my super terms',
93 codeOfConduct: 'my super coc',
94
95 creationReason: 'my super creation reason',
96 moderationInformation: 'my super moderation information',
97 administrator: 'Kuja',
98 maintenanceLifetime: 'forever',
99 businessModel: 'my super business model',
100 hardwareInformation: '2vCore 3GB RAM',
101
102 languages: [ 'en', 'es' ],
103 categories: [ 1, 2 ],
104
105 isNSFW: true,
106 defaultNSFWPolicy: 'blur',
107
108 defaultClientRoute: '/videos/recently-added',
109
110 customizations: {
111 javascript: 'alert("coucou")',
112 css: 'body { background-color: red; }'
113 }
114 },
115 theme: {
116 default: 'default'
117 },
118 services: {
119 twitter: {
120 username: '@MySuperUsername',
121 whitelisted: true
122 }
123 },
124 cache: {
125 previews: {
126 size: 2
127 },
128 captions: {
129 size: 3
130 },
131 torrents: {
132 size: 4
133 }
134 },
135 signup: {
136 enabled: false,
137 limit: 5,
138 requiresEmailVerification: false,
139 minimumAge: 16
140 },
141 admin: {
142 email: 'superadmin1@example.com'
143 },
144 contactForm: {
145 enabled: true
146 },
147 user: {
148 videoQuota: 5242881,
149 videoQuotaDaily: 318742
150 },
151 transcoding: {
152 enabled: true,
153 allowAdditionalExtensions: true,
154 allowAudioFiles: true,
155 threads: 1,
156 concurrency: 3,
157 profile: 'default',
158 resolutions: {
159 '0p': false,
160 '240p': false,
161 '360p': true,
162 '480p': true,
163 '720p': false,
164 '1080p': false,
165 '1440p': false,
166 '2160p': false
167 },
168 webtorrent: {
169 enabled: true
170 },
171 hls: {
172 enabled: false
173 }
174 },
175 live: {
176 enabled: true,
177 allowReplay: false,
178 maxDuration: -1,
179 maxInstanceLives: -1,
180 maxUserLives: 50,
181 transcoding: {
182 enabled: true,
183 threads: 4,
184 profile: 'default',
185 resolutions: {
186 '240p': true,
187 '360p': true,
188 '480p': true,
189 '720p': true,
190 '1080p': true,
191 '1440p': true,
192 '2160p': true
193 }
194 }
195 },
196 import: {
197 videos: {
198 concurrency: 3,
199 http: {
200 enabled: false
201 },
202 torrent: {
203 enabled: false
204 }
205 }
206 },
207 trending: {
208 videos: {
209 algorithms: {
210 enabled: [ 'best', 'hot', 'most-viewed', 'most-liked' ],
211 default: 'hot'
212 }
213 }
214 },
215 autoBlacklist: {
216 videos: {
217 ofUsers: {
218 enabled: false
219 }
220 }
221 },
222 followers: {
223 instance: {
224 enabled: true,
225 manualApproval: false
226 }
227 },
228 followings: {
229 instance: {
230 autoFollowBack: {
231 enabled: false
232 },
233 autoFollowIndex: {
234 indexUrl: 'https://instances.joinpeertube.org/api/v1/instances/hosts',
235 enabled: false
236 }
237 }
238 },
239 broadcastMessage: {
240 enabled: true,
241 level: 'warning',
242 message: 'hello',
243 dismissable: true
244 },
245 search: {
246 remoteUri: {
247 users: true,
248 anonymous: true
249 },
250 searchIndex: {
251 enabled: true,
252 url: 'https://search.joinpeertube.org',
253 disableLocalSearch: true,
254 isDefaultSearch: true
255 }
256 }
257 }
258
259 merge(newCustomConfig, options.newConfig)
260
261 return this.updateCustomConfig({ ...options, newCustomConfig })
262 }
263}
diff --git a/shared/extra-utils/server/config.ts b/shared/extra-utils/server/config.ts
deleted file mode 100644
index 9fcfb31fd..000000000
--- a/shared/extra-utils/server/config.ts
+++ /dev/null
@@ -1,260 +0,0 @@
1import { makeDeleteRequest, makeGetRequest, makePutBodyRequest } from '../requests/requests'
2import { CustomConfig } from '../../models/server/custom-config.model'
3import { DeepPartial, HttpStatusCode } from '@shared/core-utils'
4import { merge } from 'lodash'
5
6function getConfig (url: string) {
7 const path = '/api/v1/config'
8
9 return makeGetRequest({
10 url,
11 path,
12 statusCodeExpected: HttpStatusCode.OK_200
13 })
14}
15
16function getAbout (url: string) {
17 const path = '/api/v1/config/about'
18
19 return makeGetRequest({
20 url,
21 path,
22 statusCodeExpected: HttpStatusCode.OK_200
23 })
24}
25
26function getCustomConfig (url: string, token: string, statusCodeExpected = HttpStatusCode.OK_200) {
27 const path = '/api/v1/config/custom'
28
29 return makeGetRequest({
30 url,
31 token,
32 path,
33 statusCodeExpected
34 })
35}
36
37function updateCustomConfig (url: string, token: string, newCustomConfig: CustomConfig, statusCodeExpected = HttpStatusCode.OK_200) {
38 const path = '/api/v1/config/custom'
39
40 return makePutBodyRequest({
41 url,
42 token,
43 path,
44 fields: newCustomConfig,
45 statusCodeExpected
46 })
47}
48
49function updateCustomSubConfig (url: string, token: string, newConfig: DeepPartial<CustomConfig>) {
50 const updateParams: CustomConfig = {
51 instance: {
52 name: 'PeerTube updated',
53 shortDescription: 'my short description',
54 description: 'my super description',
55 terms: 'my super terms',
56 codeOfConduct: 'my super coc',
57
58 creationReason: 'my super creation reason',
59 moderationInformation: 'my super moderation information',
60 administrator: 'Kuja',
61 maintenanceLifetime: 'forever',
62 businessModel: 'my super business model',
63 hardwareInformation: '2vCore 3GB RAM',
64
65 languages: [ 'en', 'es' ],
66 categories: [ 1, 2 ],
67
68 isNSFW: true,
69 defaultNSFWPolicy: 'blur',
70
71 defaultClientRoute: '/videos/recently-added',
72
73 customizations: {
74 javascript: 'alert("coucou")',
75 css: 'body { background-color: red; }'
76 }
77 },
78 theme: {
79 default: 'default'
80 },
81 services: {
82 twitter: {
83 username: '@MySuperUsername',
84 whitelisted: true
85 }
86 },
87 cache: {
88 previews: {
89 size: 2
90 },
91 captions: {
92 size: 3
93 },
94 torrents: {
95 size: 4
96 }
97 },
98 signup: {
99 enabled: false,
100 limit: 5,
101 requiresEmailVerification: false,
102 minimumAge: 16
103 },
104 admin: {
105 email: 'superadmin1@example.com'
106 },
107 contactForm: {
108 enabled: true
109 },
110 user: {
111 videoQuota: 5242881,
112 videoQuotaDaily: 318742
113 },
114 transcoding: {
115 enabled: true,
116 allowAdditionalExtensions: true,
117 allowAudioFiles: true,
118 threads: 1,
119 concurrency: 3,
120 profile: 'default',
121 resolutions: {
122 '0p': false,
123 '240p': false,
124 '360p': true,
125 '480p': true,
126 '720p': false,
127 '1080p': false,
128 '1440p': false,
129 '2160p': false
130 },
131 webtorrent: {
132 enabled: true
133 },
134 hls: {
135 enabled: false
136 }
137 },
138 live: {
139 enabled: true,
140 allowReplay: false,
141 maxDuration: -1,
142 maxInstanceLives: -1,
143 maxUserLives: 50,
144 transcoding: {
145 enabled: true,
146 threads: 4,
147 profile: 'default',
148 resolutions: {
149 '240p': true,
150 '360p': true,
151 '480p': true,
152 '720p': true,
153 '1080p': true,
154 '1440p': true,
155 '2160p': true
156 }
157 }
158 },
159 import: {
160 videos: {
161 concurrency: 3,
162 http: {
163 enabled: false
164 },
165 torrent: {
166 enabled: false
167 }
168 }
169 },
170 trending: {
171 videos: {
172 algorithms: {
173 enabled: [ 'best', 'hot', 'most-viewed', 'most-liked' ],
174 default: 'hot'
175 }
176 }
177 },
178 autoBlacklist: {
179 videos: {
180 ofUsers: {
181 enabled: false
182 }
183 }
184 },
185 followers: {
186 instance: {
187 enabled: true,
188 manualApproval: false
189 }
190 },
191 followings: {
192 instance: {
193 autoFollowBack: {
194 enabled: false
195 },
196 autoFollowIndex: {
197 indexUrl: 'https://instances.joinpeertube.org/api/v1/instances/hosts',
198 enabled: false
199 }
200 }
201 },
202 broadcastMessage: {
203 enabled: true,
204 level: 'warning',
205 message: 'hello',
206 dismissable: true
207 },
208 search: {
209 remoteUri: {
210 users: true,
211 anonymous: true
212 },
213 searchIndex: {
214 enabled: true,
215 url: 'https://search.joinpeertube.org',
216 disableLocalSearch: true,
217 isDefaultSearch: true
218 }
219 }
220 }
221
222 merge(updateParams, newConfig)
223
224 return updateCustomConfig(url, token, updateParams)
225}
226
227function getCustomConfigResolutions (enabled: boolean) {
228 return {
229 '240p': enabled,
230 '360p': enabled,
231 '480p': enabled,
232 '720p': enabled,
233 '1080p': enabled,
234 '1440p': enabled,
235 '2160p': enabled
236 }
237}
238
239function deleteCustomConfig (url: string, token: string, statusCodeExpected = HttpStatusCode.OK_200) {
240 const path = '/api/v1/config/custom'
241
242 return makeDeleteRequest({
243 url,
244 token,
245 path,
246 statusCodeExpected
247 })
248}
249
250// ---------------------------------------------------------------------------
251
252export {
253 getConfig,
254 getCustomConfig,
255 updateCustomConfig,
256 getAbout,
257 deleteCustomConfig,
258 updateCustomSubConfig,
259 getCustomConfigResolutions
260}
diff --git a/shared/extra-utils/server/contact-form-command.ts b/shared/extra-utils/server/contact-form-command.ts
new file mode 100644
index 000000000..0e8fd6d84
--- /dev/null
+++ b/shared/extra-utils/server/contact-form-command.ts
@@ -0,0 +1,31 @@
1import { HttpStatusCode } from '@shared/models'
2import { ContactForm } from '../../models/server'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class ContactFormCommand extends AbstractCommand {
6
7 send (options: OverrideCommandOptions & {
8 fromEmail: string
9 fromName: string
10 subject: string
11 body: string
12 }) {
13 const path = '/api/v1/server/contact'
14
15 const body: ContactForm = {
16 fromEmail: options.fromEmail,
17 fromName: options.fromName,
18 subject: options.subject,
19 body: options.body
20 }
21
22 return this.postBodyRequest({
23 ...options,
24
25 path,
26 fields: body,
27 implicitToken: false,
28 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
29 })
30 }
31}
diff --git a/shared/extra-utils/server/contact-form.ts b/shared/extra-utils/server/contact-form.ts
deleted file mode 100644
index 6c9232cc6..000000000
--- a/shared/extra-utils/server/contact-form.ts
+++ /dev/null
@@ -1,31 +0,0 @@
1import * as request from 'supertest'
2import { ContactForm } from '../../models/server'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4
5function sendContactForm (options: {
6 url: string
7 fromEmail: string
8 fromName: string
9 subject: string
10 body: string
11 expectedStatus?: number
12}) {
13 const path = '/api/v1/server/contact'
14
15 const body: ContactForm = {
16 fromEmail: options.fromEmail,
17 fromName: options.fromName,
18 subject: options.subject,
19 body: options.body
20 }
21 return request(options.url)
22 .post(path)
23 .send(body)
24 .expect(options.expectedStatus || HttpStatusCode.NO_CONTENT_204)
25}
26
27// ---------------------------------------------------------------------------
28
29export {
30 sendContactForm
31}
diff --git a/shared/extra-utils/server/debug-command.ts b/shared/extra-utils/server/debug-command.ts
new file mode 100644
index 000000000..3c5a785bb
--- /dev/null
+++ b/shared/extra-utils/server/debug-command.ts
@@ -0,0 +1,33 @@
1import { Debug, HttpStatusCode, SendDebugCommand } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class DebugCommand extends AbstractCommand {
5
6 getDebug (options: OverrideCommandOptions = {}) {
7 const path = '/api/v1/server/debug'
8
9 return this.getRequestBody<Debug>({
10 ...options,
11
12 path,
13 implicitToken: true,
14 defaultExpectedStatus: HttpStatusCode.OK_200
15 })
16 }
17
18 sendCommand (options: OverrideCommandOptions & {
19 body: SendDebugCommand
20 }) {
21 const { body } = options
22 const path = '/api/v1/server/debug/run-command'
23
24 return this.postBodyRequest({
25 ...options,
26
27 path,
28 fields: body,
29 implicitToken: true,
30 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
31 })
32 }
33}
diff --git a/shared/extra-utils/server/debug.ts b/shared/extra-utils/server/debug.ts
deleted file mode 100644
index f196812b7..000000000
--- a/shared/extra-utils/server/debug.ts
+++ /dev/null
@@ -1,33 +0,0 @@
1import { makeGetRequest, makePostBodyRequest } from '../requests/requests'
2import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes'
3import { SendDebugCommand } from '@shared/models'
4
5function getDebug (url: string, token: string) {
6 const path = '/api/v1/server/debug'
7
8 return makeGetRequest({
9 url,
10 path,
11 token,
12 statusCodeExpected: HttpStatusCode.OK_200
13 })
14}
15
16function sendDebugCommand (url: string, token: string, body: SendDebugCommand) {
17 const path = '/api/v1/server/debug/run-command'
18
19 return makePostBodyRequest({
20 url,
21 path,
22 token,
23 fields: body,
24 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
25 })
26}
27
28// ---------------------------------------------------------------------------
29
30export {
31 getDebug,
32 sendDebugCommand
33}
diff --git a/shared/extra-utils/server/directories.ts b/shared/extra-utils/server/directories.ts
new file mode 100644
index 000000000..b6465cbf4
--- /dev/null
+++ b/shared/extra-utils/server/directories.ts
@@ -0,0 +1,34 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { pathExists, readdir } from 'fs-extra'
5import { join } from 'path'
6import { root } from '@server/helpers/core-utils'
7import { PeerTubeServer } from './server'
8
9async function checkTmpIsEmpty (server: PeerTubeServer) {
10 await checkDirectoryIsEmpty(server, 'tmp', [ 'plugins-global.css', 'hls', 'resumable-uploads' ])
11
12 if (await pathExists(join('test' + server.internalServerNumber, 'tmp', 'hls'))) {
13 await checkDirectoryIsEmpty(server, 'tmp/hls')
14 }
15}
16
17async function checkDirectoryIsEmpty (server: PeerTubeServer, directory: string, exceptions: string[] = []) {
18 const testDirectory = 'test' + server.internalServerNumber
19
20 const directoryPath = join(root(), testDirectory, directory)
21
22 const directoryExists = await pathExists(directoryPath)
23 expect(directoryExists).to.be.true
24
25 const files = await readdir(directoryPath)
26 const filtered = files.filter(f => exceptions.includes(f) === false)
27
28 expect(filtered).to.have.lengthOf(0)
29}
30
31export {
32 checkTmpIsEmpty,
33 checkDirectoryIsEmpty
34}
diff --git a/shared/extra-utils/server/follows-command.ts b/shared/extra-utils/server/follows-command.ts
new file mode 100644
index 000000000..01ef6f179
--- /dev/null
+++ b/shared/extra-utils/server/follows-command.ts
@@ -0,0 +1,139 @@
1import { pick } from '@shared/core-utils'
2import { ActivityPubActorType, ActorFollow, FollowState, HttpStatusCode, ResultList, ServerFollowCreate } from '@shared/models'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4import { PeerTubeServer } from './server'
5
6export class FollowsCommand extends AbstractCommand {
7
8 getFollowers (options: OverrideCommandOptions & {
9 start: number
10 count: number
11 sort: string
12 search?: string
13 actorType?: ActivityPubActorType
14 state?: FollowState
15 }) {
16 const path = '/api/v1/server/followers'
17
18 const query = pick(options, [ 'start', 'count', 'sort', 'search', 'state', 'actorType' ])
19
20 return this.getRequestBody<ResultList<ActorFollow>>({
21 ...options,
22
23 path,
24 query,
25 implicitToken: false,
26 defaultExpectedStatus: HttpStatusCode.OK_200
27 })
28 }
29
30 getFollowings (options: OverrideCommandOptions & {
31 start?: number
32 count?: number
33 sort?: string
34 search?: string
35 actorType?: ActivityPubActorType
36 state?: FollowState
37 } = {}) {
38 const path = '/api/v1/server/following'
39
40 const query = pick(options, [ 'start', 'count', 'sort', 'search', 'state', 'actorType' ])
41
42 return this.getRequestBody<ResultList<ActorFollow>>({
43 ...options,
44
45 path,
46 query,
47 implicitToken: false,
48 defaultExpectedStatus: HttpStatusCode.OK_200
49 })
50 }
51
52 follow (options: OverrideCommandOptions & {
53 hosts?: string[]
54 handles?: string[]
55 }) {
56 const path = '/api/v1/server/following'
57
58 const fields: ServerFollowCreate = {}
59
60 if (options.hosts) {
61 fields.hosts = options.hosts.map(f => f.replace(/^http:\/\//, ''))
62 }
63
64 if (options.handles) {
65 fields.handles = options.handles
66 }
67
68 return this.postBodyRequest({
69 ...options,
70
71 path,
72 fields,
73 implicitToken: true,
74 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
75 })
76 }
77
78 async unfollow (options: OverrideCommandOptions & {
79 target: PeerTubeServer | string
80 }) {
81 const { target } = options
82
83 const handle = typeof target === 'string'
84 ? target
85 : target.host
86
87 const path = '/api/v1/server/following/' + handle
88
89 return this.deleteRequest({
90 ...options,
91
92 path,
93 implicitToken: true,
94 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
95 })
96 }
97
98 acceptFollower (options: OverrideCommandOptions & {
99 follower: string
100 }) {
101 const path = '/api/v1/server/followers/' + options.follower + '/accept'
102
103 return this.postBodyRequest({
104 ...options,
105
106 path,
107 implicitToken: true,
108 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
109 })
110 }
111
112 rejectFollower (options: OverrideCommandOptions & {
113 follower: string
114 }) {
115 const path = '/api/v1/server/followers/' + options.follower + '/reject'
116
117 return this.postBodyRequest({
118 ...options,
119
120 path,
121 implicitToken: true,
122 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
123 })
124 }
125
126 removeFollower (options: OverrideCommandOptions & {
127 follower: PeerTubeServer
128 }) {
129 const path = '/api/v1/server/followers/peertube@' + options.follower.host
130
131 return this.deleteRequest({
132 ...options,
133
134 path,
135 implicitToken: true,
136 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
137 })
138 }
139}
diff --git a/shared/extra-utils/server/follows.ts b/shared/extra-utils/server/follows.ts
index 6aae4a31d..698238f29 100644
--- a/shared/extra-utils/server/follows.ts
+++ b/shared/extra-utils/server/follows.ts
@@ -1,126 +1,10 @@
1import * as request from 'supertest'
2import { ServerInfo } from './servers'
3import { waitJobs } from './jobs' 1import { waitJobs } from './jobs'
4import { makePostBodyRequest } from '../requests/requests' 2import { PeerTubeServer } from './server'
5import { ActivityPubActorType, FollowState } from '@shared/models'
6import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
7 3
8function getFollowersListPaginationAndSort (options: { 4async function doubleFollow (server1: PeerTubeServer, server2: PeerTubeServer) {
9 url: string
10 start: number
11 count: number
12 sort: string
13 search?: string
14 actorType?: ActivityPubActorType
15 state?: FollowState
16}) {
17 const { url, start, count, sort, search, state, actorType } = options
18 const path = '/api/v1/server/followers'
19
20 const query = {
21 start,
22 count,
23 sort,
24 search,
25 state,
26 actorType
27 }
28
29 return request(url)
30 .get(path)
31 .query(query)
32 .set('Accept', 'application/json')
33 .expect(HttpStatusCode.OK_200)
34 .expect('Content-Type', /json/)
35}
36
37function acceptFollower (url: string, token: string, follower: string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
38 const path = '/api/v1/server/followers/' + follower + '/accept'
39
40 return makePostBodyRequest({
41 url,
42 token,
43 path,
44 statusCodeExpected
45 })
46}
47
48function rejectFollower (url: string, token: string, follower: string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
49 const path = '/api/v1/server/followers/' + follower + '/reject'
50
51 return makePostBodyRequest({
52 url,
53 token,
54 path,
55 statusCodeExpected
56 })
57}
58
59function getFollowingListPaginationAndSort (options: {
60 url: string
61 start: number
62 count: number
63 sort: string
64 search?: string
65 actorType?: ActivityPubActorType
66 state?: FollowState
67}) {
68 const { url, start, count, sort, search, state, actorType } = options
69 const path = '/api/v1/server/following'
70
71 const query = {
72 start,
73 count,
74 sort,
75 search,
76 state,
77 actorType
78 }
79
80 return request(url)
81 .get(path)
82 .query(query)
83 .set('Accept', 'application/json')
84 .expect(HttpStatusCode.OK_200)
85 .expect('Content-Type', /json/)
86}
87
88function follow (follower: string, following: string[], accessToken: string, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
89 const path = '/api/v1/server/following'
90
91 const followingHosts = following.map(f => f.replace(/^http:\/\//, ''))
92 return request(follower)
93 .post(path)
94 .set('Accept', 'application/json')
95 .set('Authorization', 'Bearer ' + accessToken)
96 .send({ hosts: followingHosts })
97 .expect(expectedStatus)
98}
99
100async function unfollow (url: string, accessToken: string, target: ServerInfo, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
101 const path = '/api/v1/server/following/' + target.host
102
103 return request(url)
104 .delete(path)
105 .set('Accept', 'application/json')
106 .set('Authorization', 'Bearer ' + accessToken)
107 .expect(expectedStatus)
108}
109
110function removeFollower (url: string, accessToken: string, follower: ServerInfo, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
111 const path = '/api/v1/server/followers/peertube@' + follower.host
112
113 return request(url)
114 .delete(path)
115 .set('Accept', 'application/json')
116 .set('Authorization', 'Bearer ' + accessToken)
117 .expect(expectedStatus)
118}
119
120async function doubleFollow (server1: ServerInfo, server2: ServerInfo) {
121 await Promise.all([ 5 await Promise.all([
122 follow(server1.url, [ server2.url ], server1.accessToken), 6 server1.follows.follow({ hosts: [ server2.url ] }),
123 follow(server2.url, [ server1.url ], server2.accessToken) 7 server2.follows.follow({ hosts: [ server1.url ] })
124 ]) 8 ])
125 9
126 // Wait request propagation 10 // Wait request propagation
@@ -132,12 +16,5 @@ async function doubleFollow (server1: ServerInfo, server2: ServerInfo) {
132// --------------------------------------------------------------------------- 16// ---------------------------------------------------------------------------
133 17
134export { 18export {
135 getFollowersListPaginationAndSort, 19 doubleFollow
136 getFollowingListPaginationAndSort,
137 unfollow,
138 removeFollower,
139 follow,
140 doubleFollow,
141 acceptFollower,
142 rejectFollower
143} 20}
diff --git a/shared/extra-utils/server/index.ts b/shared/extra-utils/server/index.ts
new file mode 100644
index 000000000..9055dfc57
--- /dev/null
+++ b/shared/extra-utils/server/index.ts
@@ -0,0 +1,15 @@
1export * from './config-command'
2export * from './contact-form-command'
3export * from './debug-command'
4export * from './directories'
5export * from './follows-command'
6export * from './follows'
7export * from './jobs'
8export * from './jobs-command'
9export * from './plugins-command'
10export * from './plugins'
11export * from './redundancy-command'
12export * from './server'
13export * from './servers-command'
14export * from './servers'
15export * from './stats-command'
diff --git a/shared/extra-utils/server/jobs-command.ts b/shared/extra-utils/server/jobs-command.ts
new file mode 100644
index 000000000..c4eb12dc2
--- /dev/null
+++ b/shared/extra-utils/server/jobs-command.ts
@@ -0,0 +1,36 @@
1import { pick } from '@shared/core-utils'
2import { HttpStatusCode } from '@shared/models'
3import { Job, JobState, JobType, ResultList } from '../../models'
4import { AbstractCommand, OverrideCommandOptions } from '../shared'
5
6export class JobsCommand extends AbstractCommand {
7
8 getJobsList (options: OverrideCommandOptions & {
9 state?: JobState
10 jobType?: JobType
11 start?: number
12 count?: number
13 sort?: string
14 } = {}) {
15 const path = this.buildJobsUrl(options.state)
16
17 const query = pick(options, [ 'start', 'count', 'sort', 'jobType' ])
18
19 return this.getRequestBody<ResultList<Job>>({
20 ...options,
21
22 path,
23 query,
24 implicitToken: true,
25 defaultExpectedStatus: HttpStatusCode.OK_200
26 })
27 }
28
29 private buildJobsUrl (state?: JobState) {
30 let path = '/api/v1/jobs'
31
32 if (state) path += '/' + state
33
34 return path
35 }
36}
diff --git a/shared/extra-utils/server/jobs.ts b/shared/extra-utils/server/jobs.ts
index 763374e03..64a0353eb 100644
--- a/shared/extra-utils/server/jobs.ts
+++ b/shared/extra-utils/server/jobs.ts
@@ -1,66 +1,17 @@
1import * as request from 'supertest'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3import { getDebug, makeGetRequest } from '../../../shared/extra-utils'
4import { Job, JobState, JobType, ServerDebug } from '../../models'
5import { wait } from '../miscs/miscs'
6import { ServerInfo } from './servers'
7 1
8function buildJobsUrl (state?: JobState) { 2import { JobState } from '../../models'
9 let path = '/api/v1/jobs' 3import { wait } from '../miscs'
4import { PeerTubeServer } from './server'
10 5
11 if (state) path += '/' + state 6async function waitJobs (serversArg: PeerTubeServer[] | PeerTubeServer) {
12
13 return path
14}
15
16function getJobsList (url: string, accessToken: string, state?: JobState) {
17 const path = buildJobsUrl(state)
18
19 return request(url)
20 .get(path)
21 .set('Accept', 'application/json')
22 .set('Authorization', 'Bearer ' + accessToken)
23 .expect(HttpStatusCode.OK_200)
24 .expect('Content-Type', /json/)
25}
26
27function getJobsListPaginationAndSort (options: {
28 url: string
29 accessToken: string
30 start: number
31 count: number
32 sort: string
33 state?: JobState
34 jobType?: JobType
35}) {
36 const { url, accessToken, state, start, count, sort, jobType } = options
37 const path = buildJobsUrl(state)
38
39 const query = {
40 start,
41 count,
42 sort,
43 jobType
44 }
45
46 return makeGetRequest({
47 url,
48 path,
49 token: accessToken,
50 statusCodeExpected: HttpStatusCode.OK_200,
51 query
52 })
53}
54
55async function waitJobs (serversArg: ServerInfo[] | ServerInfo) {
56 const pendingJobWait = process.env.NODE_PENDING_JOB_WAIT 7 const pendingJobWait = process.env.NODE_PENDING_JOB_WAIT
57 ? parseInt(process.env.NODE_PENDING_JOB_WAIT, 10) 8 ? parseInt(process.env.NODE_PENDING_JOB_WAIT, 10)
58 : 250 9 : 250
59 10
60 let servers: ServerInfo[] 11 let servers: PeerTubeServer[]
61 12
62 if (Array.isArray(serversArg) === false) servers = [ serversArg as ServerInfo ] 13 if (Array.isArray(serversArg) === false) servers = [ serversArg as PeerTubeServer ]
63 else servers = serversArg as ServerInfo[] 14 else servers = serversArg as PeerTubeServer[]
64 15
65 const states: JobState[] = [ 'waiting', 'active', 'delayed' ] 16 const states: JobState[] = [ 'waiting', 'active', 'delayed' ]
66 const repeatableJobs = [ 'videos-views', 'activitypub-cleaner' ] 17 const repeatableJobs = [ 'videos-views', 'activitypub-cleaner' ]
@@ -72,15 +23,13 @@ async function waitJobs (serversArg: ServerInfo[] | ServerInfo) {
72 // Check if each server has pending request 23 // Check if each server has pending request
73 for (const server of servers) { 24 for (const server of servers) {
74 for (const state of states) { 25 for (const state of states) {
75 const p = getJobsListPaginationAndSort({ 26 const p = server.jobs.getJobsList({
76 url: server.url, 27 state,
77 accessToken: server.accessToken,
78 state: state,
79 start: 0, 28 start: 0,
80 count: 10, 29 count: 10,
81 sort: '-createdAt' 30 sort: '-createdAt'
82 }).then(res => res.body.data) 31 }).then(body => body.data)
83 .then((jobs: Job[]) => jobs.filter(j => !repeatableJobs.includes(j.type))) 32 .then(jobs => jobs.filter(j => !repeatableJobs.includes(j.type)))
84 .then(jobs => { 33 .then(jobs => {
85 if (jobs.length !== 0) { 34 if (jobs.length !== 0) {
86 pendingRequests = true 35 pendingRequests = true
@@ -90,9 +39,8 @@ async function waitJobs (serversArg: ServerInfo[] | ServerInfo) {
90 tasks.push(p) 39 tasks.push(p)
91 } 40 }
92 41
93 const p = getDebug(server.url, server.accessToken) 42 const p = server.debug.getDebug()
94 .then(res => res.body) 43 .then(obj => {
95 .then((obj: ServerDebug) => {
96 if (obj.activityPubMessagesWaiting !== 0) { 44 if (obj.activityPubMessagesWaiting !== 0) {
97 pendingRequests = true 45 pendingRequests = true
98 } 46 }
@@ -123,7 +71,5 @@ async function waitJobs (serversArg: ServerInfo[] | ServerInfo) {
123// --------------------------------------------------------------------------- 71// ---------------------------------------------------------------------------
124 72
125export { 73export {
126 getJobsList, 74 waitJobs
127 waitJobs,
128 getJobsListPaginationAndSort
129} 75}
diff --git a/shared/extra-utils/server/plugins-command.ts b/shared/extra-utils/server/plugins-command.ts
new file mode 100644
index 000000000..b944475a2
--- /dev/null
+++ b/shared/extra-utils/server/plugins-command.ts
@@ -0,0 +1,256 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { readJSON, writeJSON } from 'fs-extra'
4import { join } from 'path'
5import { root } from '@server/helpers/core-utils'
6import {
7 HttpStatusCode,
8 PeerTubePlugin,
9 PeerTubePluginIndex,
10 PeertubePluginIndexList,
11 PluginPackageJson,
12 PluginTranslation,
13 PluginType,
14 PublicServerSetting,
15 RegisteredServerSettings,
16 ResultList
17} from '@shared/models'
18import { AbstractCommand, OverrideCommandOptions } from '../shared'
19
20export class PluginsCommand extends AbstractCommand {
21
22 static getPluginTestPath (suffix = '') {
23 return join(root(), 'server', 'tests', 'fixtures', 'peertube-plugin-test' + suffix)
24 }
25
26 list (options: OverrideCommandOptions & {
27 start?: number
28 count?: number
29 sort?: string
30 pluginType?: PluginType
31 uninstalled?: boolean
32 }) {
33 const { start, count, sort, pluginType, uninstalled } = options
34 const path = '/api/v1/plugins'
35
36 return this.getRequestBody<ResultList<PeerTubePlugin>>({
37 ...options,
38
39 path,
40 query: {
41 start,
42 count,
43 sort,
44 pluginType,
45 uninstalled
46 },
47 implicitToken: true,
48 defaultExpectedStatus: HttpStatusCode.OK_200
49 })
50 }
51
52 listAvailable (options: OverrideCommandOptions & {
53 start?: number
54 count?: number
55 sort?: string
56 pluginType?: PluginType
57 currentPeerTubeEngine?: string
58 search?: string
59 expectedStatus?: HttpStatusCode
60 }) {
61 const { start, count, sort, pluginType, search, currentPeerTubeEngine } = options
62 const path = '/api/v1/plugins/available'
63
64 const query: PeertubePluginIndexList = {
65 start,
66 count,
67 sort,
68 pluginType,
69 currentPeerTubeEngine,
70 search
71 }
72
73 return this.getRequestBody<ResultList<PeerTubePluginIndex>>({
74 ...options,
75
76 path,
77 query,
78 implicitToken: true,
79 defaultExpectedStatus: HttpStatusCode.OK_200
80 })
81 }
82
83 get (options: OverrideCommandOptions & {
84 npmName: string
85 }) {
86 const path = '/api/v1/plugins/' + options.npmName
87
88 return this.getRequestBody<PeerTubePlugin>({
89 ...options,
90
91 path,
92 implicitToken: true,
93 defaultExpectedStatus: HttpStatusCode.OK_200
94 })
95 }
96
97 updateSettings (options: OverrideCommandOptions & {
98 npmName: string
99 settings: any
100 }) {
101 const { npmName, settings } = options
102 const path = '/api/v1/plugins/' + npmName + '/settings'
103
104 return this.putBodyRequest({
105 ...options,
106
107 path,
108 fields: { settings },
109 implicitToken: true,
110 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
111 })
112 }
113
114 getRegisteredSettings (options: OverrideCommandOptions & {
115 npmName: string
116 }) {
117 const path = '/api/v1/plugins/' + options.npmName + '/registered-settings'
118
119 return this.getRequestBody<RegisteredServerSettings>({
120 ...options,
121
122 path,
123 implicitToken: true,
124 defaultExpectedStatus: HttpStatusCode.OK_200
125 })
126 }
127
128 getPublicSettings (options: OverrideCommandOptions & {
129 npmName: string
130 }) {
131 const { npmName } = options
132 const path = '/api/v1/plugins/' + npmName + '/public-settings'
133
134 return this.getRequestBody<PublicServerSetting>({
135 ...options,
136
137 path,
138 implicitToken: false,
139 defaultExpectedStatus: HttpStatusCode.OK_200
140 })
141 }
142
143 getTranslations (options: OverrideCommandOptions & {
144 locale: string
145 }) {
146 const { locale } = options
147 const path = '/plugins/translations/' + locale + '.json'
148
149 return this.getRequestBody<PluginTranslation>({
150 ...options,
151
152 path,
153 implicitToken: false,
154 defaultExpectedStatus: HttpStatusCode.OK_200
155 })
156 }
157
158 install (options: OverrideCommandOptions & {
159 path?: string
160 npmName?: string
161 }) {
162 const { npmName, path } = options
163 const apiPath = '/api/v1/plugins/install'
164
165 return this.postBodyRequest({
166 ...options,
167
168 path: apiPath,
169 fields: { npmName, path },
170 implicitToken: true,
171 defaultExpectedStatus: HttpStatusCode.OK_200
172 })
173 }
174
175 update (options: OverrideCommandOptions & {
176 path?: string
177 npmName?: string
178 }) {
179 const { npmName, path } = options
180 const apiPath = '/api/v1/plugins/update'
181
182 return this.postBodyRequest({
183 ...options,
184
185 path: apiPath,
186 fields: { npmName, path },
187 implicitToken: true,
188 defaultExpectedStatus: HttpStatusCode.OK_200
189 })
190 }
191
192 uninstall (options: OverrideCommandOptions & {
193 npmName: string
194 }) {
195 const { npmName } = options
196 const apiPath = '/api/v1/plugins/uninstall'
197
198 return this.postBodyRequest({
199 ...options,
200
201 path: apiPath,
202 fields: { npmName },
203 implicitToken: true,
204 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
205 })
206 }
207
208 getCSS (options: OverrideCommandOptions = {}) {
209 const path = '/plugins/global.css'
210
211 return this.getRequestText({
212 ...options,
213
214 path,
215 implicitToken: false,
216 defaultExpectedStatus: HttpStatusCode.OK_200
217 })
218 }
219
220 getExternalAuth (options: OverrideCommandOptions & {
221 npmName: string
222 npmVersion: string
223 authName: string
224 query?: any
225 }) {
226 const { npmName, npmVersion, authName, query } = options
227
228 const path = '/plugins/' + npmName + '/' + npmVersion + '/auth/' + authName
229
230 return this.getRequest({
231 ...options,
232
233 path,
234 query,
235 implicitToken: false,
236 defaultExpectedStatus: HttpStatusCode.OK_200,
237 redirects: 0
238 })
239 }
240
241 updatePackageJSON (npmName: string, json: any) {
242 const path = this.getPackageJSONPath(npmName)
243
244 return writeJSON(path, json)
245 }
246
247 getPackageJSON (npmName: string): Promise<PluginPackageJson> {
248 const path = this.getPackageJSONPath(npmName)
249
250 return readJSON(path)
251 }
252
253 private getPackageJSONPath (npmName: string) {
254 return this.server.servers.buildDirectory(join('plugins', 'node_modules', npmName, 'package.json'))
255 }
256}
diff --git a/shared/extra-utils/server/plugins.ts b/shared/extra-utils/server/plugins.ts
index d53e5b382..0f5fabd5a 100644
--- a/shared/extra-utils/server/plugins.ts
+++ b/shared/extra-utils/server/plugins.ts
@@ -1,307 +1,18 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { readJSON, writeJSON } from 'fs-extra' 4import { PeerTubeServer } from '../server/server'
5import { join } from 'path'
6import { RegisteredServerSettings } from '@shared/models'
7import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
8import { PeertubePluginIndexList } from '../../models/plugins/plugin-index/peertube-plugin-index-list.model'
9import { PluginType } from '../../models/plugins/plugin.type'
10import { buildServerDirectory, root } from '../miscs/miscs'
11import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
12import { ServerInfo } from './servers'
13 5
14function listPlugins (parameters: { 6async function testHelloWorldRegisteredSettings (server: PeerTubeServer) {
15 url: string 7 const body = await server.plugins.getRegisteredSettings({ npmName: 'peertube-plugin-hello-world' })
16 accessToken: string
17 start?: number
18 count?: number
19 sort?: string
20 pluginType?: PluginType
21 uninstalled?: boolean
22 expectedStatus?: HttpStatusCode
23}) {
24 const { url, accessToken, start, count, sort, pluginType, uninstalled, expectedStatus = HttpStatusCode.OK_200 } = parameters
25 const path = '/api/v1/plugins'
26
27 return makeGetRequest({
28 url,
29 path,
30 token: accessToken,
31 query: {
32 start,
33 count,
34 sort,
35 pluginType,
36 uninstalled
37 },
38 statusCodeExpected: expectedStatus
39 })
40}
41
42function listAvailablePlugins (parameters: {
43 url: string
44 accessToken: string
45 start?: number
46 count?: number
47 sort?: string
48 pluginType?: PluginType
49 currentPeerTubeEngine?: string
50 search?: string
51 expectedStatus?: HttpStatusCode
52}) {
53 const {
54 url,
55 accessToken,
56 start,
57 count,
58 sort,
59 pluginType,
60 search,
61 currentPeerTubeEngine,
62 expectedStatus = HttpStatusCode.OK_200
63 } = parameters
64 const path = '/api/v1/plugins/available'
65
66 const query: PeertubePluginIndexList = {
67 start,
68 count,
69 sort,
70 pluginType,
71 currentPeerTubeEngine,
72 search
73 }
74
75 return makeGetRequest({
76 url,
77 path,
78 token: accessToken,
79 query,
80 statusCodeExpected: expectedStatus
81 })
82}
83
84function getPlugin (parameters: {
85 url: string
86 accessToken: string
87 npmName: string
88 expectedStatus?: HttpStatusCode
89}) {
90 const { url, accessToken, npmName, expectedStatus = HttpStatusCode.OK_200 } = parameters
91 const path = '/api/v1/plugins/' + npmName
92
93 return makeGetRequest({
94 url,
95 path,
96 token: accessToken,
97 statusCodeExpected: expectedStatus
98 })
99}
100
101function updatePluginSettings (parameters: {
102 url: string
103 accessToken: string
104 npmName: string
105 settings: any
106 expectedStatus?: HttpStatusCode
107}) {
108 const { url, accessToken, npmName, settings, expectedStatus = HttpStatusCode.NO_CONTENT_204 } = parameters
109 const path = '/api/v1/plugins/' + npmName + '/settings'
110
111 return makePutBodyRequest({
112 url,
113 path,
114 token: accessToken,
115 fields: { settings },
116 statusCodeExpected: expectedStatus
117 })
118}
119
120function getPluginRegisteredSettings (parameters: {
121 url: string
122 accessToken: string
123 npmName: string
124 expectedStatus?: HttpStatusCode
125}) {
126 const { url, accessToken, npmName, expectedStatus = HttpStatusCode.OK_200 } = parameters
127 const path = '/api/v1/plugins/' + npmName + '/registered-settings'
128
129 return makeGetRequest({
130 url,
131 path,
132 token: accessToken,
133 statusCodeExpected: expectedStatus
134 })
135}
136
137async function testHelloWorldRegisteredSettings (server: ServerInfo) {
138 const res = await getPluginRegisteredSettings({
139 url: server.url,
140 accessToken: server.accessToken,
141 npmName: 'peertube-plugin-hello-world'
142 })
143
144 const registeredSettings = (res.body as RegisteredServerSettings).registeredSettings
145 8
9 const registeredSettings = body.registeredSettings
146 expect(registeredSettings).to.have.length.at.least(1) 10 expect(registeredSettings).to.have.length.at.least(1)
147 11
148 const adminNameSettings = registeredSettings.find(s => s.name === 'admin-name') 12 const adminNameSettings = registeredSettings.find(s => s.name === 'admin-name')
149 expect(adminNameSettings).to.not.be.undefined 13 expect(adminNameSettings).to.not.be.undefined
150} 14}
151 15
152function getPublicSettings (parameters: {
153 url: string
154 npmName: string
155 expectedStatus?: HttpStatusCode
156}) {
157 const { url, npmName, expectedStatus = HttpStatusCode.OK_200 } = parameters
158 const path = '/api/v1/plugins/' + npmName + '/public-settings'
159
160 return makeGetRequest({
161 url,
162 path,
163 statusCodeExpected: expectedStatus
164 })
165}
166
167function getPluginTranslations (parameters: {
168 url: string
169 locale: string
170 expectedStatus?: HttpStatusCode
171}) {
172 const { url, locale, expectedStatus = HttpStatusCode.OK_200 } = parameters
173 const path = '/plugins/translations/' + locale + '.json'
174
175 return makeGetRequest({
176 url,
177 path,
178 statusCodeExpected: expectedStatus
179 })
180}
181
182function installPlugin (parameters: {
183 url: string
184 accessToken: string
185 path?: string
186 npmName?: string
187 expectedStatus?: HttpStatusCode
188}) {
189 const { url, accessToken, npmName, path, expectedStatus = HttpStatusCode.OK_200 } = parameters
190 const apiPath = '/api/v1/plugins/install'
191
192 return makePostBodyRequest({
193 url,
194 path: apiPath,
195 token: accessToken,
196 fields: { npmName, path },
197 statusCodeExpected: expectedStatus
198 })
199}
200
201function updatePlugin (parameters: {
202 url: string
203 accessToken: string
204 path?: string
205 npmName?: string
206 expectedStatus?: HttpStatusCode
207}) {
208 const { url, accessToken, npmName, path, expectedStatus = HttpStatusCode.OK_200 } = parameters
209 const apiPath = '/api/v1/plugins/update'
210
211 return makePostBodyRequest({
212 url,
213 path: apiPath,
214 token: accessToken,
215 fields: { npmName, path },
216 statusCodeExpected: expectedStatus
217 })
218}
219
220function uninstallPlugin (parameters: {
221 url: string
222 accessToken: string
223 npmName: string
224 expectedStatus?: HttpStatusCode
225}) {
226 const { url, accessToken, npmName, expectedStatus = HttpStatusCode.NO_CONTENT_204 } = parameters
227 const apiPath = '/api/v1/plugins/uninstall'
228
229 return makePostBodyRequest({
230 url,
231 path: apiPath,
232 token: accessToken,
233 fields: { npmName },
234 statusCodeExpected: expectedStatus
235 })
236}
237
238function getPluginsCSS (url: string) {
239 const path = '/plugins/global.css'
240
241 return makeGetRequest({
242 url,
243 path,
244 statusCodeExpected: HttpStatusCode.OK_200
245 })
246}
247
248function getPackageJSONPath (server: ServerInfo, npmName: string) {
249 return buildServerDirectory(server, join('plugins', 'node_modules', npmName, 'package.json'))
250}
251
252function updatePluginPackageJSON (server: ServerInfo, npmName: string, json: any) {
253 const path = getPackageJSONPath(server, npmName)
254
255 return writeJSON(path, json)
256}
257
258function getPluginPackageJSON (server: ServerInfo, npmName: string) {
259 const path = getPackageJSONPath(server, npmName)
260
261 return readJSON(path)
262}
263
264function getPluginTestPath (suffix = '') {
265 return join(root(), 'server', 'tests', 'fixtures', 'peertube-plugin-test' + suffix)
266}
267
268function getExternalAuth (options: {
269 url: string
270 npmName: string
271 npmVersion: string
272 authName: string
273 query?: any
274 statusCodeExpected?: HttpStatusCode
275}) {
276 const { url, npmName, npmVersion, authName, statusCodeExpected, query } = options
277
278 const path = '/plugins/' + npmName + '/' + npmVersion + '/auth/' + authName
279
280 return makeGetRequest({
281 url,
282 path,
283 query,
284 statusCodeExpected: statusCodeExpected || HttpStatusCode.OK_200,
285 redirects: 0
286 })
287}
288
289export { 16export {
290 listPlugins, 17 testHelloWorldRegisteredSettings
291 listAvailablePlugins,
292 installPlugin,
293 getPluginTranslations,
294 getPluginsCSS,
295 updatePlugin,
296 getPlugin,
297 uninstallPlugin,
298 testHelloWorldRegisteredSettings,
299 updatePluginSettings,
300 getPluginRegisteredSettings,
301 getPackageJSONPath,
302 updatePluginPackageJSON,
303 getPluginPackageJSON,
304 getPluginTestPath,
305 getPublicSettings,
306 getExternalAuth
307} 18}
diff --git a/shared/extra-utils/server/redundancy-command.ts b/shared/extra-utils/server/redundancy-command.ts
new file mode 100644
index 000000000..e7a8b3c29
--- /dev/null
+++ b/shared/extra-utils/server/redundancy-command.ts
@@ -0,0 +1,80 @@
1import { HttpStatusCode, ResultList, VideoRedundanciesTarget, VideoRedundancy } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class RedundancyCommand extends AbstractCommand {
5
6 updateRedundancy (options: OverrideCommandOptions & {
7 host: string
8 redundancyAllowed: boolean
9 }) {
10 const { host, redundancyAllowed } = options
11 const path = '/api/v1/server/redundancy/' + host
12
13 return this.putBodyRequest({
14 ...options,
15
16 path,
17 fields: { redundancyAllowed },
18 implicitToken: true,
19 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
20 })
21 }
22
23 listVideos (options: OverrideCommandOptions & {
24 target: VideoRedundanciesTarget
25 start?: number
26 count?: number
27 sort?: string
28 }) {
29 const path = '/api/v1/server/redundancy/videos'
30
31 const { target, start, count, sort } = options
32
33 return this.getRequestBody<ResultList<VideoRedundancy>>({
34 ...options,
35
36 path,
37
38 query: {
39 start: start ?? 0,
40 count: count ?? 5,
41 sort: sort ?? 'name',
42 target
43 },
44
45 implicitToken: true,
46 defaultExpectedStatus: HttpStatusCode.OK_200
47 })
48 }
49
50 addVideo (options: OverrideCommandOptions & {
51 videoId: number
52 }) {
53 const path = '/api/v1/server/redundancy/videos'
54 const { videoId } = options
55
56 return this.postBodyRequest({
57 ...options,
58
59 path,
60 fields: { videoId },
61 implicitToken: true,
62 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
63 })
64 }
65
66 removeVideo (options: OverrideCommandOptions & {
67 redundancyId: number
68 }) {
69 const { redundancyId } = options
70 const path = '/api/v1/server/redundancy/videos/' + redundancyId
71
72 return this.deleteRequest({
73 ...options,
74
75 path,
76 implicitToken: true,
77 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
78 })
79 }
80}
diff --git a/shared/extra-utils/server/redundancy.ts b/shared/extra-utils/server/redundancy.ts
deleted file mode 100644
index b83815a37..000000000
--- a/shared/extra-utils/server/redundancy.ts
+++ /dev/null
@@ -1,88 +0,0 @@
1import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
2import { VideoRedundanciesTarget } from '@shared/models'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4
5function updateRedundancy (
6 url: string,
7 accessToken: string,
8 host: string,
9 redundancyAllowed: boolean,
10 expectedStatus = HttpStatusCode.NO_CONTENT_204
11) {
12 const path = '/api/v1/server/redundancy/' + host
13
14 return makePutBodyRequest({
15 url,
16 path,
17 token: accessToken,
18 fields: { redundancyAllowed },
19 statusCodeExpected: expectedStatus
20 })
21}
22
23function listVideoRedundancies (options: {
24 url: string
25 accessToken: string
26 target: VideoRedundanciesTarget
27 start?: number
28 count?: number
29 sort?: string
30 statusCodeExpected?: HttpStatusCode
31}) {
32 const path = '/api/v1/server/redundancy/videos'
33
34 const { url, accessToken, target, statusCodeExpected, start, count, sort } = options
35
36 return makeGetRequest({
37 url,
38 token: accessToken,
39 path,
40 query: {
41 start: start ?? 0,
42 count: count ?? 5,
43 sort: sort ?? 'name',
44 target
45 },
46 statusCodeExpected: statusCodeExpected || HttpStatusCode.OK_200
47 })
48}
49
50function addVideoRedundancy (options: {
51 url: string
52 accessToken: string
53 videoId: number
54}) {
55 const path = '/api/v1/server/redundancy/videos'
56 const { url, accessToken, videoId } = options
57
58 return makePostBodyRequest({
59 url,
60 token: accessToken,
61 path,
62 fields: { videoId },
63 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
64 })
65}
66
67function removeVideoRedundancy (options: {
68 url: string
69 accessToken: string
70 redundancyId: number
71}) {
72 const { url, accessToken, redundancyId } = options
73 const path = '/api/v1/server/redundancy/videos/' + redundancyId
74
75 return makeDeleteRequest({
76 url,
77 token: accessToken,
78 path,
79 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
80 })
81}
82
83export {
84 updateRedundancy,
85 listVideoRedundancies,
86 addVideoRedundancy,
87 removeVideoRedundancy
88}
diff --git a/shared/extra-utils/server/server.ts b/shared/extra-utils/server/server.ts
new file mode 100644
index 000000000..3c335b8e4
--- /dev/null
+++ b/shared/extra-utils/server/server.ts
@@ -0,0 +1,369 @@
1import { ChildProcess, fork } from 'child_process'
2import { copy } from 'fs-extra'
3import { join } from 'path'
4import { root } from '@server/helpers/core-utils'
5import { randomInt } from '@shared/core-utils'
6import { Video, VideoChannel, VideoCreateResult, VideoDetails } from '../../models/videos'
7import { BulkCommand } from '../bulk'
8import { CLICommand } from '../cli'
9import { CustomPagesCommand } from '../custom-pages'
10import { FeedCommand } from '../feeds'
11import { LogsCommand } from '../logs'
12import { parallelTests, SQLCommand } from '../miscs'
13import { AbusesCommand } from '../moderation'
14import { OverviewsCommand } from '../overviews'
15import { SearchCommand } from '../search'
16import { SocketIOCommand } from '../socket'
17import { AccountsCommand, BlocklistCommand, LoginCommand, NotificationsCommand, SubscriptionsCommand, UsersCommand } from '../users'
18import {
19 BlacklistCommand,
20 CaptionsCommand,
21 ChangeOwnershipCommand,
22 ChannelsCommand,
23 HistoryCommand,
24 ImportsCommand,
25 LiveCommand,
26 PlaylistsCommand,
27 ServicesCommand,
28 StreamingPlaylistsCommand,
29 VideosCommand
30} from '../videos'
31import { CommentsCommand } from '../videos/comments-command'
32import { ConfigCommand } from './config-command'
33import { ContactFormCommand } from './contact-form-command'
34import { DebugCommand } from './debug-command'
35import { FollowsCommand } from './follows-command'
36import { JobsCommand } from './jobs-command'
37import { PluginsCommand } from './plugins-command'
38import { RedundancyCommand } from './redundancy-command'
39import { ServersCommand } from './servers-command'
40import { StatsCommand } from './stats-command'
41
42export type RunServerOptions = {
43 hideLogs?: boolean
44 nodeArgs?: string[]
45 peertubeArgs?: string[]
46}
47
48export class PeerTubeServer {
49 app?: ChildProcess
50
51 url: string
52 host?: string
53 hostname?: string
54 port?: number
55
56 rtmpPort?: number
57
58 parallel?: boolean
59 internalServerNumber: number
60
61 serverNumber?: number
62 customConfigFile?: string
63
64 store?: {
65 client?: {
66 id?: string
67 secret?: string
68 }
69
70 user?: {
71 username: string
72 password: string
73 email?: string
74 }
75
76 channel?: VideoChannel
77
78 video?: Video
79 videoCreated?: VideoCreateResult
80 videoDetails?: VideoDetails
81
82 videos?: { id: number, uuid: string }[]
83 }
84
85 accessToken?: string
86 refreshToken?: string
87
88 bulk?: BulkCommand
89 cli?: CLICommand
90 customPage?: CustomPagesCommand
91 feed?: FeedCommand
92 logs?: LogsCommand
93 abuses?: AbusesCommand
94 overviews?: OverviewsCommand
95 search?: SearchCommand
96 contactForm?: ContactFormCommand
97 debug?: DebugCommand
98 follows?: FollowsCommand
99 jobs?: JobsCommand
100 plugins?: PluginsCommand
101 redundancy?: RedundancyCommand
102 stats?: StatsCommand
103 config?: ConfigCommand
104 socketIO?: SocketIOCommand
105 accounts?: AccountsCommand
106 blocklist?: BlocklistCommand
107 subscriptions?: SubscriptionsCommand
108 live?: LiveCommand
109 services?: ServicesCommand
110 blacklist?: BlacklistCommand
111 captions?: CaptionsCommand
112 changeOwnership?: ChangeOwnershipCommand
113 playlists?: PlaylistsCommand
114 history?: HistoryCommand
115 imports?: ImportsCommand
116 streamingPlaylists?: StreamingPlaylistsCommand
117 channels?: ChannelsCommand
118 comments?: CommentsCommand
119 sql?: SQLCommand
120 notifications?: NotificationsCommand
121 servers?: ServersCommand
122 login?: LoginCommand
123 users?: UsersCommand
124 videos?: VideosCommand
125
126 constructor (options: { serverNumber: number } | { url: string }) {
127 if ((options as any).url) {
128 this.setUrl((options as any).url)
129 } else {
130 this.setServerNumber((options as any).serverNumber)
131 }
132
133 this.store = {
134 client: {
135 id: null,
136 secret: null
137 },
138 user: {
139 username: null,
140 password: null
141 }
142 }
143
144 this.assignCommands()
145 }
146
147 setServerNumber (serverNumber: number) {
148 this.serverNumber = serverNumber
149
150 this.parallel = parallelTests()
151
152 this.internalServerNumber = this.parallel ? this.randomServer() : this.serverNumber
153 this.rtmpPort = this.parallel ? this.randomRTMP() : 1936
154 this.port = 9000 + this.internalServerNumber
155
156 this.url = `http://localhost:${this.port}`
157 this.host = `localhost:${this.port}`
158 this.hostname = 'localhost'
159 }
160
161 setUrl (url: string) {
162 const parsed = new URL(url)
163
164 this.url = url
165 this.host = parsed.host
166 this.hostname = parsed.hostname
167 this.port = parseInt(parsed.port)
168 }
169
170 async flushAndRun (configOverride?: Object, options: RunServerOptions = {}) {
171 await ServersCommand.flushTests(this.internalServerNumber)
172
173 return this.run(configOverride, options)
174 }
175
176 async run (configOverrideArg?: any, options: RunServerOptions = {}) {
177 // These actions are async so we need to be sure that they have both been done
178 const serverRunString = {
179 'HTTP server listening': false
180 }
181 const key = 'Database peertube_test' + this.internalServerNumber + ' is ready'
182 serverRunString[key] = false
183
184 const regexps = {
185 client_id: 'Client id: (.+)',
186 client_secret: 'Client secret: (.+)',
187 user_username: 'Username: (.+)',
188 user_password: 'User password: (.+)'
189 }
190
191 await this.assignCustomConfigFile()
192
193 const configOverride = this.buildConfigOverride()
194
195 if (configOverrideArg !== undefined) {
196 Object.assign(configOverride, configOverrideArg)
197 }
198
199 // Share the environment
200 const env = Object.create(process.env)
201 env['NODE_ENV'] = 'test'
202 env['NODE_APP_INSTANCE'] = this.internalServerNumber.toString()
203 env['NODE_CONFIG'] = JSON.stringify(configOverride)
204
205 const forkOptions = {
206 silent: true,
207 env,
208 detached: true,
209 execArgv: options.nodeArgs || []
210 }
211
212 return new Promise<void>(res => {
213 const self = this
214
215 this.app = fork(join(root(), 'dist', 'server.js'), options.peertubeArgs || [], forkOptions)
216 this.app.stdout.on('data', function onStdout (data) {
217 let dontContinue = false
218
219 // Capture things if we want to
220 for (const key of Object.keys(regexps)) {
221 const regexp = regexps[key]
222 const matches = data.toString().match(regexp)
223 if (matches !== null) {
224 if (key === 'client_id') self.store.client.id = matches[1]
225 else if (key === 'client_secret') self.store.client.secret = matches[1]
226 else if (key === 'user_username') self.store.user.username = matches[1]
227 else if (key === 'user_password') self.store.user.password = matches[1]
228 }
229 }
230
231 // Check if all required sentences are here
232 for (const key of Object.keys(serverRunString)) {
233 if (data.toString().indexOf(key) !== -1) serverRunString[key] = true
234 if (serverRunString[key] === false) dontContinue = true
235 }
236
237 // If no, there is maybe one thing not already initialized (client/user credentials generation...)
238 if (dontContinue === true) return
239
240 if (options.hideLogs === false) {
241 console.log(data.toString())
242 } else {
243 self.app.stdout.removeListener('data', onStdout)
244 }
245
246 process.on('exit', () => {
247 try {
248 process.kill(self.app.pid)
249 } catch { /* empty */ }
250 })
251
252 res()
253 })
254 })
255 }
256
257 async kill () {
258 if (!this.app) return
259
260 await this.sql.cleanup()
261
262 process.kill(-this.app.pid)
263
264 this.app = null
265 }
266
267 private randomServer () {
268 const low = 10
269 const high = 10000
270
271 return randomInt(low, high)
272 }
273
274 private randomRTMP () {
275 const low = 1900
276 const high = 2100
277
278 return randomInt(low, high)
279 }
280
281 private async assignCustomConfigFile () {
282 if (this.internalServerNumber === this.serverNumber) return
283
284 const basePath = join(root(), 'config')
285
286 const tmpConfigFile = join(basePath, `test-${this.internalServerNumber}.yaml`)
287 await copy(join(basePath, `test-${this.serverNumber}.yaml`), tmpConfigFile)
288
289 this.customConfigFile = tmpConfigFile
290 }
291
292 private buildConfigOverride () {
293 if (!this.parallel) return {}
294
295 return {
296 listen: {
297 port: this.port
298 },
299 webserver: {
300 port: this.port
301 },
302 database: {
303 suffix: '_test' + this.internalServerNumber
304 },
305 storage: {
306 tmp: `test${this.internalServerNumber}/tmp/`,
307 avatars: `test${this.internalServerNumber}/avatars/`,
308 videos: `test${this.internalServerNumber}/videos/`,
309 streaming_playlists: `test${this.internalServerNumber}/streaming-playlists/`,
310 redundancy: `test${this.internalServerNumber}/redundancy/`,
311 logs: `test${this.internalServerNumber}/logs/`,
312 previews: `test${this.internalServerNumber}/previews/`,
313 thumbnails: `test${this.internalServerNumber}/thumbnails/`,
314 torrents: `test${this.internalServerNumber}/torrents/`,
315 captions: `test${this.internalServerNumber}/captions/`,
316 cache: `test${this.internalServerNumber}/cache/`,
317 plugins: `test${this.internalServerNumber}/plugins/`
318 },
319 admin: {
320 email: `admin${this.internalServerNumber}@example.com`
321 },
322 live: {
323 rtmp: {
324 port: this.rtmpPort
325 }
326 }
327 }
328 }
329
330 private assignCommands () {
331 this.bulk = new BulkCommand(this)
332 this.cli = new CLICommand(this)
333 this.customPage = new CustomPagesCommand(this)
334 this.feed = new FeedCommand(this)
335 this.logs = new LogsCommand(this)
336 this.abuses = new AbusesCommand(this)
337 this.overviews = new OverviewsCommand(this)
338 this.search = new SearchCommand(this)
339 this.contactForm = new ContactFormCommand(this)
340 this.debug = new DebugCommand(this)
341 this.follows = new FollowsCommand(this)
342 this.jobs = new JobsCommand(this)
343 this.plugins = new PluginsCommand(this)
344 this.redundancy = new RedundancyCommand(this)
345 this.stats = new StatsCommand(this)
346 this.config = new ConfigCommand(this)
347 this.socketIO = new SocketIOCommand(this)
348 this.accounts = new AccountsCommand(this)
349 this.blocklist = new BlocklistCommand(this)
350 this.subscriptions = new SubscriptionsCommand(this)
351 this.live = new LiveCommand(this)
352 this.services = new ServicesCommand(this)
353 this.blacklist = new BlacklistCommand(this)
354 this.captions = new CaptionsCommand(this)
355 this.changeOwnership = new ChangeOwnershipCommand(this)
356 this.playlists = new PlaylistsCommand(this)
357 this.history = new HistoryCommand(this)
358 this.imports = new ImportsCommand(this)
359 this.streamingPlaylists = new StreamingPlaylistsCommand(this)
360 this.channels = new ChannelsCommand(this)
361 this.comments = new CommentsCommand(this)
362 this.sql = new SQLCommand(this)
363 this.notifications = new NotificationsCommand(this)
364 this.servers = new ServersCommand(this)
365 this.login = new LoginCommand(this)
366 this.users = new UsersCommand(this)
367 this.videos = new VideosCommand(this)
368 }
369}
diff --git a/shared/extra-utils/server/servers-command.ts b/shared/extra-utils/server/servers-command.ts
new file mode 100644
index 000000000..40a11e8d7
--- /dev/null
+++ b/shared/extra-utils/server/servers-command.ts
@@ -0,0 +1,88 @@
1import { exec } from 'child_process'
2import { copy, ensureDir, readFile, remove } from 'fs-extra'
3import { basename, join } from 'path'
4import { root } from '@server/helpers/core-utils'
5import { HttpStatusCode } from '@shared/models'
6import { getFileSize, isGithubCI, wait } from '../miscs'
7import { AbstractCommand, OverrideCommandOptions } from '../shared'
8
9export class ServersCommand extends AbstractCommand {
10
11 static flushTests (internalServerNumber: number) {
12 return new Promise<void>((res, rej) => {
13 const suffix = ` -- ${internalServerNumber}`
14
15 return exec('npm run clean:server:test' + suffix, (err, _stdout, stderr) => {
16 if (err || stderr) return rej(err || new Error(stderr))
17
18 return res()
19 })
20 })
21 }
22
23 ping (options: OverrideCommandOptions = {}) {
24 return this.getRequestBody({
25 ...options,
26
27 path: '/api/v1/ping',
28 implicitToken: false,
29 defaultExpectedStatus: HttpStatusCode.OK_200
30 })
31 }
32
33 async cleanupTests () {
34 const p: Promise<any>[] = []
35
36 if (isGithubCI()) {
37 await ensureDir('artifacts')
38
39 const origin = this.buildDirectory('logs/peertube.log')
40 const destname = `peertube-${this.server.internalServerNumber}.log`
41 console.log('Saving logs %s.', destname)
42
43 await copy(origin, join('artifacts', destname))
44 }
45
46 if (this.server.parallel) {
47 p.push(ServersCommand.flushTests(this.server.internalServerNumber))
48 }
49
50 if (this.server.customConfigFile) {
51 p.push(remove(this.server.customConfigFile))
52 }
53
54 return p
55 }
56
57 async waitUntilLog (str: string, count = 1, strictCount = true) {
58 const logfile = this.server.servers.buildDirectory('logs/peertube.log')
59
60 while (true) {
61 const buf = await readFile(logfile)
62
63 const matches = buf.toString().match(new RegExp(str, 'g'))
64 if (matches && matches.length === count) return
65 if (matches && strictCount === false && matches.length >= count) return
66
67 await wait(1000)
68 }
69 }
70
71 buildDirectory (directory: string) {
72 return join(root(), 'test' + this.server.internalServerNumber, directory)
73 }
74
75 buildWebTorrentFilePath (fileUrl: string) {
76 return this.buildDirectory(join('videos', basename(fileUrl)))
77 }
78
79 buildFragmentedFilePath (videoUUID: string, fileUrl: string) {
80 return this.buildDirectory(join('streaming-playlists', 'hls', videoUUID, basename(fileUrl)))
81 }
82
83 async getServerFileSize (subPath: string) {
84 const path = this.server.servers.buildDirectory(subPath)
85
86 return getFileSize(path)
87 }
88}
diff --git a/shared/extra-utils/server/servers.ts b/shared/extra-utils/server/servers.ts
index 28e431e94..f0622feb0 100644
--- a/shared/extra-utils/server/servers.ts
+++ b/shared/extra-utils/server/servers.ts
@@ -1,384 +1,49 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ 1import { ensureDir } from 'fs-extra'
2import { isGithubCI } from '../miscs'
3import { PeerTubeServer, RunServerOptions } from './server'
2 4
3import { expect } from 'chai' 5async function createSingleServer (serverNumber: number, configOverride?: Object, options: RunServerOptions = {}) {
4import { ChildProcess, exec, fork } from 'child_process' 6 const server = new PeerTubeServer({ serverNumber })
5import { copy, ensureDir, pathExists, readdir, readFile, remove } from 'fs-extra'
6import { join } from 'path'
7import { randomInt } from '../../core-utils/miscs/miscs'
8import { VideoChannel } from '../../models/videos'
9import { buildServerDirectory, getFileSize, isGithubCI, root, wait } from '../miscs/miscs'
10import { makeGetRequest } from '../requests/requests'
11 7
12interface ServerInfo { 8 await server.flushAndRun(configOverride, options)
13 app: ChildProcess
14
15 url: string
16 host: string
17 hostname: string
18 port: number
19
20 rtmpPort: number
21
22 parallel: boolean
23 internalServerNumber: number
24 serverNumber: number
25
26 client: {
27 id: string
28 secret: string
29 }
30
31 user: {
32 username: string
33 password: string
34 email?: string
35 }
36
37 customConfigFile?: string
38
39 accessToken?: string
40 refreshToken?: string
41 videoChannel?: VideoChannel
42
43 video?: {
44 id: number
45 uuid: string
46 shortUUID: string
47 name?: string
48 url?: string
49
50 account?: {
51 name: string
52 }
53
54 embedPath?: string
55 }
56
57 remoteVideo?: {
58 id: number
59 uuid: string
60 }
61
62 videos?: { id: number, uuid: string }[]
63}
64
65function parallelTests () {
66 return process.env.MOCHA_PARALLEL === 'true'
67}
68
69function flushAndRunMultipleServers (totalServers: number, configOverride?: Object) {
70 const apps = []
71 let i = 0
72
73 return new Promise<ServerInfo[]>(res => {
74 function anotherServerDone (serverNumber, app) {
75 apps[serverNumber - 1] = app
76 i++
77 if (i === totalServers) {
78 return res(apps)
79 }
80 }
81
82 for (let j = 1; j <= totalServers; j++) {
83 flushAndRunServer(j, configOverride).then(app => anotherServerDone(j, app))
84 }
85 })
86}
87
88function flushTests (serverNumber?: number) {
89 return new Promise<void>((res, rej) => {
90 const suffix = serverNumber ? ` -- ${serverNumber}` : ''
91
92 return exec('npm run clean:server:test' + suffix, (err, _stdout, stderr) => {
93 if (err || stderr) return rej(err || new Error(stderr))
94
95 return res()
96 })
97 })
98}
99
100function randomServer () {
101 const low = 10
102 const high = 10000
103
104 return randomInt(low, high)
105}
106
107function randomRTMP () {
108 const low = 1900
109 const high = 2100
110
111 return randomInt(low, high)
112}
113
114type RunServerOptions = {
115 hideLogs?: boolean
116 execArgv?: string[]
117}
118
119async function flushAndRunServer (serverNumber: number, configOverride?: Object, args = [], options: RunServerOptions = {}) {
120 const parallel = parallelTests()
121
122 const internalServerNumber = parallel ? randomServer() : serverNumber
123 const rtmpPort = parallel ? randomRTMP() : 1936
124 const port = 9000 + internalServerNumber
125
126 await flushTests(internalServerNumber)
127
128 const server: ServerInfo = {
129 app: null,
130 port,
131 internalServerNumber,
132 rtmpPort,
133 parallel,
134 serverNumber,
135 url: `http://localhost:${port}`,
136 host: `localhost:${port}`,
137 hostname: 'localhost',
138 client: {
139 id: null,
140 secret: null
141 },
142 user: {
143 username: null,
144 password: null
145 }
146 }
147
148 return runServer(server, configOverride, args, options)
149}
150
151async function runServer (server: ServerInfo, configOverrideArg?: any, args = [], options: RunServerOptions = {}) {
152 // These actions are async so we need to be sure that they have both been done
153 const serverRunString = {
154 'HTTP server listening': false
155 }
156 const key = 'Database peertube_test' + server.internalServerNumber + ' is ready'
157 serverRunString[key] = false
158
159 const regexps = {
160 client_id: 'Client id: (.+)',
161 client_secret: 'Client secret: (.+)',
162 user_username: 'Username: (.+)',
163 user_password: 'User password: (.+)'
164 }
165
166 if (server.internalServerNumber !== server.serverNumber) {
167 const basePath = join(root(), 'config')
168
169 const tmpConfigFile = join(basePath, `test-${server.internalServerNumber}.yaml`)
170 await copy(join(basePath, `test-${server.serverNumber}.yaml`), tmpConfigFile)
171
172 server.customConfigFile = tmpConfigFile
173 }
174
175 const configOverride: any = {}
176
177 if (server.parallel) {
178 Object.assign(configOverride, {
179 listen: {
180 port: server.port
181 },
182 webserver: {
183 port: server.port
184 },
185 database: {
186 suffix: '_test' + server.internalServerNumber
187 },
188 storage: {
189 tmp: `test${server.internalServerNumber}/tmp/`,
190 avatars: `test${server.internalServerNumber}/avatars/`,
191 videos: `test${server.internalServerNumber}/videos/`,
192 streaming_playlists: `test${server.internalServerNumber}/streaming-playlists/`,
193 redundancy: `test${server.internalServerNumber}/redundancy/`,
194 logs: `test${server.internalServerNumber}/logs/`,
195 previews: `test${server.internalServerNumber}/previews/`,
196 thumbnails: `test${server.internalServerNumber}/thumbnails/`,
197 torrents: `test${server.internalServerNumber}/torrents/`,
198 captions: `test${server.internalServerNumber}/captions/`,
199 cache: `test${server.internalServerNumber}/cache/`,
200 plugins: `test${server.internalServerNumber}/plugins/`
201 },
202 admin: {
203 email: `admin${server.internalServerNumber}@example.com`
204 },
205 live: {
206 rtmp: {
207 port: server.rtmpPort
208 }
209 }
210 })
211 }
212
213 if (configOverrideArg !== undefined) {
214 Object.assign(configOverride, configOverrideArg)
215 }
216
217 // Share the environment
218 const env = Object.create(process.env)
219 env['NODE_ENV'] = 'test'
220 env['NODE_APP_INSTANCE'] = server.internalServerNumber.toString()
221 env['NODE_CONFIG'] = JSON.stringify(configOverride)
222
223 const forkOptions = {
224 silent: true,
225 env,
226 detached: true,
227 execArgv: options.execArgv || []
228 }
229
230 return new Promise<ServerInfo>(res => {
231 server.app = fork(join(root(), 'dist', 'server.js'), args, forkOptions)
232 server.app.stdout.on('data', function onStdout (data) {
233 let dontContinue = false
234
235 // Capture things if we want to
236 for (const key of Object.keys(regexps)) {
237 const regexp = regexps[key]
238 const matches = data.toString().match(regexp)
239 if (matches !== null) {
240 if (key === 'client_id') server.client.id = matches[1]
241 else if (key === 'client_secret') server.client.secret = matches[1]
242 else if (key === 'user_username') server.user.username = matches[1]
243 else if (key === 'user_password') server.user.password = matches[1]
244 }
245 }
246
247 // Check if all required sentences are here
248 for (const key of Object.keys(serverRunString)) {
249 if (data.toString().indexOf(key) !== -1) serverRunString[key] = true
250 if (serverRunString[key] === false) dontContinue = true
251 }
252
253 // If no, there is maybe one thing not already initialized (client/user credentials generation...)
254 if (dontContinue === true) return
255
256 if (options.hideLogs === false) {
257 console.log(data.toString())
258 } else {
259 server.app.stdout.removeListener('data', onStdout)
260 }
261
262 process.on('exit', () => {
263 try {
264 process.kill(server.app.pid)
265 } catch { /* empty */ }
266 })
267
268 res(server)
269 })
270 })
271}
272
273async function reRunServer (server: ServerInfo, configOverride?: any) {
274 const newServer = await runServer(server, configOverride)
275 server.app = newServer.app
276 9
277 return server 10 return server
278} 11}
279 12
280async function checkTmpIsEmpty (server: ServerInfo) { 13function createMultipleServers (totalServers: number, configOverride?: Object) {
281 await checkDirectoryIsEmpty(server, 'tmp', [ 'plugins-global.css', 'hls', 'resumable-uploads' ]) 14 const serverPromises: Promise<PeerTubeServer>[] = []
282 15
283 if (await pathExists(join('test' + server.internalServerNumber, 'tmp', 'hls'))) { 16 for (let i = 1; i <= totalServers; i++) {
284 await checkDirectoryIsEmpty(server, 'tmp/hls') 17 serverPromises.push(createSingleServer(i, configOverride))
285 } 18 }
286}
287
288async function checkDirectoryIsEmpty (server: ServerInfo, directory: string, exceptions: string[] = []) {
289 const testDirectory = 'test' + server.internalServerNumber
290
291 const directoryPath = join(root(), testDirectory, directory)
292 19
293 const directoryExists = await pathExists(directoryPath) 20 return Promise.all(serverPromises)
294 expect(directoryExists).to.be.true
295
296 const files = await readdir(directoryPath)
297 const filtered = files.filter(f => exceptions.includes(f) === false)
298
299 expect(filtered).to.have.lengthOf(0)
300} 21}
301 22
302function killallServers (servers: ServerInfo[]) { 23async function killallServers (servers: PeerTubeServer[]) {
303 for (const server of servers) { 24 return Promise.all(servers.map(s => s.kill()))
304 if (!server.app) continue
305
306 process.kill(-server.app.pid)
307 server.app = null
308 }
309} 25}
310 26
311async function cleanupTests (servers: ServerInfo[]) { 27async function cleanupTests (servers: PeerTubeServer[]) {
312 killallServers(servers) 28 await killallServers(servers)
313 29
314 if (isGithubCI()) { 30 if (isGithubCI()) {
315 await ensureDir('artifacts') 31 await ensureDir('artifacts')
316 } 32 }
317 33
318 const p: Promise<any>[] = [] 34 let p: Promise<any>[] = []
319 for (const server of servers) { 35 for (const server of servers) {
320 if (isGithubCI()) { 36 p = p.concat(server.servers.cleanupTests())
321 const origin = await buildServerDirectory(server, 'logs/peertube.log')
322 const destname = `peertube-${server.internalServerNumber}.log`
323 console.log('Saving logs %s.', destname)
324
325 await copy(origin, join('artifacts', destname))
326 }
327
328 if (server.parallel) {
329 p.push(flushTests(server.internalServerNumber))
330 }
331
332 if (server.customConfigFile) {
333 p.push(remove(server.customConfigFile))
334 }
335 } 37 }
336 38
337 return Promise.all(p) 39 return Promise.all(p)
338} 40}
339 41
340async function waitUntilLog (server: ServerInfo, str: string, count = 1, strictCount = true) {
341 const logfile = buildServerDirectory(server, 'logs/peertube.log')
342
343 while (true) {
344 const buf = await readFile(logfile)
345
346 const matches = buf.toString().match(new RegExp(str, 'g'))
347 if (matches && matches.length === count) return
348 if (matches && strictCount === false && matches.length >= count) return
349
350 await wait(1000)
351 }
352}
353
354async function getServerFileSize (server: ServerInfo, subPath: string) {
355 const path = buildServerDirectory(server, subPath)
356
357 return getFileSize(path)
358}
359
360function makePingRequest (server: ServerInfo) {
361 return makeGetRequest({
362 url: server.url,
363 path: '/api/v1/ping',
364 statusCodeExpected: 200
365 })
366}
367
368// --------------------------------------------------------------------------- 42// ---------------------------------------------------------------------------
369 43
370export { 44export {
371 checkDirectoryIsEmpty, 45 createSingleServer,
372 checkTmpIsEmpty, 46 createMultipleServers,
373 getServerFileSize,
374 ServerInfo,
375 parallelTests,
376 cleanupTests, 47 cleanupTests,
377 flushAndRunMultipleServers, 48 killallServers
378 flushTests,
379 makePingRequest,
380 flushAndRunServer,
381 killallServers,
382 reRunServer,
383 waitUntilLog
384} 49}
diff --git a/shared/extra-utils/server/stats-command.ts b/shared/extra-utils/server/stats-command.ts
new file mode 100644
index 000000000..64a452306
--- /dev/null
+++ b/shared/extra-utils/server/stats-command.ts
@@ -0,0 +1,25 @@
1import { HttpStatusCode, ServerStats } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class StatsCommand extends AbstractCommand {
5
6 get (options: OverrideCommandOptions & {
7 useCache?: boolean // default false
8 } = {}) {
9 const { useCache = false } = options
10 const path = '/api/v1/server/stats'
11
12 const query = {
13 t: useCache ? undefined : new Date().getTime()
14 }
15
16 return this.getRequestBody<ServerStats>({
17 ...options,
18
19 path,
20 query,
21 implicitToken: false,
22 defaultExpectedStatus: HttpStatusCode.OK_200
23 })
24 }
25}
diff --git a/shared/extra-utils/server/stats.ts b/shared/extra-utils/server/stats.ts
deleted file mode 100644
index b9dae24e2..000000000
--- a/shared/extra-utils/server/stats.ts
+++ /dev/null
@@ -1,23 +0,0 @@
1import { makeGetRequest } from '../requests/requests'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function getStats (url: string, useCache = false) {
5 const path = '/api/v1/server/stats'
6
7 const query = {
8 t: useCache ? undefined : new Date().getTime()
9 }
10
11 return makeGetRequest({
12 url,
13 path,
14 query,
15 statusCodeExpected: HttpStatusCode.OK_200
16 })
17}
18
19// ---------------------------------------------------------------------------
20
21export {
22 getStats
23}
diff --git a/shared/extra-utils/shared/abstract-command.ts b/shared/extra-utils/shared/abstract-command.ts
new file mode 100644
index 000000000..021045e49
--- /dev/null
+++ b/shared/extra-utils/shared/abstract-command.ts
@@ -0,0 +1,199 @@
1import { isAbsolute, join } from 'path'
2import { root } from '../miscs/tests'
3import {
4 makeDeleteRequest,
5 makeGetRequest,
6 makePostBodyRequest,
7 makePutBodyRequest,
8 makeUploadRequest,
9 unwrapBody,
10 unwrapText
11} from '../requests/requests'
12import { PeerTubeServer } from '../server/server'
13
14export interface OverrideCommandOptions {
15 token?: string
16 expectedStatus?: number
17}
18
19interface InternalCommonCommandOptions extends OverrideCommandOptions {
20 // Default to server.url
21 url?: string
22
23 path: string
24 // If we automatically send the server token if the token is not provided
25 implicitToken: boolean
26 defaultExpectedStatus: number
27
28 // Common optional request parameters
29 contentType?: string
30 accept?: string
31 redirects?: number
32 range?: string
33 host?: string
34 headers?: { [ name: string ]: string }
35 requestType?: string
36 xForwardedFor?: string
37}
38
39interface InternalGetCommandOptions extends InternalCommonCommandOptions {
40 query?: { [ id: string ]: any }
41}
42
43abstract class AbstractCommand {
44
45 constructor (
46 protected server: PeerTubeServer
47 ) {
48
49 }
50
51 protected getRequestBody <T> (options: InternalGetCommandOptions) {
52 return unwrapBody<T>(this.getRequest(options))
53 }
54
55 protected getRequestText (options: InternalGetCommandOptions) {
56 return unwrapText(this.getRequest(options))
57 }
58
59 protected getRawRequest (options: Omit<InternalGetCommandOptions, 'path'>) {
60 const { url, range } = options
61 const { host, protocol, pathname } = new URL(url)
62
63 return this.getRequest({
64 ...options,
65
66 token: this.buildCommonRequestToken(options),
67 defaultExpectedStatus: this.buildExpectedStatus(options),
68
69 url: `${protocol}//${host}`,
70 path: pathname,
71 range
72 })
73 }
74
75 protected getRequest (options: InternalGetCommandOptions) {
76 const { query } = options
77
78 return makeGetRequest({
79 ...this.buildCommonRequestOptions(options),
80
81 query
82 })
83 }
84
85 protected deleteRequest (options: InternalCommonCommandOptions) {
86 return makeDeleteRequest(this.buildCommonRequestOptions(options))
87 }
88
89 protected putBodyRequest (options: InternalCommonCommandOptions & {
90 fields?: { [ fieldName: string ]: any }
91 }) {
92 const { fields } = options
93
94 return makePutBodyRequest({
95 ...this.buildCommonRequestOptions(options),
96
97 fields
98 })
99 }
100
101 protected postBodyRequest (options: InternalCommonCommandOptions & {
102 fields?: { [ fieldName: string ]: any }
103 }) {
104 const { fields } = options
105
106 return makePostBodyRequest({
107 ...this.buildCommonRequestOptions(options),
108
109 fields
110 })
111 }
112
113 protected postUploadRequest (options: InternalCommonCommandOptions & {
114 fields?: { [ fieldName: string ]: any }
115 attaches?: { [ fieldName: string ]: any }
116 }) {
117 const { fields, attaches } = options
118
119 return makeUploadRequest({
120 ...this.buildCommonRequestOptions(options),
121
122 method: 'POST',
123 fields,
124 attaches
125 })
126 }
127
128 protected putUploadRequest (options: InternalCommonCommandOptions & {
129 fields?: { [ fieldName: string ]: any }
130 attaches?: { [ fieldName: string ]: any }
131 }) {
132 const { fields, attaches } = options
133
134 return makeUploadRequest({
135 ...this.buildCommonRequestOptions(options),
136
137 method: 'PUT',
138 fields,
139 attaches
140 })
141 }
142
143 protected updateImageRequest (options: InternalCommonCommandOptions & {
144 fixture: string
145 fieldname: string
146 }) {
147 const filePath = isAbsolute(options.fixture)
148 ? options.fixture
149 : join(root(), 'server', 'tests', 'fixtures', options.fixture)
150
151 return this.postUploadRequest({
152 ...options,
153
154 fields: {},
155 attaches: { [options.fieldname]: filePath }
156 })
157 }
158
159 protected buildCommonRequestOptions (options: InternalCommonCommandOptions) {
160 const { url, path, redirects, contentType, accept, range, host, headers, requestType, xForwardedFor } = options
161
162 return {
163 url: url ?? this.server.url,
164 path,
165
166 token: this.buildCommonRequestToken(options),
167 expectedStatus: this.buildExpectedStatus(options),
168
169 redirects,
170 contentType,
171 range,
172 host,
173 accept,
174 headers,
175 type: requestType,
176 xForwardedFor
177 }
178 }
179
180 protected buildCommonRequestToken (options: Pick<InternalCommonCommandOptions, 'token' | 'implicitToken'>) {
181 const { token } = options
182
183 const fallbackToken = options.implicitToken
184 ? this.server.accessToken
185 : undefined
186
187 return token !== undefined ? token : fallbackToken
188 }
189
190 protected buildExpectedStatus (options: Pick<InternalCommonCommandOptions, 'expectedStatus' | 'defaultExpectedStatus'>) {
191 const { expectedStatus, defaultExpectedStatus } = options
192
193 return expectedStatus !== undefined ? expectedStatus : defaultExpectedStatus
194 }
195}
196
197export {
198 AbstractCommand
199}
diff --git a/shared/extra-utils/shared/index.ts b/shared/extra-utils/shared/index.ts
new file mode 100644
index 000000000..e807ab4f7
--- /dev/null
+++ b/shared/extra-utils/shared/index.ts
@@ -0,0 +1 @@
export * from './abstract-command'
diff --git a/shared/extra-utils/socket/index.ts b/shared/extra-utils/socket/index.ts
new file mode 100644
index 000000000..594329b2f
--- /dev/null
+++ b/shared/extra-utils/socket/index.ts
@@ -0,0 +1 @@
export * from './socket-io-command'
diff --git a/shared/extra-utils/socket/socket-io-command.ts b/shared/extra-utils/socket/socket-io-command.ts
new file mode 100644
index 000000000..c277ead28
--- /dev/null
+++ b/shared/extra-utils/socket/socket-io-command.ts
@@ -0,0 +1,15 @@
1import { io } from 'socket.io-client'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class SocketIOCommand extends AbstractCommand {
5
6 getUserNotificationSocket (options: OverrideCommandOptions = {}) {
7 return io(this.server.url + '/user-notifications', {
8 query: { accessToken: options.token ?? this.server.accessToken }
9 })
10 }
11
12 getLiveNotificationSocket () {
13 return io(this.server.url + '/live-videos')
14 }
15}
diff --git a/shared/extra-utils/socket/socket-io.ts b/shared/extra-utils/socket/socket-io.ts
deleted file mode 100644
index 4ca93f453..000000000
--- a/shared/extra-utils/socket/socket-io.ts
+++ /dev/null
@@ -1,18 +0,0 @@
1import { io } from 'socket.io-client'
2
3function getUserNotificationSocket (serverUrl: string, accessToken: string) {
4 return io(serverUrl + '/user-notifications', {
5 query: { accessToken }
6 })
7}
8
9function getLiveNotificationSocket (serverUrl: string) {
10 return io(serverUrl + '/live-videos')
11}
12
13// ---------------------------------------------------------------------------
14
15export {
16 getUserNotificationSocket,
17 getLiveNotificationSocket
18}
diff --git a/shared/extra-utils/users/accounts-command.ts b/shared/extra-utils/users/accounts-command.ts
new file mode 100644
index 000000000..2f586104e
--- /dev/null
+++ b/shared/extra-utils/users/accounts-command.ts
@@ -0,0 +1,56 @@
1import { HttpStatusCode, ResultList } from '@shared/models'
2import { Account } from '../../models/actors'
3import { AccountVideoRate, VideoRateType } from '../../models/videos'
4import { AbstractCommand, OverrideCommandOptions } from '../shared'
5
6export class AccountsCommand extends AbstractCommand {
7
8 list (options: OverrideCommandOptions & {
9 sort?: string // default -createdAt
10 } = {}) {
11 const { sort = '-createdAt' } = options
12 const path = '/api/v1/accounts'
13
14 return this.getRequestBody<ResultList<Account>>({
15 ...options,
16
17 path,
18 query: { sort },
19 implicitToken: false,
20 defaultExpectedStatus: HttpStatusCode.OK_200
21 })
22 }
23
24 get (options: OverrideCommandOptions & {
25 accountName: string
26 }) {
27 const path = '/api/v1/accounts/' + options.accountName
28
29 return this.getRequestBody<Account>({
30 ...options,
31
32 path,
33 implicitToken: false,
34 defaultExpectedStatus: HttpStatusCode.OK_200
35 })
36 }
37
38 listRatings (options: OverrideCommandOptions & {
39 accountName: string
40 rating?: VideoRateType
41 }) {
42 const { rating, accountName } = options
43 const path = '/api/v1/accounts/' + accountName + '/ratings'
44
45 const query = { rating }
46
47 return this.getRequestBody<ResultList<AccountVideoRate>>({
48 ...options,
49
50 path,
51 query,
52 implicitToken: true,
53 defaultExpectedStatus: HttpStatusCode.OK_200
54 })
55 }
56}
diff --git a/shared/extra-utils/users/accounts.ts b/shared/extra-utils/users/accounts.ts
deleted file mode 100644
index 4ea7f1402..000000000
--- a/shared/extra-utils/users/accounts.ts
+++ /dev/null
@@ -1,87 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import * as request from 'supertest'
4import { expect } from 'chai'
5import { existsSync, readdir } from 'fs-extra'
6import { join } from 'path'
7import { Account } from '../../models/actors'
8import { root } from '../miscs/miscs'
9import { makeGetRequest } from '../requests/requests'
10import { VideoRateType } from '../../models/videos'
11import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
12
13function getAccountsList (url: string, sort = '-createdAt', statusCodeExpected = HttpStatusCode.OK_200) {
14 const path = '/api/v1/accounts'
15
16 return makeGetRequest({
17 url,
18 query: { sort },
19 path,
20 statusCodeExpected
21 })
22}
23
24function getAccount (url: string, accountName: string, statusCodeExpected = HttpStatusCode.OK_200) {
25 const path = '/api/v1/accounts/' + accountName
26
27 return makeGetRequest({
28 url,
29 path,
30 statusCodeExpected
31 })
32}
33
34async function expectAccountFollows (url: string, nameWithDomain: string, followersCount: number, followingCount: number) {
35 const res = await getAccountsList(url)
36 const account = res.body.data.find((a: Account) => a.name + '@' + a.host === nameWithDomain)
37
38 const message = `${nameWithDomain} on ${url}`
39 expect(account.followersCount).to.equal(followersCount, message)
40 expect(account.followingCount).to.equal(followingCount, message)
41}
42
43async function checkActorFilesWereRemoved (filename: string, serverNumber: number) {
44 const testDirectory = 'test' + serverNumber
45
46 for (const directory of [ 'avatars' ]) {
47 const directoryPath = join(root(), testDirectory, directory)
48
49 const directoryExists = existsSync(directoryPath)
50 expect(directoryExists).to.be.true
51
52 const files = await readdir(directoryPath)
53 for (const file of files) {
54 expect(file).to.not.contain(filename)
55 }
56 }
57}
58
59function getAccountRatings (
60 url: string,
61 accountName: string,
62 accessToken: string,
63 rating?: VideoRateType,
64 statusCodeExpected = HttpStatusCode.OK_200
65) {
66 const path = '/api/v1/accounts/' + accountName + '/ratings'
67
68 const query = rating ? { rating } : {}
69
70 return request(url)
71 .get(path)
72 .query(query)
73 .set('Accept', 'application/json')
74 .set('Authorization', 'Bearer ' + accessToken)
75 .expect(statusCodeExpected)
76 .expect('Content-Type', /json/)
77}
78
79// ---------------------------------------------------------------------------
80
81export {
82 getAccount,
83 expectAccountFollows,
84 getAccountsList,
85 checkActorFilesWereRemoved,
86 getAccountRatings
87}
diff --git a/shared/extra-utils/users/actors.ts b/shared/extra-utils/users/actors.ts
new file mode 100644
index 000000000..cfcc7d0a7
--- /dev/null
+++ b/shared/extra-utils/users/actors.ts
@@ -0,0 +1,73 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { pathExists, readdir } from 'fs-extra'
5import { join } from 'path'
6import { root } from '@server/helpers/core-utils'
7import { Account, VideoChannel } from '@shared/models'
8import { PeerTubeServer } from '../server'
9
10async function expectChannelsFollows (options: {
11 server: PeerTubeServer
12 handle: string
13 followers: number
14 following: number
15}) {
16 const { server } = options
17 const { data } = await server.channels.list()
18
19 return expectActorFollow({ ...options, data })
20}
21
22async function expectAccountFollows (options: {
23 server: PeerTubeServer
24 handle: string
25 followers: number
26 following: number
27}) {
28 const { server } = options
29 const { data } = await server.accounts.list()
30
31 return expectActorFollow({ ...options, data })
32}
33
34async function checkActorFilesWereRemoved (filename: string, serverNumber: number) {
35 const testDirectory = 'test' + serverNumber
36
37 for (const directory of [ 'avatars' ]) {
38 const directoryPath = join(root(), testDirectory, directory)
39
40 const directoryExists = await pathExists(directoryPath)
41 expect(directoryExists).to.be.true
42
43 const files = await readdir(directoryPath)
44 for (const file of files) {
45 expect(file).to.not.contain(filename)
46 }
47 }
48}
49
50export {
51 expectAccountFollows,
52 expectChannelsFollows,
53 checkActorFilesWereRemoved
54}
55
56// ---------------------------------------------------------------------------
57
58function expectActorFollow (options: {
59 server: PeerTubeServer
60 data: (Account | VideoChannel)[]
61 handle: string
62 followers: number
63 following: number
64}) {
65 const { server, data, handle, followers, following } = options
66
67 const actor = data.find(a => a.name + '@' + a.host === handle)
68 const message = `${handle} on ${server.url}`
69
70 expect(actor, message).to.exist
71 expect(actor.followersCount).to.equal(followers, message)
72 expect(actor.followingCount).to.equal(following, message)
73}
diff --git a/shared/extra-utils/users/blocklist-command.ts b/shared/extra-utils/users/blocklist-command.ts
new file mode 100644
index 000000000..14491a1ae
--- /dev/null
+++ b/shared/extra-utils/users/blocklist-command.ts
@@ -0,0 +1,139 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { AccountBlock, HttpStatusCode, ResultList, ServerBlock } from '@shared/models'
4import { AbstractCommand, OverrideCommandOptions } from '../shared'
5
6type ListBlocklistOptions = OverrideCommandOptions & {
7 start: number
8 count: number
9 sort: string // default -createdAt
10}
11
12export class BlocklistCommand extends AbstractCommand {
13
14 listMyAccountBlocklist (options: ListBlocklistOptions) {
15 const path = '/api/v1/users/me/blocklist/accounts'
16
17 return this.listBlocklist<AccountBlock>(options, path)
18 }
19
20 listMyServerBlocklist (options: ListBlocklistOptions) {
21 const path = '/api/v1/users/me/blocklist/servers'
22
23 return this.listBlocklist<ServerBlock>(options, path)
24 }
25
26 listServerAccountBlocklist (options: ListBlocklistOptions) {
27 const path = '/api/v1/server/blocklist/accounts'
28
29 return this.listBlocklist<AccountBlock>(options, path)
30 }
31
32 listServerServerBlocklist (options: ListBlocklistOptions) {
33 const path = '/api/v1/server/blocklist/servers'
34
35 return this.listBlocklist<ServerBlock>(options, path)
36 }
37
38 // ---------------------------------------------------------------------------
39
40 addToMyBlocklist (options: OverrideCommandOptions & {
41 account?: string
42 server?: string
43 }) {
44 const { account, server } = options
45
46 const path = account
47 ? '/api/v1/users/me/blocklist/accounts'
48 : '/api/v1/users/me/blocklist/servers'
49
50 return this.postBodyRequest({
51 ...options,
52
53 path,
54 fields: {
55 accountName: account,
56 host: server
57 },
58 implicitToken: true,
59 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
60 })
61 }
62
63 addToServerBlocklist (options: OverrideCommandOptions & {
64 account?: string
65 server?: string
66 }) {
67 const { account, server } = options
68
69 const path = account
70 ? '/api/v1/server/blocklist/accounts'
71 : '/api/v1/server/blocklist/servers'
72
73 return this.postBodyRequest({
74 ...options,
75
76 path,
77 fields: {
78 accountName: account,
79 host: server
80 },
81 implicitToken: true,
82 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
83 })
84 }
85
86 // ---------------------------------------------------------------------------
87
88 removeFromMyBlocklist (options: OverrideCommandOptions & {
89 account?: string
90 server?: string
91 }) {
92 const { account, server } = options
93
94 const path = account
95 ? '/api/v1/users/me/blocklist/accounts/' + account
96 : '/api/v1/users/me/blocklist/servers/' + server
97
98 return this.deleteRequest({
99 ...options,
100
101 path,
102 implicitToken: true,
103 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
104 })
105 }
106
107 removeFromServerBlocklist (options: OverrideCommandOptions & {
108 account?: string
109 server?: string
110 }) {
111 const { account, server } = options
112
113 const path = account
114 ? '/api/v1/server/blocklist/accounts/' + account
115 : '/api/v1/server/blocklist/servers/' + server
116
117 return this.deleteRequest({
118 ...options,
119
120 path,
121 implicitToken: true,
122 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
123 })
124 }
125
126 private listBlocklist <T> (options: ListBlocklistOptions, path: string) {
127 const { start, count, sort = '-createdAt' } = options
128
129 return this.getRequestBody<ResultList<T>>({
130 ...options,
131
132 path,
133 query: { start, count, sort },
134 implicitToken: true,
135 defaultExpectedStatus: HttpStatusCode.OK_200
136 })
137 }
138
139}
diff --git a/shared/extra-utils/users/blocklist.ts b/shared/extra-utils/users/blocklist.ts
deleted file mode 100644
index bdf7ee58a..000000000
--- a/shared/extra-utils/users/blocklist.ts
+++ /dev/null
@@ -1,238 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { makeGetRequest, makeDeleteRequest, makePostBodyRequest } from '../requests/requests'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
5
6function getAccountBlocklistByAccount (
7 url: string,
8 token: string,
9 start: number,
10 count: number,
11 sort = '-createdAt',
12 statusCodeExpected = HttpStatusCode.OK_200
13) {
14 const path = '/api/v1/users/me/blocklist/accounts'
15
16 return makeGetRequest({
17 url,
18 token,
19 query: { start, count, sort },
20 path,
21 statusCodeExpected
22 })
23}
24
25function addAccountToAccountBlocklist (
26 url: string,
27 token: string,
28 accountToBlock: string,
29 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
30) {
31 const path = '/api/v1/users/me/blocklist/accounts'
32
33 return makePostBodyRequest({
34 url,
35 path,
36 token,
37 fields: {
38 accountName: accountToBlock
39 },
40 statusCodeExpected
41 })
42}
43
44function removeAccountFromAccountBlocklist (
45 url: string,
46 token: string,
47 accountToUnblock: string,
48 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
49) {
50 const path = '/api/v1/users/me/blocklist/accounts/' + accountToUnblock
51
52 return makeDeleteRequest({
53 url,
54 path,
55 token,
56 statusCodeExpected
57 })
58}
59
60function getServerBlocklistByAccount (
61 url: string,
62 token: string,
63 start: number,
64 count: number,
65 sort = '-createdAt',
66 statusCodeExpected = HttpStatusCode.OK_200
67) {
68 const path = '/api/v1/users/me/blocklist/servers'
69
70 return makeGetRequest({
71 url,
72 token,
73 query: { start, count, sort },
74 path,
75 statusCodeExpected
76 })
77}
78
79function addServerToAccountBlocklist (
80 url: string,
81 token: string,
82 serverToBlock: string,
83 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
84) {
85 const path = '/api/v1/users/me/blocklist/servers'
86
87 return makePostBodyRequest({
88 url,
89 path,
90 token,
91 fields: {
92 host: serverToBlock
93 },
94 statusCodeExpected
95 })
96}
97
98function removeServerFromAccountBlocklist (
99 url: string,
100 token: string,
101 serverToBlock: string,
102 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
103) {
104 const path = '/api/v1/users/me/blocklist/servers/' + serverToBlock
105
106 return makeDeleteRequest({
107 url,
108 path,
109 token,
110 statusCodeExpected
111 })
112}
113
114function getAccountBlocklistByServer (
115 url: string,
116 token: string,
117 start: number,
118 count: number,
119 sort = '-createdAt',
120 statusCodeExpected = HttpStatusCode.OK_200
121) {
122 const path = '/api/v1/server/blocklist/accounts'
123
124 return makeGetRequest({
125 url,
126 token,
127 query: { start, count, sort },
128 path,
129 statusCodeExpected
130 })
131}
132
133function addAccountToServerBlocklist (
134 url: string,
135 token: string,
136 accountToBlock: string,
137 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
138) {
139 const path = '/api/v1/server/blocklist/accounts'
140
141 return makePostBodyRequest({
142 url,
143 path,
144 token,
145 fields: {
146 accountName: accountToBlock
147 },
148 statusCodeExpected
149 })
150}
151
152function removeAccountFromServerBlocklist (
153 url: string,
154 token: string,
155 accountToUnblock: string,
156 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
157) {
158 const path = '/api/v1/server/blocklist/accounts/' + accountToUnblock
159
160 return makeDeleteRequest({
161 url,
162 path,
163 token,
164 statusCodeExpected
165 })
166}
167
168function getServerBlocklistByServer (
169 url: string,
170 token: string,
171 start: number,
172 count: number,
173 sort = '-createdAt',
174 statusCodeExpected = HttpStatusCode.OK_200
175) {
176 const path = '/api/v1/server/blocklist/servers'
177
178 return makeGetRequest({
179 url,
180 token,
181 query: { start, count, sort },
182 path,
183 statusCodeExpected
184 })
185}
186
187function addServerToServerBlocklist (
188 url: string,
189 token: string,
190 serverToBlock: string,
191 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
192) {
193 const path = '/api/v1/server/blocklist/servers'
194
195 return makePostBodyRequest({
196 url,
197 path,
198 token,
199 fields: {
200 host: serverToBlock
201 },
202 statusCodeExpected
203 })
204}
205
206function removeServerFromServerBlocklist (
207 url: string,
208 token: string,
209 serverToBlock: string,
210 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
211) {
212 const path = '/api/v1/server/blocklist/servers/' + serverToBlock
213
214 return makeDeleteRequest({
215 url,
216 path,
217 token,
218 statusCodeExpected
219 })
220}
221
222// ---------------------------------------------------------------------------
223
224export {
225 getAccountBlocklistByAccount,
226 addAccountToAccountBlocklist,
227 removeAccountFromAccountBlocklist,
228 getServerBlocklistByAccount,
229 addServerToAccountBlocklist,
230 removeServerFromAccountBlocklist,
231
232 getAccountBlocklistByServer,
233 addAccountToServerBlocklist,
234 removeAccountFromServerBlocklist,
235 getServerBlocklistByServer,
236 addServerToServerBlocklist,
237 removeServerFromServerBlocklist
238}
diff --git a/shared/extra-utils/users/index.ts b/shared/extra-utils/users/index.ts
new file mode 100644
index 000000000..460a06f70
--- /dev/null
+++ b/shared/extra-utils/users/index.ts
@@ -0,0 +1,9 @@
1export * from './accounts-command'
2export * from './actors'
3export * from './blocklist-command'
4export * from './login'
5export * from './login-command'
6export * from './notifications'
7export * from './notifications-command'
8export * from './subscriptions-command'
9export * from './users-command'
diff --git a/shared/extra-utils/users/login-command.ts b/shared/extra-utils/users/login-command.ts
new file mode 100644
index 000000000..143f72a59
--- /dev/null
+++ b/shared/extra-utils/users/login-command.ts
@@ -0,0 +1,132 @@
1import { HttpStatusCode, PeerTubeProblemDocument } from '@shared/models'
2import { unwrapBody } from '../requests'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class LoginCommand extends AbstractCommand {
6
7 login (options: OverrideCommandOptions & {
8 client?: { id?: string, secret?: string }
9 user?: { username: string, password?: string }
10 } = {}) {
11 const { client = this.server.store.client, user = this.server.store.user } = options
12 const path = '/api/v1/users/token'
13
14 const body = {
15 client_id: client.id,
16 client_secret: client.secret,
17 username: user.username,
18 password: user.password ?? 'password',
19 response_type: 'code',
20 grant_type: 'password',
21 scope: 'upload'
22 }
23
24 return unwrapBody<{ access_token: string, refresh_token: string } & PeerTubeProblemDocument>(this.postBodyRequest({
25 ...options,
26
27 path,
28 requestType: 'form',
29 fields: body,
30 implicitToken: false,
31 defaultExpectedStatus: HttpStatusCode.OK_200
32 }))
33 }
34
35 getAccessToken (arg1?: { username: string, password?: string }): Promise<string>
36 getAccessToken (arg1: string, password?: string): Promise<string>
37 async getAccessToken (arg1?: { username: string, password?: string } | string, password?: string) {
38 let user: { username: string, password?: string }
39
40 if (!arg1) user = this.server.store.user
41 else if (typeof arg1 === 'object') user = arg1
42 else user = { username: arg1, password }
43
44 try {
45 const body = await this.login({ user })
46
47 return body.access_token
48 } catch (err) {
49 throw new Error(`Cannot authenticate. Please check your username/password. (${err})`)
50 }
51 }
52
53 loginUsingExternalToken (options: OverrideCommandOptions & {
54 username: string
55 externalAuthToken: string
56 }) {
57 const { username, externalAuthToken } = options
58 const path = '/api/v1/users/token'
59
60 const body = {
61 client_id: this.server.store.client.id,
62 client_secret: this.server.store.client.secret,
63 username: username,
64 response_type: 'code',
65 grant_type: 'password',
66 scope: 'upload',
67 externalAuthToken
68 }
69
70 return this.postBodyRequest({
71 ...options,
72
73 path,
74 requestType: 'form',
75 fields: body,
76 implicitToken: false,
77 defaultExpectedStatus: HttpStatusCode.OK_200
78 })
79 }
80
81 logout (options: OverrideCommandOptions & {
82 token: string
83 }) {
84 const path = '/api/v1/users/revoke-token'
85
86 return unwrapBody<{ redirectUrl: string }>(this.postBodyRequest({
87 ...options,
88
89 path,
90 requestType: 'form',
91 implicitToken: false,
92 defaultExpectedStatus: HttpStatusCode.OK_200
93 }))
94 }
95
96 refreshToken (options: OverrideCommandOptions & {
97 refreshToken: string
98 }) {
99 const path = '/api/v1/users/token'
100
101 const body = {
102 client_id: this.server.store.client.id,
103 client_secret: this.server.store.client.secret,
104 refresh_token: options.refreshToken,
105 response_type: 'code',
106 grant_type: 'refresh_token'
107 }
108
109 return this.postBodyRequest({
110 ...options,
111
112 path,
113 requestType: 'form',
114 fields: body,
115 implicitToken: false,
116 defaultExpectedStatus: HttpStatusCode.OK_200
117 })
118 }
119
120 getClient (options: OverrideCommandOptions = {}) {
121 const path = '/api/v1/oauth-clients/local'
122
123 return this.getRequestBody<{ client_id: string, client_secret: string }>({
124 ...options,
125
126 path,
127 host: this.server.host,
128 implicitToken: false,
129 defaultExpectedStatus: HttpStatusCode.OK_200
130 })
131 }
132}
diff --git a/shared/extra-utils/users/login.ts b/shared/extra-utils/users/login.ts
index 39e1a2747..f1df027d3 100644
--- a/shared/extra-utils/users/login.ts
+++ b/shared/extra-utils/users/login.ts
@@ -1,133 +1,19 @@
1import * as request from 'supertest' 1import { PeerTubeServer } from '../server/server'
2 2
3import { ServerInfo } from '../server/servers' 3function setAccessTokensToServers (servers: PeerTubeServer[]) {
4import { getClient } from '../server/clients'
5import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
6
7type Client = { id: string, secret: string }
8type User = { username: string, password: string }
9type Server = { url: string, client: Client, user: User }
10
11function login (url: string, client: Client, user: User, expectedStatus = HttpStatusCode.OK_200) {
12 const path = '/api/v1/users/token'
13
14 const body = {
15 client_id: client.id,
16 client_secret: client.secret,
17 username: user.username,
18 password: user.password,
19 response_type: 'code',
20 grant_type: 'password',
21 scope: 'upload'
22 }
23
24 return request(url)
25 .post(path)
26 .type('form')
27 .send(body)
28 .expect(expectedStatus)
29}
30
31function logout (url: string, token: string, expectedStatus = HttpStatusCode.OK_200) {
32 const path = '/api/v1/users/revoke-token'
33
34 return request(url)
35 .post(path)
36 .set('Authorization', 'Bearer ' + token)
37 .type('form')
38 .expect(expectedStatus)
39}
40
41async function serverLogin (server: Server) {
42 const res = await login(server.url, server.client, server.user, HttpStatusCode.OK_200)
43
44 return res.body.access_token as string
45}
46
47function refreshToken (server: ServerInfo, refreshToken: string, expectedStatus = HttpStatusCode.OK_200) {
48 const path = '/api/v1/users/token'
49
50 const body = {
51 client_id: server.client.id,
52 client_secret: server.client.secret,
53 refresh_token: refreshToken,
54 response_type: 'code',
55 grant_type: 'refresh_token'
56 }
57
58 return request(server.url)
59 .post(path)
60 .type('form')
61 .send(body)
62 .expect(expectedStatus)
63}
64
65async function userLogin (server: Server, user: User, expectedStatus = HttpStatusCode.OK_200) {
66 const res = await login(server.url, server.client, user, expectedStatus)
67
68 return res.body.access_token as string
69}
70
71async function getAccessToken (url: string, username: string, password: string) {
72 const resClient = await getClient(url)
73 const client = {
74 id: resClient.body.client_id,
75 secret: resClient.body.client_secret
76 }
77
78 const user = { username, password }
79
80 try {
81 const res = await login(url, client, user)
82 return res.body.access_token
83 } catch (err) {
84 throw new Error('Cannot authenticate. Please check your username/password.')
85 }
86}
87
88function setAccessTokensToServers (servers: ServerInfo[]) {
89 const tasks: Promise<any>[] = [] 4 const tasks: Promise<any>[] = []
90 5
91 for (const server of servers) { 6 for (const server of servers) {
92 const p = serverLogin(server).then(t => { server.accessToken = t }) 7 const p = server.login.getAccessToken()
8 .then(t => { server.accessToken = t })
93 tasks.push(p) 9 tasks.push(p)
94 } 10 }
95 11
96 return Promise.all(tasks) 12 return Promise.all(tasks)
97} 13}
98 14
99function loginUsingExternalToken (server: Server, username: string, externalAuthToken: string, expectedStatus = HttpStatusCode.OK_200) {
100 const path = '/api/v1/users/token'
101
102 const body = {
103 client_id: server.client.id,
104 client_secret: server.client.secret,
105 username: username,
106 response_type: 'code',
107 grant_type: 'password',
108 scope: 'upload',
109 externalAuthToken
110 }
111
112 return request(server.url)
113 .post(path)
114 .type('form')
115 .send(body)
116 .expect(expectedStatus)
117}
118
119// --------------------------------------------------------------------------- 15// ---------------------------------------------------------------------------
120 16
121export { 17export {
122 login, 18 setAccessTokensToServers
123 logout,
124 serverLogin,
125 refreshToken,
126 userLogin,
127 getAccessToken,
128 setAccessTokensToServers,
129 Server,
130 Client,
131 User,
132 loginUsingExternalToken
133} 19}
diff --git a/shared/extra-utils/users/notifications-command.ts b/shared/extra-utils/users/notifications-command.ts
new file mode 100644
index 000000000..2d79a3747
--- /dev/null
+++ b/shared/extra-utils/users/notifications-command.ts
@@ -0,0 +1,86 @@
1import { HttpStatusCode, ResultList } from '@shared/models'
2import { UserNotification, UserNotificationSetting } from '../../models/users'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class NotificationsCommand extends AbstractCommand {
6
7 updateMySettings (options: OverrideCommandOptions & {
8 settings: UserNotificationSetting
9 }) {
10 const path = '/api/v1/users/me/notification-settings'
11
12 return this.putBodyRequest({
13 ...options,
14
15 path,
16 fields: options.settings,
17 implicitToken: true,
18 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
19 })
20 }
21
22 list (options: OverrideCommandOptions & {
23 start?: number
24 count?: number
25 unread?: boolean
26 sort?: string
27 }) {
28 const { start, count, unread, sort = '-createdAt' } = options
29 const path = '/api/v1/users/me/notifications'
30
31 return this.getRequestBody<ResultList<UserNotification>>({
32 ...options,
33
34 path,
35 query: {
36 start,
37 count,
38 sort,
39 unread
40 },
41 implicitToken: true,
42 defaultExpectedStatus: HttpStatusCode.OK_200
43 })
44 }
45
46 markAsRead (options: OverrideCommandOptions & {
47 ids: number[]
48 }) {
49 const { ids } = options
50 const path = '/api/v1/users/me/notifications/read'
51
52 return this.postBodyRequest({
53 ...options,
54
55 path,
56 fields: { ids },
57 implicitToken: true,
58 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
59 })
60 }
61
62 markAsReadAll (options: OverrideCommandOptions) {
63 const path = '/api/v1/users/me/notifications/read-all'
64
65 return this.postBodyRequest({
66 ...options,
67
68 path,
69 implicitToken: true,
70 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
71 })
72 }
73
74 async getLastest (options: OverrideCommandOptions = {}) {
75 const { total, data } = await this.list({
76 ...options,
77 start: 0,
78 count: 1,
79 sort: '-createdAt'
80 })
81
82 if (total === 0) return undefined
83
84 return data[0]
85 }
86}
diff --git a/shared/extra-utils/users/user-notifications.ts b/shared/extra-utils/users/notifications.ts
index 844f4442d..7db4bfd3f 100644
--- a/shared/extra-utils/users/user-notifications.ts
+++ b/shared/extra-utils/users/notifications.ts
@@ -3,91 +3,15 @@
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { inspect } from 'util' 4import { inspect } from 'util'
5import { AbuseState, PluginType } from '@shared/models' 5import { AbuseState, PluginType } from '@shared/models'
6import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
7import { UserNotification, UserNotificationSetting, UserNotificationSettingValue, UserNotificationType } from '../../models/users' 6import { UserNotification, UserNotificationSetting, UserNotificationSettingValue, UserNotificationType } from '../../models/users'
8import { MockSmtpServer } from '../miscs/email' 7import { MockSmtpServer } from '../mock-servers/mock-email'
9import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests' 8import { PeerTubeServer } from '../server'
10import { doubleFollow } from '../server/follows' 9import { doubleFollow } from '../server/follows'
11import { flushAndRunMultipleServers, ServerInfo } from '../server/servers' 10import { createMultipleServers } from '../server/servers'
12import { getUserNotificationSocket } from '../socket/socket-io' 11import { setAccessTokensToServers } from './login'
13import { setAccessTokensToServers, userLogin } from './login'
14import { createUser, getMyUserInformation } from './users'
15
16function updateMyNotificationSettings (
17 url: string,
18 token: string,
19 settings: UserNotificationSetting,
20 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
21) {
22 const path = '/api/v1/users/me/notification-settings'
23
24 return makePutBodyRequest({
25 url,
26 path,
27 token,
28 fields: settings,
29 statusCodeExpected
30 })
31}
32
33async function getUserNotifications (
34 url: string,
35 token: string,
36 start: number,
37 count: number,
38 unread?: boolean,
39 sort = '-createdAt',
40 statusCodeExpected = HttpStatusCode.OK_200
41) {
42 const path = '/api/v1/users/me/notifications'
43
44 return makeGetRequest({
45 url,
46 path,
47 token,
48 query: {
49 start,
50 count,
51 sort,
52 unread
53 },
54 statusCodeExpected
55 })
56}
57
58function markAsReadNotifications (url: string, token: string, ids: number[], statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
59 const path = '/api/v1/users/me/notifications/read'
60
61 return makePostBodyRequest({
62 url,
63 path,
64 token,
65 fields: { ids },
66 statusCodeExpected
67 })
68}
69
70function markAsReadAllNotifications (url: string, token: string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
71 const path = '/api/v1/users/me/notifications/read-all'
72
73 return makePostBodyRequest({
74 url,
75 path,
76 token,
77 statusCodeExpected
78 })
79}
80
81async function getLastNotification (serverUrl: string, accessToken: string) {
82 const res = await getUserNotifications(serverUrl, accessToken, 0, 1, undefined, '-createdAt')
83
84 if (res.body.total === 0) return undefined
85
86 return res.body.data[0] as UserNotification
87}
88 12
89type CheckerBaseParams = { 13type CheckerBaseParams = {
90 server: ServerInfo 14 server: PeerTubeServer
91 emails: any[] 15 emails: any[]
92 socketNotifications: UserNotification[] 16 socketNotifications: UserNotification[]
93 token: string 17 token: string
@@ -96,91 +20,41 @@ type CheckerBaseParams = {
96 20
97type CheckerType = 'presence' | 'absence' 21type CheckerType = 'presence' | 'absence'
98 22
99async function checkNotification ( 23function getAllNotificationsSettings (): UserNotificationSetting {
100 base: CheckerBaseParams, 24 return {
101 notificationChecker: (notification: UserNotification, type: CheckerType) => void, 25 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
102 emailNotificationFinder: (email: object) => boolean, 26 newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
103 checkType: CheckerType 27 abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
104) { 28 videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
105 const check = base.check || { web: true, mail: true } 29 blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
106 30 myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
107 if (check.web) { 31 myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
108 const notification = await getLastNotification(base.server.url, base.token) 32 commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
109 33 newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
110 if (notification || checkType !== 'absence') { 34 newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
111 notificationChecker(notification, checkType) 35 newInstanceFollower: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
112 } 36 abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
113 37 abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
114 const socketNotification = base.socketNotifications.find(n => { 38 autoInstanceFollowing: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
115 try { 39 newPeerTubeVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
116 notificationChecker(n, 'presence') 40 newPluginVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
117 return true
118 } catch {
119 return false
120 }
121 })
122
123 if (checkType === 'presence') {
124 const obj = inspect(base.socketNotifications, { depth: 5 })
125 expect(socketNotification, 'The socket notification is absent when it should be present. ' + obj).to.not.be.undefined
126 } else {
127 const obj = inspect(socketNotification, { depth: 5 })
128 expect(socketNotification, 'The socket notification is present when it should not be present. ' + obj).to.be.undefined
129 }
130 }
131
132 if (check.mail) {
133 // Last email
134 const email = base.emails
135 .slice()
136 .reverse()
137 .find(e => emailNotificationFinder(e))
138
139 if (checkType === 'presence') {
140 const emails = base.emails.map(e => e.text)
141 expect(email, 'The email is absent when is should be present. ' + inspect(emails)).to.not.be.undefined
142 } else {
143 expect(email, 'The email is present when is should not be present. ' + inspect(email)).to.be.undefined
144 }
145 }
146}
147
148function checkVideo (video: any, videoName?: string, videoUUID?: string) {
149 if (videoName) {
150 expect(video.name).to.be.a('string')
151 expect(video.name).to.not.be.empty
152 expect(video.name).to.equal(videoName)
153 }
154
155 if (videoUUID) {
156 expect(video.uuid).to.be.a('string')
157 expect(video.uuid).to.not.be.empty
158 expect(video.uuid).to.equal(videoUUID)
159 } 41 }
160
161 expect(video.id).to.be.a('number')
162} 42}
163 43
164function checkActor (actor: any) { 44async function checkNewVideoFromSubscription (options: CheckerBaseParams & {
165 expect(actor.displayName).to.be.a('string') 45 videoName: string
166 expect(actor.displayName).to.not.be.empty 46 shortUUID: string
167 expect(actor.host).to.not.be.undefined 47 checkType: CheckerType
168} 48}) {
169 49 const { videoName, shortUUID } = options
170function checkComment (comment: any, commentId: number, threadId: number) {
171 expect(comment.id).to.equal(commentId)
172 expect(comment.threadId).to.equal(threadId)
173}
174
175async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
176 const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION 50 const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION
177 51
178 function notificationChecker (notification: UserNotification, type: CheckerType) { 52 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
179 if (type === 'presence') { 53 if (checkType === 'presence') {
180 expect(notification).to.not.be.undefined 54 expect(notification).to.not.be.undefined
181 expect(notification.type).to.equal(notificationType) 55 expect(notification.type).to.equal(notificationType)
182 56
183 checkVideo(notification.video, videoName, videoUUID) 57 checkVideo(notification.video, videoName, shortUUID)
184 checkActor(notification.video.channel) 58 checkActor(notification.video.channel)
185 } else { 59 } else {
186 expect(notification).to.satisfy((n: UserNotification) => { 60 expect(notification).to.satisfy((n: UserNotification) => {
@@ -191,21 +65,26 @@ async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName
191 65
192 function emailNotificationFinder (email: object) { 66 function emailNotificationFinder (email: object) {
193 const text = email['text'] 67 const text = email['text']
194 return text.indexOf(videoUUID) !== -1 && text.indexOf('Your subscription') !== -1 68 return text.indexOf(shortUUID) !== -1 && text.indexOf('Your subscription') !== -1
195 } 69 }
196 70
197 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 71 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
198} 72}
199 73
200async function checkVideoIsPublished (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) { 74async function checkVideoIsPublished (options: CheckerBaseParams & {
75 videoName: string
76 shortUUID: string
77 checkType: CheckerType
78}) {
79 const { videoName, shortUUID } = options
201 const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED 80 const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED
202 81
203 function notificationChecker (notification: UserNotification, type: CheckerType) { 82 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
204 if (type === 'presence') { 83 if (checkType === 'presence') {
205 expect(notification).to.not.be.undefined 84 expect(notification).to.not.be.undefined
206 expect(notification.type).to.equal(notificationType) 85 expect(notification.type).to.equal(notificationType)
207 86
208 checkVideo(notification.video, videoName, videoUUID) 87 checkVideo(notification.video, videoName, shortUUID)
209 checkActor(notification.video.channel) 88 checkActor(notification.video.channel)
210 } else { 89 } else {
211 expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName) 90 expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName)
@@ -214,30 +93,31 @@ async function checkVideoIsPublished (base: CheckerBaseParams, videoName: string
214 93
215 function emailNotificationFinder (email: object) { 94 function emailNotificationFinder (email: object) {
216 const text: string = email['text'] 95 const text: string = email['text']
217 return text.includes(videoUUID) && text.includes('Your video') 96 return text.includes(shortUUID) && text.includes('Your video')
218 } 97 }
219 98
220 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 99 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
221} 100}
222 101
223async function checkMyVideoImportIsFinished ( 102async function checkMyVideoImportIsFinished (options: CheckerBaseParams & {
224 base: CheckerBaseParams, 103 videoName: string
225 videoName: string, 104 shortUUID: string
226 videoUUID: string, 105 url: string
227 url: string, 106 success: boolean
228 success: boolean, 107 checkType: CheckerType
229 type: CheckerType 108}) {
230) { 109 const { videoName, shortUUID, url, success } = options
110
231 const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR 111 const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR
232 112
233 function notificationChecker (notification: UserNotification, type: CheckerType) { 113 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
234 if (type === 'presence') { 114 if (checkType === 'presence') {
235 expect(notification).to.not.be.undefined 115 expect(notification).to.not.be.undefined
236 expect(notification.type).to.equal(notificationType) 116 expect(notification.type).to.equal(notificationType)
237 117
238 expect(notification.videoImport.targetUrl).to.equal(url) 118 expect(notification.videoImport.targetUrl).to.equal(url)
239 119
240 if (success) checkVideo(notification.videoImport.video, videoName, videoUUID) 120 if (success) checkVideo(notification.videoImport.video, videoName, shortUUID)
241 } else { 121 } else {
242 expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url) 122 expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url)
243 } 123 }
@@ -250,14 +130,18 @@ async function checkMyVideoImportIsFinished (
250 return text.includes(url) && text.includes(toFind) 130 return text.includes(url) && text.includes(toFind)
251 } 131 }
252 132
253 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 133 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
254} 134}
255 135
256async function checkUserRegistered (base: CheckerBaseParams, username: string, type: CheckerType) { 136async function checkUserRegistered (options: CheckerBaseParams & {
137 username: string
138 checkType: CheckerType
139}) {
140 const { username } = options
257 const notificationType = UserNotificationType.NEW_USER_REGISTRATION 141 const notificationType = UserNotificationType.NEW_USER_REGISTRATION
258 142
259 function notificationChecker (notification: UserNotification, type: CheckerType) { 143 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
260 if (type === 'presence') { 144 if (checkType === 'presence') {
261 expect(notification).to.not.be.undefined 145 expect(notification).to.not.be.undefined
262 expect(notification.type).to.equal(notificationType) 146 expect(notification.type).to.equal(notificationType)
263 147
@@ -274,21 +158,21 @@ async function checkUserRegistered (base: CheckerBaseParams, username: string, t
274 return text.includes(' registered.') && text.includes(username) 158 return text.includes(' registered.') && text.includes(username)
275 } 159 }
276 160
277 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 161 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
278} 162}
279 163
280async function checkNewActorFollow ( 164async function checkNewActorFollow (options: CheckerBaseParams & {
281 base: CheckerBaseParams, 165 followType: 'channel' | 'account'
282 followType: 'channel' | 'account', 166 followerName: string
283 followerName: string, 167 followerDisplayName: string
284 followerDisplayName: string, 168 followingDisplayName: string
285 followingDisplayName: string, 169 checkType: CheckerType
286 type: CheckerType 170}) {
287) { 171 const { followType, followerName, followerDisplayName, followingDisplayName } = options
288 const notificationType = UserNotificationType.NEW_FOLLOW 172 const notificationType = UserNotificationType.NEW_FOLLOW
289 173
290 function notificationChecker (notification: UserNotification, type: CheckerType) { 174 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
291 if (type === 'presence') { 175 if (checkType === 'presence') {
292 expect(notification).to.not.be.undefined 176 expect(notification).to.not.be.undefined
293 expect(notification.type).to.equal(notificationType) 177 expect(notification.type).to.equal(notificationType)
294 178
@@ -314,14 +198,18 @@ async function checkNewActorFollow (
314 return text.includes(followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName) 198 return text.includes(followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName)
315 } 199 }
316 200
317 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 201 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
318} 202}
319 203
320async function checkNewInstanceFollower (base: CheckerBaseParams, followerHost: string, type: CheckerType) { 204async function checkNewInstanceFollower (options: CheckerBaseParams & {
205 followerHost: string
206 checkType: CheckerType
207}) {
208 const { followerHost } = options
321 const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER 209 const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER
322 210
323 function notificationChecker (notification: UserNotification, type: CheckerType) { 211 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
324 if (type === 'presence') { 212 if (checkType === 'presence') {
325 expect(notification).to.not.be.undefined 213 expect(notification).to.not.be.undefined
326 expect(notification.type).to.equal(notificationType) 214 expect(notification.type).to.equal(notificationType)
327 215
@@ -343,14 +231,19 @@ async function checkNewInstanceFollower (base: CheckerBaseParams, followerHost:
343 return text.includes('instance has a new follower') && text.includes(followerHost) 231 return text.includes('instance has a new follower') && text.includes(followerHost)
344 } 232 }
345 233
346 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 234 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
347} 235}
348 236
349async function checkAutoInstanceFollowing (base: CheckerBaseParams, followerHost: string, followingHost: string, type: CheckerType) { 237async function checkAutoInstanceFollowing (options: CheckerBaseParams & {
238 followerHost: string
239 followingHost: string
240 checkType: CheckerType
241}) {
242 const { followerHost, followingHost } = options
350 const notificationType = UserNotificationType.AUTO_INSTANCE_FOLLOWING 243 const notificationType = UserNotificationType.AUTO_INSTANCE_FOLLOWING
351 244
352 function notificationChecker (notification: UserNotification, type: CheckerType) { 245 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
353 if (type === 'presence') { 246 if (checkType === 'presence') {
354 expect(notification).to.not.be.undefined 247 expect(notification).to.not.be.undefined
355 expect(notification.type).to.equal(notificationType) 248 expect(notification.type).to.equal(notificationType)
356 249
@@ -374,21 +267,21 @@ async function checkAutoInstanceFollowing (base: CheckerBaseParams, followerHost
374 return text.includes(' automatically followed a new instance') && text.includes(followingHost) 267 return text.includes(' automatically followed a new instance') && text.includes(followingHost)
375 } 268 }
376 269
377 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 270 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
378} 271}
379 272
380async function checkCommentMention ( 273async function checkCommentMention (options: CheckerBaseParams & {
381 base: CheckerBaseParams, 274 shortUUID: string
382 uuid: string, 275 commentId: number
383 commentId: number, 276 threadId: number
384 threadId: number, 277 byAccountDisplayName: string
385 byAccountDisplayName: string, 278 checkType: CheckerType
386 type: CheckerType 279}) {
387) { 280 const { shortUUID, commentId, threadId, byAccountDisplayName } = options
388 const notificationType = UserNotificationType.COMMENT_MENTION 281 const notificationType = UserNotificationType.COMMENT_MENTION
389 282
390 function notificationChecker (notification: UserNotification, type: CheckerType) { 283 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
391 if (type === 'presence') { 284 if (checkType === 'presence') {
392 expect(notification).to.not.be.undefined 285 expect(notification).to.not.be.undefined
393 expect(notification.type).to.equal(notificationType) 286 expect(notification.type).to.equal(notificationType)
394 287
@@ -396,7 +289,7 @@ async function checkCommentMention (
396 checkActor(notification.comment.account) 289 checkActor(notification.comment.account)
397 expect(notification.comment.account.displayName).to.equal(byAccountDisplayName) 290 expect(notification.comment.account.displayName).to.equal(byAccountDisplayName)
398 291
399 checkVideo(notification.comment.video, undefined, uuid) 292 checkVideo(notification.comment.video, undefined, shortUUID)
400 } else { 293 } else {
401 expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId) 294 expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId)
402 } 295 }
@@ -405,25 +298,31 @@ async function checkCommentMention (
405 function emailNotificationFinder (email: object) { 298 function emailNotificationFinder (email: object) {
406 const text: string = email['text'] 299 const text: string = email['text']
407 300
408 return text.includes(' mentioned ') && text.includes(uuid) && text.includes(byAccountDisplayName) 301 return text.includes(' mentioned ') && text.includes(shortUUID) && text.includes(byAccountDisplayName)
409 } 302 }
410 303
411 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 304 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
412} 305}
413 306
414let lastEmailCount = 0 307let lastEmailCount = 0
415 308
416async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) { 309async function checkNewCommentOnMyVideo (options: CheckerBaseParams & {
310 shortUUID: string
311 commentId: number
312 threadId: number
313 checkType: CheckerType
314}) {
315 const { server, shortUUID, commentId, threadId, checkType, emails } = options
417 const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO 316 const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO
418 317
419 function notificationChecker (notification: UserNotification, type: CheckerType) { 318 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
420 if (type === 'presence') { 319 if (checkType === 'presence') {
421 expect(notification).to.not.be.undefined 320 expect(notification).to.not.be.undefined
422 expect(notification.type).to.equal(notificationType) 321 expect(notification.type).to.equal(notificationType)
423 322
424 checkComment(notification.comment, commentId, threadId) 323 checkComment(notification.comment, commentId, threadId)
425 checkActor(notification.comment.account) 324 checkActor(notification.comment.account)
426 checkVideo(notification.comment.video, undefined, uuid) 325 checkVideo(notification.comment.video, undefined, shortUUID)
427 } else { 326 } else {
428 expect(notification).to.satisfy((n: UserNotification) => { 327 expect(notification).to.satisfy((n: UserNotification) => {
429 return n === undefined || n.comment === undefined || n.comment.id !== commentId 328 return n === undefined || n.comment === undefined || n.comment.id !== commentId
@@ -431,51 +330,62 @@ async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string,
431 } 330 }
432 } 331 }
433 332
434 const commentUrl = `http://localhost:${base.server.port}/w/${uuid};threadId=${threadId}` 333 const commentUrl = `http://localhost:${server.port}/w/${shortUUID};threadId=${threadId}`
435 334
436 function emailNotificationFinder (email: object) { 335 function emailNotificationFinder (email: object) {
437 return email['text'].indexOf(commentUrl) !== -1 336 return email['text'].indexOf(commentUrl) !== -1
438 } 337 }
439 338
440 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 339 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
441 340
442 if (type === 'presence') { 341 if (checkType === 'presence') {
443 // We cannot detect email duplicates, so check we received another email 342 // We cannot detect email duplicates, so check we received another email
444 expect(base.emails).to.have.length.above(lastEmailCount) 343 expect(emails).to.have.length.above(lastEmailCount)
445 lastEmailCount = base.emails.length 344 lastEmailCount = emails.length
446 } 345 }
447} 346}
448 347
449async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { 348async function checkNewVideoAbuseForModerators (options: CheckerBaseParams & {
349 shortUUID: string
350 videoName: string
351 checkType: CheckerType
352}) {
353 const { shortUUID, videoName } = options
450 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS 354 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
451 355
452 function notificationChecker (notification: UserNotification, type: CheckerType) { 356 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
453 if (type === 'presence') { 357 if (checkType === 'presence') {
454 expect(notification).to.not.be.undefined 358 expect(notification).to.not.be.undefined
455 expect(notification.type).to.equal(notificationType) 359 expect(notification.type).to.equal(notificationType)
456 360
457 expect(notification.abuse.id).to.be.a('number') 361 expect(notification.abuse.id).to.be.a('number')
458 checkVideo(notification.abuse.video, videoName, videoUUID) 362 checkVideo(notification.abuse.video, videoName, shortUUID)
459 } else { 363 } else {
460 expect(notification).to.satisfy((n: UserNotification) => { 364 expect(notification).to.satisfy((n: UserNotification) => {
461 return n === undefined || n.abuse === undefined || n.abuse.video.uuid !== videoUUID 365 return n === undefined || n.abuse === undefined || n.abuse.video.shortUUID !== shortUUID
462 }) 366 })
463 } 367 }
464 } 368 }
465 369
466 function emailNotificationFinder (email: object) { 370 function emailNotificationFinder (email: object) {
467 const text = email['text'] 371 const text = email['text']
468 return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1 372 return text.indexOf(shortUUID) !== -1 && text.indexOf('abuse') !== -1
469 } 373 }
470 374
471 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 375 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
472} 376}
473 377
474async function checkNewAbuseMessage (base: CheckerBaseParams, abuseId: number, message: string, toEmail: string, type: CheckerType) { 378async function checkNewAbuseMessage (options: CheckerBaseParams & {
379 abuseId: number
380 message: string
381 toEmail: string
382 checkType: CheckerType
383}) {
384 const { abuseId, message, toEmail } = options
475 const notificationType = UserNotificationType.ABUSE_NEW_MESSAGE 385 const notificationType = UserNotificationType.ABUSE_NEW_MESSAGE
476 386
477 function notificationChecker (notification: UserNotification, type: CheckerType) { 387 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
478 if (type === 'presence') { 388 if (checkType === 'presence') {
479 expect(notification).to.not.be.undefined 389 expect(notification).to.not.be.undefined
480 expect(notification.type).to.equal(notificationType) 390 expect(notification.type).to.equal(notificationType)
481 391
@@ -494,14 +404,19 @@ async function checkNewAbuseMessage (base: CheckerBaseParams, abuseId: number, m
494 return text.indexOf(message) !== -1 && to.length !== 0 404 return text.indexOf(message) !== -1 && to.length !== 0
495 } 405 }
496 406
497 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 407 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
498} 408}
499 409
500async function checkAbuseStateChange (base: CheckerBaseParams, abuseId: number, state: AbuseState, type: CheckerType) { 410async function checkAbuseStateChange (options: CheckerBaseParams & {
411 abuseId: number
412 state: AbuseState
413 checkType: CheckerType
414}) {
415 const { abuseId, state } = options
501 const notificationType = UserNotificationType.ABUSE_STATE_CHANGE 416 const notificationType = UserNotificationType.ABUSE_STATE_CHANGE
502 417
503 function notificationChecker (notification: UserNotification, type: CheckerType) { 418 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
504 if (type === 'presence') { 419 if (checkType === 'presence') {
505 expect(notification).to.not.be.undefined 420 expect(notification).to.not.be.undefined
506 expect(notification.type).to.equal(notificationType) 421 expect(notification.type).to.equal(notificationType)
507 422
@@ -524,39 +439,48 @@ async function checkAbuseStateChange (base: CheckerBaseParams, abuseId: number,
524 return text.indexOf(contains) !== -1 439 return text.indexOf(contains) !== -1
525 } 440 }
526 441
527 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 442 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
528} 443}
529 444
530async function checkNewCommentAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { 445async function checkNewCommentAbuseForModerators (options: CheckerBaseParams & {
446 shortUUID: string
447 videoName: string
448 checkType: CheckerType
449}) {
450 const { shortUUID, videoName } = options
531 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS 451 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
532 452
533 function notificationChecker (notification: UserNotification, type: CheckerType) { 453 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
534 if (type === 'presence') { 454 if (checkType === 'presence') {
535 expect(notification).to.not.be.undefined 455 expect(notification).to.not.be.undefined
536 expect(notification.type).to.equal(notificationType) 456 expect(notification.type).to.equal(notificationType)
537 457
538 expect(notification.abuse.id).to.be.a('number') 458 expect(notification.abuse.id).to.be.a('number')
539 checkVideo(notification.abuse.comment.video, videoName, videoUUID) 459 checkVideo(notification.abuse.comment.video, videoName, shortUUID)
540 } else { 460 } else {
541 expect(notification).to.satisfy((n: UserNotification) => { 461 expect(notification).to.satisfy((n: UserNotification) => {
542 return n === undefined || n.abuse === undefined || n.abuse.comment.video.uuid !== videoUUID 462 return n === undefined || n.abuse === undefined || n.abuse.comment.video.shortUUID !== shortUUID
543 }) 463 })
544 } 464 }
545 } 465 }
546 466
547 function emailNotificationFinder (email: object) { 467 function emailNotificationFinder (email: object) {
548 const text = email['text'] 468 const text = email['text']
549 return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1 469 return text.indexOf(shortUUID) !== -1 && text.indexOf('abuse') !== -1
550 } 470 }
551 471
552 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 472 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
553} 473}
554 474
555async function checkNewAccountAbuseForModerators (base: CheckerBaseParams, displayName: string, type: CheckerType) { 475async function checkNewAccountAbuseForModerators (options: CheckerBaseParams & {
476 displayName: string
477 checkType: CheckerType
478}) {
479 const { displayName } = options
556 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS 480 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
557 481
558 function notificationChecker (notification: UserNotification, type: CheckerType) { 482 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
559 if (type === 'presence') { 483 if (checkType === 'presence') {
560 expect(notification).to.not.be.undefined 484 expect(notification).to.not.be.undefined
561 expect(notification.type).to.equal(notificationType) 485 expect(notification.type).to.equal(notificationType)
562 486
@@ -574,40 +498,45 @@ async function checkNewAccountAbuseForModerators (base: CheckerBaseParams, displ
574 return text.indexOf(displayName) !== -1 && text.indexOf('abuse') !== -1 498 return text.indexOf(displayName) !== -1 && text.indexOf('abuse') !== -1
575 } 499 }
576 500
577 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 501 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
578} 502}
579 503
580async function checkVideoAutoBlacklistForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { 504async function checkVideoAutoBlacklistForModerators (options: CheckerBaseParams & {
505 shortUUID: string
506 videoName: string
507 checkType: CheckerType
508}) {
509 const { shortUUID, videoName } = options
581 const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS 510 const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS
582 511
583 function notificationChecker (notification: UserNotification, type: CheckerType) { 512 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
584 if (type === 'presence') { 513 if (checkType === 'presence') {
585 expect(notification).to.not.be.undefined 514 expect(notification).to.not.be.undefined
586 expect(notification.type).to.equal(notificationType) 515 expect(notification.type).to.equal(notificationType)
587 516
588 expect(notification.videoBlacklist.video.id).to.be.a('number') 517 expect(notification.videoBlacklist.video.id).to.be.a('number')
589 checkVideo(notification.videoBlacklist.video, videoName, videoUUID) 518 checkVideo(notification.videoBlacklist.video, videoName, shortUUID)
590 } else { 519 } else {
591 expect(notification).to.satisfy((n: UserNotification) => { 520 expect(notification).to.satisfy((n: UserNotification) => {
592 return n === undefined || n.video === undefined || n.video.uuid !== videoUUID 521 return n === undefined || n.video === undefined || n.video.shortUUID !== shortUUID
593 }) 522 })
594 } 523 }
595 } 524 }
596 525
597 function emailNotificationFinder (email: object) { 526 function emailNotificationFinder (email: object) {
598 const text = email['text'] 527 const text = email['text']
599 return text.indexOf(videoUUID) !== -1 && email['text'].indexOf('video-auto-blacklist/list') !== -1 528 return text.indexOf(shortUUID) !== -1 && email['text'].indexOf('video-auto-blacklist/list') !== -1
600 } 529 }
601 530
602 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 531 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
603} 532}
604 533
605async function checkNewBlacklistOnMyVideo ( 534async function checkNewBlacklistOnMyVideo (options: CheckerBaseParams & {
606 base: CheckerBaseParams, 535 shortUUID: string
607 videoUUID: string, 536 videoName: string
608 videoName: string,
609 blacklistType: 'blacklist' | 'unblacklist' 537 blacklistType: 'blacklist' | 'unblacklist'
610) { 538}) {
539 const { videoName, shortUUID, blacklistType } = options
611 const notificationType = blacklistType === 'blacklist' 540 const notificationType = blacklistType === 'blacklist'
612 ? UserNotificationType.BLACKLIST_ON_MY_VIDEO 541 ? UserNotificationType.BLACKLIST_ON_MY_VIDEO
613 : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO 542 : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO
@@ -618,22 +547,30 @@ async function checkNewBlacklistOnMyVideo (
618 547
619 const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video 548 const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video
620 549
621 checkVideo(video, videoName, videoUUID) 550 checkVideo(video, videoName, shortUUID)
622 } 551 }
623 552
624 function emailNotificationFinder (email: object) { 553 function emailNotificationFinder (email: object) {
625 const text = email['text'] 554 const text = email['text']
626 return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1 555 const blacklistText = blacklistType === 'blacklist'
556 ? 'blacklisted'
557 : 'unblacklisted'
558
559 return text.includes(shortUUID) && text.includes(blacklistText)
627 } 560 }
628 561
629 await checkNotification(base, notificationChecker, emailNotificationFinder, 'presence') 562 await checkNotification({ ...options, notificationChecker, emailNotificationFinder, checkType: 'presence' })
630} 563}
631 564
632async function checkNewPeerTubeVersion (base: CheckerBaseParams, latestVersion: string, type: CheckerType) { 565async function checkNewPeerTubeVersion (options: CheckerBaseParams & {
566 latestVersion: string
567 checkType: CheckerType
568}) {
569 const { latestVersion } = options
633 const notificationType = UserNotificationType.NEW_PEERTUBE_VERSION 570 const notificationType = UserNotificationType.NEW_PEERTUBE_VERSION
634 571
635 function notificationChecker (notification: UserNotification, type: CheckerType) { 572 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
636 if (type === 'presence') { 573 if (checkType === 'presence') {
637 expect(notification).to.not.be.undefined 574 expect(notification).to.not.be.undefined
638 expect(notification.type).to.equal(notificationType) 575 expect(notification.type).to.equal(notificationType)
639 576
@@ -652,14 +589,19 @@ async function checkNewPeerTubeVersion (base: CheckerBaseParams, latestVersion:
652 return text.includes(latestVersion) 589 return text.includes(latestVersion)
653 } 590 }
654 591
655 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 592 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
656} 593}
657 594
658async function checkNewPluginVersion (base: CheckerBaseParams, pluginType: PluginType, pluginName: string, type: CheckerType) { 595async function checkNewPluginVersion (options: CheckerBaseParams & {
596 pluginType: PluginType
597 pluginName: string
598 checkType: CheckerType
599}) {
600 const { pluginName, pluginType } = options
659 const notificationType = UserNotificationType.NEW_PLUGIN_VERSION 601 const notificationType = UserNotificationType.NEW_PLUGIN_VERSION
660 602
661 function notificationChecker (notification: UserNotification, type: CheckerType) { 603 function notificationChecker (notification: UserNotification, checkType: CheckerType) {
662 if (type === 'presence') { 604 if (checkType === 'presence') {
663 expect(notification).to.not.be.undefined 605 expect(notification).to.not.be.undefined
664 expect(notification.type).to.equal(notificationType) 606 expect(notification.type).to.equal(notificationType)
665 607
@@ -678,28 +620,7 @@ async function checkNewPluginVersion (base: CheckerBaseParams, pluginType: Plugi
678 return text.includes(pluginName) 620 return text.includes(pluginName)
679 } 621 }
680 622
681 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 623 await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
682}
683
684function getAllNotificationsSettings (): UserNotificationSetting {
685 return {
686 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
687 newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
688 abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
689 videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
690 blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
691 myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
692 myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
693 commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
694 newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
695 newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
696 newInstanceFollower: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
697 abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
698 abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
699 autoInstanceFollowing: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
700 newPeerTubeVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
701 newPluginVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
702 }
703} 624}
704 625
705async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: any = {}) { 626async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: any = {}) {
@@ -719,7 +640,7 @@ async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: an
719 limit: 20 640 limit: 20
720 } 641 }
721 } 642 }
722 const servers = await flushAndRunMultipleServers(serversCount, Object.assign(overrideConfig, overrideConfigArg)) 643 const servers = await createMultipleServers(serversCount, Object.assign(overrideConfig, overrideConfigArg))
723 644
724 await setAccessTokensToServers(servers) 645 await setAccessTokensToServers(servers)
725 646
@@ -727,42 +648,33 @@ async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: an
727 await doubleFollow(servers[0], servers[1]) 648 await doubleFollow(servers[0], servers[1])
728 } 649 }
729 650
730 const user = { 651 const user = { username: 'user_1', password: 'super password' }
731 username: 'user_1', 652 await servers[0].users.create({ ...user, videoQuota: 10 * 1000 * 1000 })
732 password: 'super password' 653 const userAccessToken = await servers[0].login.getAccessToken(user)
733 }
734 await createUser({
735 url: servers[0].url,
736 accessToken: servers[0].accessToken,
737 username: user.username,
738 password: user.password,
739 videoQuota: 10 * 1000 * 1000
740 })
741 const userAccessToken = await userLogin(servers[0], user)
742 654
743 await updateMyNotificationSettings(servers[0].url, userAccessToken, getAllNotificationsSettings()) 655 await servers[0].notifications.updateMySettings({ token: userAccessToken, settings: getAllNotificationsSettings() })
744 await updateMyNotificationSettings(servers[0].url, servers[0].accessToken, getAllNotificationsSettings()) 656 await servers[0].notifications.updateMySettings({ settings: getAllNotificationsSettings() })
745 657
746 if (serversCount > 1) { 658 if (serversCount > 1) {
747 await updateMyNotificationSettings(servers[1].url, servers[1].accessToken, getAllNotificationsSettings()) 659 await servers[1].notifications.updateMySettings({ settings: getAllNotificationsSettings() })
748 } 660 }
749 661
750 { 662 {
751 const socket = getUserNotificationSocket(servers[0].url, userAccessToken) 663 const socket = servers[0].socketIO.getUserNotificationSocket({ token: userAccessToken })
752 socket.on('new-notification', n => userNotifications.push(n)) 664 socket.on('new-notification', n => userNotifications.push(n))
753 } 665 }
754 { 666 {
755 const socket = getUserNotificationSocket(servers[0].url, servers[0].accessToken) 667 const socket = servers[0].socketIO.getUserNotificationSocket()
756 socket.on('new-notification', n => adminNotifications.push(n)) 668 socket.on('new-notification', n => adminNotifications.push(n))
757 } 669 }
758 670
759 if (serversCount > 1) { 671 if (serversCount > 1) {
760 const socket = getUserNotificationSocket(servers[1].url, servers[1].accessToken) 672 const socket = servers[1].socketIO.getUserNotificationSocket()
761 socket.on('new-notification', n => adminNotificationsServer2.push(n)) 673 socket.on('new-notification', n => adminNotificationsServer2.push(n))
762 } 674 }
763 675
764 const resChannel = await getMyUserInformation(servers[0].url, servers[0].accessToken) 676 const { videoChannels } = await servers[0].users.getMyInfo()
765 const channelId = resChannel.body.videoChannels[0].id 677 const channelId = videoChannels[0].id
766 678
767 return { 679 return {
768 userNotifications, 680 userNotifications,
@@ -778,11 +690,10 @@ async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: an
778// --------------------------------------------------------------------------- 690// ---------------------------------------------------------------------------
779 691
780export { 692export {
693 getAllNotificationsSettings,
694
781 CheckerBaseParams, 695 CheckerBaseParams,
782 CheckerType, 696 CheckerType,
783 getAllNotificationsSettings,
784 checkNotification,
785 markAsReadAllNotifications,
786 checkMyVideoImportIsFinished, 697 checkMyVideoImportIsFinished,
787 checkUserRegistered, 698 checkUserRegistered,
788 checkAutoInstanceFollowing, 699 checkAutoInstanceFollowing,
@@ -792,14 +703,10 @@ export {
792 checkNewCommentOnMyVideo, 703 checkNewCommentOnMyVideo,
793 checkNewBlacklistOnMyVideo, 704 checkNewBlacklistOnMyVideo,
794 checkCommentMention, 705 checkCommentMention,
795 updateMyNotificationSettings,
796 checkNewVideoAbuseForModerators, 706 checkNewVideoAbuseForModerators,
797 checkVideoAutoBlacklistForModerators, 707 checkVideoAutoBlacklistForModerators,
798 checkNewAbuseMessage, 708 checkNewAbuseMessage,
799 checkAbuseStateChange, 709 checkAbuseStateChange,
800 getUserNotifications,
801 markAsReadNotifications,
802 getLastNotification,
803 checkNewInstanceFollower, 710 checkNewInstanceFollower,
804 prepareNotificationsTest, 711 prepareNotificationsTest,
805 checkNewCommentAbuseForModerators, 712 checkNewCommentAbuseForModerators,
@@ -807,3 +714,82 @@ export {
807 checkNewPeerTubeVersion, 714 checkNewPeerTubeVersion,
808 checkNewPluginVersion 715 checkNewPluginVersion
809} 716}
717
718// ---------------------------------------------------------------------------
719
720async function checkNotification (options: CheckerBaseParams & {
721 notificationChecker: (notification: UserNotification, checkType: CheckerType) => void
722 emailNotificationFinder: (email: object) => boolean
723 checkType: CheckerType
724}) {
725 const { server, token, checkType, notificationChecker, emailNotificationFinder, socketNotifications, emails } = options
726
727 const check = options.check || { web: true, mail: true }
728
729 if (check.web) {
730 const notification = await server.notifications.getLastest({ token: token })
731
732 if (notification || checkType !== 'absence') {
733 notificationChecker(notification, checkType)
734 }
735
736 const socketNotification = socketNotifications.find(n => {
737 try {
738 notificationChecker(n, 'presence')
739 return true
740 } catch {
741 return false
742 }
743 })
744
745 if (checkType === 'presence') {
746 const obj = inspect(socketNotifications, { depth: 5 })
747 expect(socketNotification, 'The socket notification is absent when it should be present. ' + obj).to.not.be.undefined
748 } else {
749 const obj = inspect(socketNotification, { depth: 5 })
750 expect(socketNotification, 'The socket notification is present when it should not be present. ' + obj).to.be.undefined
751 }
752 }
753
754 if (check.mail) {
755 // Last email
756 const email = emails
757 .slice()
758 .reverse()
759 .find(e => emailNotificationFinder(e))
760
761 if (checkType === 'presence') {
762 const texts = emails.map(e => e.text)
763 expect(email, 'The email is absent when is should be present. ' + inspect(texts)).to.not.be.undefined
764 } else {
765 expect(email, 'The email is present when is should not be present. ' + inspect(email)).to.be.undefined
766 }
767 }
768}
769
770function checkVideo (video: any, videoName?: string, shortUUID?: string) {
771 if (videoName) {
772 expect(video.name).to.be.a('string')
773 expect(video.name).to.not.be.empty
774 expect(video.name).to.equal(videoName)
775 }
776
777 if (shortUUID) {
778 expect(video.shortUUID).to.be.a('string')
779 expect(video.shortUUID).to.not.be.empty
780 expect(video.shortUUID).to.equal(shortUUID)
781 }
782
783 expect(video.id).to.be.a('number')
784}
785
786function checkActor (actor: any) {
787 expect(actor.displayName).to.be.a('string')
788 expect(actor.displayName).to.not.be.empty
789 expect(actor.host).to.not.be.undefined
790}
791
792function checkComment (comment: any, commentId: number, threadId: number) {
793 expect(comment.id).to.equal(commentId)
794 expect(comment.threadId).to.equal(threadId)
795}
diff --git a/shared/extra-utils/users/subscriptions-command.ts b/shared/extra-utils/users/subscriptions-command.ts
new file mode 100644
index 000000000..edc60e612
--- /dev/null
+++ b/shared/extra-utils/users/subscriptions-command.ts
@@ -0,0 +1,99 @@
1import { HttpStatusCode, ResultList, Video, VideoChannel } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class SubscriptionsCommand extends AbstractCommand {
5
6 add (options: OverrideCommandOptions & {
7 targetUri: string
8 }) {
9 const path = '/api/v1/users/me/subscriptions'
10
11 return this.postBodyRequest({
12 ...options,
13
14 path,
15 fields: { uri: options.targetUri },
16 implicitToken: true,
17 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
18 })
19 }
20
21 list (options: OverrideCommandOptions & {
22 sort?: string // default -createdAt
23 search?: string
24 } = {}) {
25 const { sort = '-createdAt', search } = options
26 const path = '/api/v1/users/me/subscriptions'
27
28 return this.getRequestBody<ResultList<VideoChannel>>({
29 ...options,
30
31 path,
32 query: {
33 sort,
34 search
35 },
36 implicitToken: true,
37 defaultExpectedStatus: HttpStatusCode.OK_200
38 })
39 }
40
41 listVideos (options: OverrideCommandOptions & {
42 sort?: string // default -createdAt
43 } = {}) {
44 const { sort = '-createdAt' } = options
45 const path = '/api/v1/users/me/subscriptions/videos'
46
47 return this.getRequestBody<ResultList<Video>>({
48 ...options,
49
50 path,
51 query: { sort },
52 implicitToken: true,
53 defaultExpectedStatus: HttpStatusCode.OK_200
54 })
55 }
56
57 get (options: OverrideCommandOptions & {
58 uri: string
59 }) {
60 const path = '/api/v1/users/me/subscriptions/' + options.uri
61
62 return this.getRequestBody<VideoChannel>({
63 ...options,
64
65 path,
66 implicitToken: true,
67 defaultExpectedStatus: HttpStatusCode.OK_200
68 })
69 }
70
71 remove (options: OverrideCommandOptions & {
72 uri: string
73 }) {
74 const path = '/api/v1/users/me/subscriptions/' + options.uri
75
76 return this.deleteRequest({
77 ...options,
78
79 path,
80 implicitToken: true,
81 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
82 })
83 }
84
85 exist (options: OverrideCommandOptions & {
86 uris: string[]
87 }) {
88 const path = '/api/v1/users/me/subscriptions/exist'
89
90 return this.getRequestBody<{ [id: string ]: boolean }>({
91 ...options,
92
93 path,
94 query: { 'uris[]': options.uris },
95 implicitToken: true,
96 defaultExpectedStatus: HttpStatusCode.OK_200
97 })
98 }
99}
diff --git a/shared/extra-utils/users/user-subscriptions.ts b/shared/extra-utils/users/user-subscriptions.ts
deleted file mode 100644
index edc7a3562..000000000
--- a/shared/extra-utils/users/user-subscriptions.ts
+++ /dev/null
@@ -1,93 +0,0 @@
1import { makeDeleteRequest, makeGetRequest, makePostBodyRequest } from '../requests/requests'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function addUserSubscription (url: string, token: string, targetUri: string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
5 const path = '/api/v1/users/me/subscriptions'
6
7 return makePostBodyRequest({
8 url,
9 path,
10 token,
11 statusCodeExpected,
12 fields: { uri: targetUri }
13 })
14}
15
16function listUserSubscriptions (parameters: {
17 url: string
18 token: string
19 sort?: string
20 search?: string
21 statusCodeExpected?: number
22}) {
23 const { url, token, sort = '-createdAt', search, statusCodeExpected = HttpStatusCode.OK_200 } = parameters
24 const path = '/api/v1/users/me/subscriptions'
25
26 return makeGetRequest({
27 url,
28 path,
29 token,
30 statusCodeExpected,
31 query: {
32 sort,
33 search
34 }
35 })
36}
37
38function listUserSubscriptionVideos (url: string, token: string, sort = '-createdAt', statusCodeExpected = HttpStatusCode.OK_200) {
39 const path = '/api/v1/users/me/subscriptions/videos'
40
41 return makeGetRequest({
42 url,
43 path,
44 token,
45 statusCodeExpected,
46 query: { sort }
47 })
48}
49
50function getUserSubscription (url: string, token: string, uri: string, statusCodeExpected = HttpStatusCode.OK_200) {
51 const path = '/api/v1/users/me/subscriptions/' + uri
52
53 return makeGetRequest({
54 url,
55 path,
56 token,
57 statusCodeExpected
58 })
59}
60
61function removeUserSubscription (url: string, token: string, uri: string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
62 const path = '/api/v1/users/me/subscriptions/' + uri
63
64 return makeDeleteRequest({
65 url,
66 path,
67 token,
68 statusCodeExpected
69 })
70}
71
72function areSubscriptionsExist (url: string, token: string, uris: string[], statusCodeExpected = HttpStatusCode.OK_200) {
73 const path = '/api/v1/users/me/subscriptions/exist'
74
75 return makeGetRequest({
76 url,
77 path,
78 query: { 'uris[]': uris },
79 token,
80 statusCodeExpected
81 })
82}
83
84// ---------------------------------------------------------------------------
85
86export {
87 areSubscriptionsExist,
88 addUserSubscription,
89 listUserSubscriptions,
90 getUserSubscription,
91 listUserSubscriptionVideos,
92 removeUserSubscription
93}
diff --git a/shared/extra-utils/users/users-command.ts b/shared/extra-utils/users/users-command.ts
new file mode 100644
index 000000000..ddd20d041
--- /dev/null
+++ b/shared/extra-utils/users/users-command.ts
@@ -0,0 +1,415 @@
1import { omit } from 'lodash'
2import { pick } from '@shared/core-utils'
3import {
4 HttpStatusCode,
5 MyUser,
6 ResultList,
7 User,
8 UserAdminFlag,
9 UserCreateResult,
10 UserRole,
11 UserUpdate,
12 UserUpdateMe,
13 UserVideoQuota,
14 UserVideoRate
15} from '@shared/models'
16import { ScopedToken } from '@shared/models/users/user-scoped-token'
17import { unwrapBody } from '../requests'
18import { AbstractCommand, OverrideCommandOptions } from '../shared'
19
20export class UsersCommand extends AbstractCommand {
21
22 askResetPassword (options: OverrideCommandOptions & {
23 email: string
24 }) {
25 const { email } = options
26 const path = '/api/v1/users/ask-reset-password'
27
28 return this.postBodyRequest({
29 ...options,
30
31 path,
32 fields: { email },
33 implicitToken: false,
34 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
35 })
36 }
37
38 resetPassword (options: OverrideCommandOptions & {
39 userId: number
40 verificationString: string
41 password: string
42 }) {
43 const { userId, verificationString, password } = options
44 const path = '/api/v1/users/' + userId + '/reset-password'
45
46 return this.postBodyRequest({
47 ...options,
48
49 path,
50 fields: { password, verificationString },
51 implicitToken: false,
52 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
53 })
54 }
55
56 // ---------------------------------------------------------------------------
57
58 askSendVerifyEmail (options: OverrideCommandOptions & {
59 email: string
60 }) {
61 const { email } = options
62 const path = '/api/v1/users/ask-send-verify-email'
63
64 return this.postBodyRequest({
65 ...options,
66
67 path,
68 fields: { email },
69 implicitToken: false,
70 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
71 })
72 }
73
74 verifyEmail (options: OverrideCommandOptions & {
75 userId: number
76 verificationString: string
77 isPendingEmail?: boolean // default false
78 }) {
79 const { userId, verificationString, isPendingEmail = false } = options
80 const path = '/api/v1/users/' + userId + '/verify-email'
81
82 return this.postBodyRequest({
83 ...options,
84
85 path,
86 fields: {
87 verificationString,
88 isPendingEmail
89 },
90 implicitToken: false,
91 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
92 })
93 }
94
95 // ---------------------------------------------------------------------------
96
97 banUser (options: OverrideCommandOptions & {
98 userId: number
99 reason?: string
100 }) {
101 const { userId, reason } = options
102 const path = '/api/v1/users' + '/' + userId + '/block'
103
104 return this.postBodyRequest({
105 ...options,
106
107 path,
108 fields: { reason },
109 implicitToken: true,
110 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
111 })
112 }
113
114 unbanUser (options: OverrideCommandOptions & {
115 userId: number
116 }) {
117 const { userId } = options
118 const path = '/api/v1/users' + '/' + userId + '/unblock'
119
120 return this.postBodyRequest({
121 ...options,
122
123 path,
124 implicitToken: true,
125 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
126 })
127 }
128
129 // ---------------------------------------------------------------------------
130
131 getMyScopedTokens (options: OverrideCommandOptions = {}) {
132 const path = '/api/v1/users/scoped-tokens'
133
134 return this.getRequestBody<ScopedToken>({
135 ...options,
136
137 path,
138 implicitToken: true,
139 defaultExpectedStatus: HttpStatusCode.OK_200
140 })
141 }
142
143 renewMyScopedTokens (options: OverrideCommandOptions = {}) {
144 const path = '/api/v1/users/scoped-tokens'
145
146 return this.postBodyRequest({
147 ...options,
148
149 path,
150 implicitToken: true,
151 defaultExpectedStatus: HttpStatusCode.OK_200
152 })
153 }
154
155 // ---------------------------------------------------------------------------
156
157 create (options: OverrideCommandOptions & {
158 username: string
159 password?: string
160 videoQuota?: number
161 videoQuotaDaily?: number
162 role?: UserRole
163 adminFlags?: UserAdminFlag
164 }) {
165 const {
166 username,
167 adminFlags,
168 password = 'password',
169 videoQuota = 42000000,
170 videoQuotaDaily = -1,
171 role = UserRole.USER
172 } = options
173
174 const path = '/api/v1/users'
175
176 return unwrapBody<{ user: UserCreateResult }>(this.postBodyRequest({
177 ...options,
178
179 path,
180 fields: {
181 username,
182 password,
183 role,
184 adminFlags,
185 email: username + '@example.com',
186 videoQuota,
187 videoQuotaDaily
188 },
189 implicitToken: true,
190 defaultExpectedStatus: HttpStatusCode.OK_200
191 })).then(res => res.user)
192 }
193
194 async generate (username: string) {
195 const password = 'password'
196 const user = await this.create({ username, password })
197
198 const token = await this.server.login.getAccessToken({ username, password })
199
200 const me = await this.getMyInfo({ token })
201
202 return {
203 token,
204 userId: user.id,
205 userChannelId: me.videoChannels[0].id
206 }
207 }
208
209 async generateUserAndToken (username: string) {
210 const password = 'password'
211 await this.create({ username, password })
212
213 return this.server.login.getAccessToken({ username, password })
214 }
215
216 register (options: OverrideCommandOptions & {
217 username: string
218 password?: string
219 displayName?: string
220 channel?: {
221 name: string
222 displayName: string
223 }
224 }) {
225 const { username, password = 'password', displayName, channel } = options
226 const path = '/api/v1/users/register'
227
228 return this.postBodyRequest({
229 ...options,
230
231 path,
232 fields: {
233 username,
234 password,
235 email: username + '@example.com',
236 displayName,
237 channel
238 },
239 implicitToken: false,
240 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
241 })
242 }
243
244 // ---------------------------------------------------------------------------
245
246 getMyInfo (options: OverrideCommandOptions = {}) {
247 const path = '/api/v1/users/me'
248
249 return this.getRequestBody<MyUser>({
250 ...options,
251
252 path,
253 implicitToken: true,
254 defaultExpectedStatus: HttpStatusCode.OK_200
255 })
256 }
257
258 getMyQuotaUsed (options: OverrideCommandOptions = {}) {
259 const path = '/api/v1/users/me/video-quota-used'
260
261 return this.getRequestBody<UserVideoQuota>({
262 ...options,
263
264 path,
265 implicitToken: true,
266 defaultExpectedStatus: HttpStatusCode.OK_200
267 })
268 }
269
270 getMyRating (options: OverrideCommandOptions & {
271 videoId: number | string
272 }) {
273 const { videoId } = options
274 const path = '/api/v1/users/me/videos/' + videoId + '/rating'
275
276 return this.getRequestBody<UserVideoRate>({
277 ...options,
278
279 path,
280 implicitToken: true,
281 defaultExpectedStatus: HttpStatusCode.OK_200
282 })
283 }
284
285 deleteMe (options: OverrideCommandOptions = {}) {
286 const path = '/api/v1/users/me'
287
288 return this.deleteRequest({
289 ...options,
290
291 path,
292 implicitToken: true,
293 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
294 })
295 }
296
297 updateMe (options: OverrideCommandOptions & UserUpdateMe) {
298 const path = '/api/v1/users/me'
299
300 const toSend: UserUpdateMe = omit(options, 'url', 'accessToken')
301
302 return this.putBodyRequest({
303 ...options,
304
305 path,
306 fields: toSend,
307 implicitToken: true,
308 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
309 })
310 }
311
312 updateMyAvatar (options: OverrideCommandOptions & {
313 fixture: string
314 }) {
315 const { fixture } = options
316 const path = '/api/v1/users/me/avatar/pick'
317
318 return this.updateImageRequest({
319 ...options,
320
321 path,
322 fixture,
323 fieldname: 'avatarfile',
324
325 implicitToken: true,
326 defaultExpectedStatus: HttpStatusCode.OK_200
327 })
328 }
329
330 // ---------------------------------------------------------------------------
331
332 get (options: OverrideCommandOptions & {
333 userId: number
334 withStats?: boolean // default false
335 }) {
336 const { userId, withStats } = options
337 const path = '/api/v1/users/' + userId
338
339 return this.getRequestBody<User>({
340 ...options,
341
342 path,
343 query: { withStats },
344 implicitToken: true,
345 defaultExpectedStatus: HttpStatusCode.OK_200
346 })
347 }
348
349 list (options: OverrideCommandOptions & {
350 start?: number
351 count?: number
352 sort?: string
353 search?: string
354 blocked?: boolean
355 } = {}) {
356 const path = '/api/v1/users'
357
358 return this.getRequestBody<ResultList<User>>({
359 ...options,
360
361 path,
362 query: pick(options, [ 'start', 'count', 'sort', 'search', 'blocked' ]),
363 implicitToken: true,
364 defaultExpectedStatus: HttpStatusCode.OK_200
365 })
366 }
367
368 remove (options: OverrideCommandOptions & {
369 userId: number
370 }) {
371 const { userId } = options
372 const path = '/api/v1/users/' + userId
373
374 return this.deleteRequest({
375 ...options,
376
377 path,
378 implicitToken: true,
379 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
380 })
381 }
382
383 update (options: OverrideCommandOptions & {
384 userId: number
385 email?: string
386 emailVerified?: boolean
387 videoQuota?: number
388 videoQuotaDaily?: number
389 password?: string
390 adminFlags?: UserAdminFlag
391 pluginAuth?: string
392 role?: UserRole
393 }) {
394 const path = '/api/v1/users/' + options.userId
395
396 const toSend: UserUpdate = {}
397 if (options.password !== undefined && options.password !== null) toSend.password = options.password
398 if (options.email !== undefined && options.email !== null) toSend.email = options.email
399 if (options.emailVerified !== undefined && options.emailVerified !== null) toSend.emailVerified = options.emailVerified
400 if (options.videoQuota !== undefined && options.videoQuota !== null) toSend.videoQuota = options.videoQuota
401 if (options.videoQuotaDaily !== undefined && options.videoQuotaDaily !== null) toSend.videoQuotaDaily = options.videoQuotaDaily
402 if (options.role !== undefined && options.role !== null) toSend.role = options.role
403 if (options.adminFlags !== undefined && options.adminFlags !== null) toSend.adminFlags = options.adminFlags
404 if (options.pluginAuth !== undefined) toSend.pluginAuth = options.pluginAuth
405
406 return this.putBodyRequest({
407 ...options,
408
409 path,
410 fields: toSend,
411 implicitToken: true,
412 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
413 })
414 }
415}
diff --git a/shared/extra-utils/users/users.ts b/shared/extra-utils/users/users.ts
deleted file mode 100644
index 0f15962ad..000000000
--- a/shared/extra-utils/users/users.ts
+++ /dev/null
@@ -1,415 +0,0 @@
1import { omit } from 'lodash'
2import * as request from 'supertest'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4import { UserUpdateMe } from '../../models/users'
5import { UserAdminFlag } from '../../models/users/user-flag.model'
6import { UserRegister } from '../../models/users/user-register.model'
7import { UserRole } from '../../models/users/user-role'
8import { makeGetRequest, makePostBodyRequest, makePutBodyRequest, updateImageRequest } from '../requests/requests'
9import { ServerInfo } from '../server/servers'
10import { userLogin } from './login'
11
12function createUser (parameters: {
13 url: string
14 accessToken: string
15 username: string
16 password: string
17 videoQuota?: number
18 videoQuotaDaily?: number
19 role?: UserRole
20 adminFlags?: UserAdminFlag
21 specialStatus?: number
22}) {
23 const {
24 url,
25 accessToken,
26 username,
27 adminFlags,
28 password = 'password',
29 videoQuota = 1000000,
30 videoQuotaDaily = -1,
31 role = UserRole.USER,
32 specialStatus = HttpStatusCode.OK_200
33 } = parameters
34
35 const path = '/api/v1/users'
36 const body = {
37 username,
38 password,
39 role,
40 adminFlags,
41 email: username + '@example.com',
42 videoQuota,
43 videoQuotaDaily
44 }
45
46 return request(url)
47 .post(path)
48 .set('Accept', 'application/json')
49 .set('Authorization', 'Bearer ' + accessToken)
50 .send(body)
51 .expect(specialStatus)
52}
53
54async function generateUser (server: ServerInfo, username: string) {
55 const password = 'my super password'
56 const resCreate = await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password })
57
58 const token = await userLogin(server, { username, password })
59
60 const resMe = await getMyUserInformation(server.url, token)
61
62 return {
63 token,
64 userId: resCreate.body.user.id,
65 userChannelId: resMe.body.videoChannels[0].id
66 }
67}
68
69async function generateUserAccessToken (server: ServerInfo, username: string) {
70 const password = 'my super password'
71 await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password })
72
73 return userLogin(server, { username, password })
74}
75
76function registerUser (url: string, username: string, password: string, specialStatus = HttpStatusCode.NO_CONTENT_204) {
77 const path = '/api/v1/users/register'
78 const body = {
79 username,
80 password,
81 email: username + '@example.com'
82 }
83
84 return request(url)
85 .post(path)
86 .set('Accept', 'application/json')
87 .send(body)
88 .expect(specialStatus)
89}
90
91function registerUserWithChannel (options: {
92 url: string
93 user: { username: string, password: string, displayName?: string }
94 channel: { name: string, displayName: string }
95}) {
96 const path = '/api/v1/users/register'
97 const body: UserRegister = {
98 username: options.user.username,
99 password: options.user.password,
100 email: options.user.username + '@example.com',
101 channel: options.channel
102 }
103
104 if (options.user.displayName) {
105 Object.assign(body, { displayName: options.user.displayName })
106 }
107
108 return makePostBodyRequest({
109 url: options.url,
110 path,
111 fields: body,
112 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
113 })
114}
115
116function getMyUserInformation (url: string, accessToken: string, specialStatus = HttpStatusCode.OK_200) {
117 const path = '/api/v1/users/me'
118
119 return request(url)
120 .get(path)
121 .set('Accept', 'application/json')
122 .set('Authorization', 'Bearer ' + accessToken)
123 .expect(specialStatus)
124 .expect('Content-Type', /json/)
125}
126
127function getUserScopedTokens (url: string, token: string, statusCodeExpected = HttpStatusCode.OK_200) {
128 const path = '/api/v1/users/scoped-tokens'
129
130 return makeGetRequest({
131 url,
132 path,
133 token,
134 statusCodeExpected
135 })
136}
137
138function renewUserScopedTokens (url: string, token: string, statusCodeExpected = HttpStatusCode.OK_200) {
139 const path = '/api/v1/users/scoped-tokens'
140
141 return makePostBodyRequest({
142 url,
143 path,
144 token,
145 statusCodeExpected
146 })
147}
148
149function deleteMe (url: string, accessToken: string, specialStatus = HttpStatusCode.NO_CONTENT_204) {
150 const path = '/api/v1/users/me'
151
152 return request(url)
153 .delete(path)
154 .set('Accept', 'application/json')
155 .set('Authorization', 'Bearer ' + accessToken)
156 .expect(specialStatus)
157}
158
159function getMyUserVideoQuotaUsed (url: string, accessToken: string, specialStatus = HttpStatusCode.OK_200) {
160 const path = '/api/v1/users/me/video-quota-used'
161
162 return request(url)
163 .get(path)
164 .set('Accept', 'application/json')
165 .set('Authorization', 'Bearer ' + accessToken)
166 .expect(specialStatus)
167 .expect('Content-Type', /json/)
168}
169
170function getUserInformation (url: string, accessToken: string, userId: number, withStats = false) {
171 const path = '/api/v1/users/' + userId
172
173 return request(url)
174 .get(path)
175 .query({ withStats })
176 .set('Accept', 'application/json')
177 .set('Authorization', 'Bearer ' + accessToken)
178 .expect(HttpStatusCode.OK_200)
179 .expect('Content-Type', /json/)
180}
181
182function getMyUserVideoRating (url: string, accessToken: string, videoId: number | string, specialStatus = HttpStatusCode.OK_200) {
183 const path = '/api/v1/users/me/videos/' + videoId + '/rating'
184
185 return request(url)
186 .get(path)
187 .set('Accept', 'application/json')
188 .set('Authorization', 'Bearer ' + accessToken)
189 .expect(specialStatus)
190 .expect('Content-Type', /json/)
191}
192
193function getUsersList (url: string, accessToken: string) {
194 const path = '/api/v1/users'
195
196 return request(url)
197 .get(path)
198 .set('Accept', 'application/json')
199 .set('Authorization', 'Bearer ' + accessToken)
200 .expect(HttpStatusCode.OK_200)
201 .expect('Content-Type', /json/)
202}
203
204function getUsersListPaginationAndSort (
205 url: string,
206 accessToken: string,
207 start: number,
208 count: number,
209 sort: string,
210 search?: string,
211 blocked?: boolean
212) {
213 const path = '/api/v1/users'
214
215 const query = {
216 start,
217 count,
218 sort,
219 search,
220 blocked
221 }
222
223 return request(url)
224 .get(path)
225 .query(query)
226 .set('Accept', 'application/json')
227 .set('Authorization', 'Bearer ' + accessToken)
228 .expect(HttpStatusCode.OK_200)
229 .expect('Content-Type', /json/)
230}
231
232function removeUser (url: string, userId: number | string, accessToken: string, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
233 const path = '/api/v1/users'
234
235 return request(url)
236 .delete(path + '/' + userId)
237 .set('Accept', 'application/json')
238 .set('Authorization', 'Bearer ' + accessToken)
239 .expect(expectedStatus)
240}
241
242function blockUser (
243 url: string,
244 userId: number | string,
245 accessToken: string,
246 expectedStatus = HttpStatusCode.NO_CONTENT_204,
247 reason?: string
248) {
249 const path = '/api/v1/users'
250 let body: any
251 if (reason) body = { reason }
252
253 return request(url)
254 .post(path + '/' + userId + '/block')
255 .send(body)
256 .set('Accept', 'application/json')
257 .set('Authorization', 'Bearer ' + accessToken)
258 .expect(expectedStatus)
259}
260
261function unblockUser (url: string, userId: number | string, accessToken: string, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
262 const path = '/api/v1/users'
263
264 return request(url)
265 .post(path + '/' + userId + '/unblock')
266 .set('Accept', 'application/json')
267 .set('Authorization', 'Bearer ' + accessToken)
268 .expect(expectedStatus)
269}
270
271function updateMyUser (options: { url: string, accessToken: string, statusCodeExpected?: HttpStatusCode } & UserUpdateMe) {
272 const path = '/api/v1/users/me'
273
274 const toSend: UserUpdateMe = omit(options, 'url', 'accessToken')
275
276 return makePutBodyRequest({
277 url: options.url,
278 path,
279 token: options.accessToken,
280 fields: toSend,
281 statusCodeExpected: options.statusCodeExpected || HttpStatusCode.NO_CONTENT_204
282 })
283}
284
285function updateMyAvatar (options: {
286 url: string
287 accessToken: string
288 fixture: string
289}) {
290 const path = '/api/v1/users/me/avatar/pick'
291
292 return updateImageRequest({ ...options, path, fieldname: 'avatarfile' })
293}
294
295function updateUser (options: {
296 url: string
297 userId: number
298 accessToken: string
299 email?: string
300 emailVerified?: boolean
301 videoQuota?: number
302 videoQuotaDaily?: number
303 password?: string
304 adminFlags?: UserAdminFlag
305 pluginAuth?: string
306 role?: UserRole
307}) {
308 const path = '/api/v1/users/' + options.userId
309
310 const toSend = {}
311 if (options.password !== undefined && options.password !== null) toSend['password'] = options.password
312 if (options.email !== undefined && options.email !== null) toSend['email'] = options.email
313 if (options.emailVerified !== undefined && options.emailVerified !== null) toSend['emailVerified'] = options.emailVerified
314 if (options.videoQuota !== undefined && options.videoQuota !== null) toSend['videoQuota'] = options.videoQuota
315 if (options.videoQuotaDaily !== undefined && options.videoQuotaDaily !== null) toSend['videoQuotaDaily'] = options.videoQuotaDaily
316 if (options.role !== undefined && options.role !== null) toSend['role'] = options.role
317 if (options.adminFlags !== undefined && options.adminFlags !== null) toSend['adminFlags'] = options.adminFlags
318 if (options.pluginAuth !== undefined) toSend['pluginAuth'] = options.pluginAuth
319
320 return makePutBodyRequest({
321 url: options.url,
322 path,
323 token: options.accessToken,
324 fields: toSend,
325 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
326 })
327}
328
329function askResetPassword (url: string, email: string) {
330 const path = '/api/v1/users/ask-reset-password'
331
332 return makePostBodyRequest({
333 url,
334 path,
335 fields: { email },
336 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
337 })
338}
339
340function resetPassword (
341 url: string,
342 userId: number,
343 verificationString: string,
344 password: string,
345 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
346) {
347 const path = '/api/v1/users/' + userId + '/reset-password'
348
349 return makePostBodyRequest({
350 url,
351 path,
352 fields: { password, verificationString },
353 statusCodeExpected
354 })
355}
356
357function askSendVerifyEmail (url: string, email: string) {
358 const path = '/api/v1/users/ask-send-verify-email'
359
360 return makePostBodyRequest({
361 url,
362 path,
363 fields: { email },
364 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
365 })
366}
367
368function verifyEmail (
369 url: string,
370 userId: number,
371 verificationString: string,
372 isPendingEmail = false,
373 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
374) {
375 const path = '/api/v1/users/' + userId + '/verify-email'
376
377 return makePostBodyRequest({
378 url,
379 path,
380 fields: {
381 verificationString,
382 isPendingEmail
383 },
384 statusCodeExpected
385 })
386}
387
388// ---------------------------------------------------------------------------
389
390export {
391 createUser,
392 registerUser,
393 getMyUserInformation,
394 getMyUserVideoRating,
395 deleteMe,
396 registerUserWithChannel,
397 getMyUserVideoQuotaUsed,
398 getUsersList,
399 getUsersListPaginationAndSort,
400 removeUser,
401 updateUser,
402 updateMyUser,
403 getUserInformation,
404 blockUser,
405 unblockUser,
406 askResetPassword,
407 resetPassword,
408 renewUserScopedTokens,
409 updateMyAvatar,
410 generateUser,
411 askSendVerifyEmail,
412 generateUserAccessToken,
413 verifyEmail,
414 getUserScopedTokens
415}
diff --git a/shared/extra-utils/videos/blacklist-command.ts b/shared/extra-utils/videos/blacklist-command.ts
new file mode 100644
index 000000000..3a2ef89ba
--- /dev/null
+++ b/shared/extra-utils/videos/blacklist-command.ts
@@ -0,0 +1,76 @@
1
2import { HttpStatusCode, ResultList } from '@shared/models'
3import { VideoBlacklist, VideoBlacklistType } from '../../models/videos'
4import { AbstractCommand, OverrideCommandOptions } from '../shared'
5
6export class BlacklistCommand extends AbstractCommand {
7
8 add (options: OverrideCommandOptions & {
9 videoId: number | string
10 reason?: string
11 unfederate?: boolean
12 }) {
13 const { videoId, reason, unfederate } = options
14 const path = '/api/v1/videos/' + videoId + '/blacklist'
15
16 return this.postBodyRequest({
17 ...options,
18
19 path,
20 fields: { reason, unfederate },
21 implicitToken: true,
22 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
23 })
24 }
25
26 update (options: OverrideCommandOptions & {
27 videoId: number | string
28 reason?: string
29 }) {
30 const { videoId, reason } = options
31 const path = '/api/v1/videos/' + videoId + '/blacklist'
32
33 return this.putBodyRequest({
34 ...options,
35
36 path,
37 fields: { reason },
38 implicitToken: true,
39 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
40 })
41 }
42
43 remove (options: OverrideCommandOptions & {
44 videoId: number | string
45 }) {
46 const { videoId } = options
47 const path = '/api/v1/videos/' + videoId + '/blacklist'
48
49 return this.deleteRequest({
50 ...options,
51
52 path,
53 implicitToken: true,
54 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
55 })
56 }
57
58 list (options: OverrideCommandOptions & {
59 sort?: string
60 type?: VideoBlacklistType
61 } = {}) {
62 const { sort, type } = options
63 const path = '/api/v1/videos/blacklist/'
64
65 const query = { sort, type }
66
67 return this.getRequestBody<ResultList<VideoBlacklist>>({
68 ...options,
69
70 path,
71 query,
72 implicitToken: true,
73 defaultExpectedStatus: HttpStatusCode.OK_200
74 })
75 }
76}
diff --git a/shared/extra-utils/videos/captions-command.ts b/shared/extra-utils/videos/captions-command.ts
new file mode 100644
index 000000000..a65ea99e3
--- /dev/null
+++ b/shared/extra-utils/videos/captions-command.ts
@@ -0,0 +1,65 @@
1import { HttpStatusCode, ResultList, VideoCaption } from '@shared/models'
2import { buildAbsoluteFixturePath } from '../miscs'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class CaptionsCommand extends AbstractCommand {
6
7 add (options: OverrideCommandOptions & {
8 videoId: string | number
9 language: string
10 fixture: string
11 mimeType?: string
12 }) {
13 const { videoId, language, fixture, mimeType } = options
14
15 const path = '/api/v1/videos/' + videoId + '/captions/' + language
16
17 const captionfile = buildAbsoluteFixturePath(fixture)
18 const captionfileAttach = mimeType
19 ? [ captionfile, { contentType: mimeType } ]
20 : captionfile
21
22 return this.putUploadRequest({
23 ...options,
24
25 path,
26 fields: {},
27 attaches: {
28 captionfile: captionfileAttach
29 },
30 implicitToken: true,
31 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
32 })
33 }
34
35 list (options: OverrideCommandOptions & {
36 videoId: string | number
37 }) {
38 const { videoId } = options
39 const path = '/api/v1/videos/' + videoId + '/captions'
40
41 return this.getRequestBody<ResultList<VideoCaption>>({
42 ...options,
43
44 path,
45 implicitToken: false,
46 defaultExpectedStatus: HttpStatusCode.OK_200
47 })
48 }
49
50 delete (options: OverrideCommandOptions & {
51 videoId: string | number
52 language: string
53 }) {
54 const { videoId, language } = options
55 const path = '/api/v1/videos/' + videoId + '/captions/' + language
56
57 return this.deleteRequest({
58 ...options,
59
60 path,
61 implicitToken: true,
62 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
63 })
64 }
65}
diff --git a/shared/extra-utils/videos/captions.ts b/shared/extra-utils/videos/captions.ts
new file mode 100644
index 000000000..ff8a43366
--- /dev/null
+++ b/shared/extra-utils/videos/captions.ts
@@ -0,0 +1,17 @@
1import { expect } from 'chai'
2import * as request from 'supertest'
3import { HttpStatusCode } from '@shared/models'
4
5async function testCaptionFile (url: string, captionPath: string, containsString: string) {
6 const res = await request(url)
7 .get(captionPath)
8 .expect(HttpStatusCode.OK_200)
9
10 expect(res.text).to.contain(containsString)
11}
12
13// ---------------------------------------------------------------------------
14
15export {
16 testCaptionFile
17}
diff --git a/shared/extra-utils/videos/change-ownership-command.ts b/shared/extra-utils/videos/change-ownership-command.ts
new file mode 100644
index 000000000..ad4c726ef
--- /dev/null
+++ b/shared/extra-utils/videos/change-ownership-command.ts
@@ -0,0 +1,68 @@
1
2import { HttpStatusCode, ResultList, VideoChangeOwnership } from '@shared/models'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class ChangeOwnershipCommand extends AbstractCommand {
6
7 create (options: OverrideCommandOptions & {
8 videoId: number | string
9 username: string
10 }) {
11 const { videoId, username } = options
12 const path = '/api/v1/videos/' + videoId + '/give-ownership'
13
14 return this.postBodyRequest({
15 ...options,
16
17 path,
18 fields: { username },
19 implicitToken: true,
20 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
21 })
22 }
23
24 list (options: OverrideCommandOptions = {}) {
25 const path = '/api/v1/videos/ownership'
26
27 return this.getRequestBody<ResultList<VideoChangeOwnership>>({
28 ...options,
29
30 path,
31 query: { sort: '-createdAt' },
32 implicitToken: true,
33 defaultExpectedStatus: HttpStatusCode.OK_200
34 })
35 }
36
37 accept (options: OverrideCommandOptions & {
38 ownershipId: number
39 channelId: number
40 }) {
41 const { ownershipId, channelId } = options
42 const path = '/api/v1/videos/ownership/' + ownershipId + '/accept'
43
44 return this.postBodyRequest({
45 ...options,
46
47 path,
48 fields: { channelId },
49 implicitToken: true,
50 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
51 })
52 }
53
54 refuse (options: OverrideCommandOptions & {
55 ownershipId: number
56 }) {
57 const { ownershipId } = options
58 const path = '/api/v1/videos/ownership/' + ownershipId + '/refuse'
59
60 return this.postBodyRequest({
61 ...options,
62
63 path,
64 implicitToken: true,
65 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
66 })
67 }
68}
diff --git a/shared/extra-utils/videos/channels-command.ts b/shared/extra-utils/videos/channels-command.ts
new file mode 100644
index 000000000..255e1d62d
--- /dev/null
+++ b/shared/extra-utils/videos/channels-command.ts
@@ -0,0 +1,156 @@
1import { pick } from '@shared/core-utils'
2import { HttpStatusCode, ResultList, VideoChannel, VideoChannelCreateResult } from '@shared/models'
3import { VideoChannelCreate } from '../../models/videos/channel/video-channel-create.model'
4import { VideoChannelUpdate } from '../../models/videos/channel/video-channel-update.model'
5import { unwrapBody } from '../requests'
6import { AbstractCommand, OverrideCommandOptions } from '../shared'
7
8export class ChannelsCommand extends AbstractCommand {
9
10 list (options: OverrideCommandOptions & {
11 start?: number
12 count?: number
13 sort?: string
14 withStats?: boolean
15 } = {}) {
16 const path = '/api/v1/video-channels'
17
18 return this.getRequestBody<ResultList<VideoChannel>>({
19 ...options,
20
21 path,
22 query: pick(options, [ 'start', 'count', 'sort', 'withStats' ]),
23 implicitToken: false,
24 defaultExpectedStatus: HttpStatusCode.OK_200
25 })
26 }
27
28 listByAccount (options: OverrideCommandOptions & {
29 accountName: string
30 start?: number
31 count?: number
32 sort?: string
33 withStats?: boolean
34 search?: string
35 }) {
36 const { accountName, sort = 'createdAt' } = options
37 const path = '/api/v1/accounts/' + accountName + '/video-channels'
38
39 return this.getRequestBody<ResultList<VideoChannel>>({
40 ...options,
41
42 path,
43 query: { sort, ...pick(options, [ 'start', 'count', 'withStats', 'search' ]) },
44 implicitToken: false,
45 defaultExpectedStatus: HttpStatusCode.OK_200
46 })
47 }
48
49 async create (options: OverrideCommandOptions & {
50 attributes: VideoChannelCreate
51 }) {
52 const path = '/api/v1/video-channels/'
53
54 // Default attributes
55 const defaultAttributes = {
56 displayName: 'my super video channel',
57 description: 'my super channel description',
58 support: 'my super channel support'
59 }
60 const attributes = { ...defaultAttributes, ...options.attributes }
61
62 const body = await unwrapBody<{ videoChannel: VideoChannelCreateResult }>(this.postBodyRequest({
63 ...options,
64
65 path,
66 fields: attributes,
67 implicitToken: true,
68 defaultExpectedStatus: HttpStatusCode.OK_200
69 }))
70
71 return body.videoChannel
72 }
73
74 update (options: OverrideCommandOptions & {
75 channelName: string
76 attributes: VideoChannelUpdate
77 }) {
78 const { channelName, attributes } = options
79 const path = '/api/v1/video-channels/' + channelName
80
81 return this.putBodyRequest({
82 ...options,
83
84 path,
85 fields: attributes,
86 implicitToken: true,
87 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
88 })
89 }
90
91 delete (options: OverrideCommandOptions & {
92 channelName: string
93 }) {
94 const path = '/api/v1/video-channels/' + options.channelName
95
96 return this.deleteRequest({
97 ...options,
98
99 path,
100 implicitToken: true,
101 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
102 })
103 }
104
105 get (options: OverrideCommandOptions & {
106 channelName: string
107 }) {
108 const path = '/api/v1/video-channels/' + options.channelName
109
110 return this.getRequestBody<VideoChannel>({
111 ...options,
112
113 path,
114 implicitToken: false,
115 defaultExpectedStatus: HttpStatusCode.OK_200
116 })
117 }
118
119 updateImage (options: OverrideCommandOptions & {
120 fixture: string
121 channelName: string | number
122 type: 'avatar' | 'banner'
123 }) {
124 const { channelName, fixture, type } = options
125
126 const path = `/api/v1/video-channels/${channelName}/${type}/pick`
127
128 return this.updateImageRequest({
129 ...options,
130
131 path,
132 fixture,
133 fieldname: type + 'file',
134
135 implicitToken: true,
136 defaultExpectedStatus: HttpStatusCode.OK_200
137 })
138 }
139
140 deleteImage (options: OverrideCommandOptions & {
141 channelName: string | number
142 type: 'avatar' | 'banner'
143 }) {
144 const { channelName, type } = options
145
146 const path = `/api/v1/video-channels/${channelName}/${type}`
147
148 return this.deleteRequest({
149 ...options,
150
151 path,
152 implicitToken: true,
153 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
154 })
155 }
156}
diff --git a/shared/extra-utils/videos/channels.ts b/shared/extra-utils/videos/channels.ts
new file mode 100644
index 000000000..756c47453
--- /dev/null
+++ b/shared/extra-utils/videos/channels.ts
@@ -0,0 +1,18 @@
1import { PeerTubeServer } from '../server/server'
2
3function setDefaultVideoChannel (servers: PeerTubeServer[]) {
4 const tasks: Promise<any>[] = []
5
6 for (const server of servers) {
7 const p = server.users.getMyInfo()
8 .then(user => { server.store.channel = user.videoChannels[0] })
9
10 tasks.push(p)
11 }
12
13 return Promise.all(tasks)
14}
15
16export {
17 setDefaultVideoChannel
18}
diff --git a/shared/extra-utils/videos/comments-command.ts b/shared/extra-utils/videos/comments-command.ts
new file mode 100644
index 000000000..f0d163a07
--- /dev/null
+++ b/shared/extra-utils/videos/comments-command.ts
@@ -0,0 +1,152 @@
1import { pick } from 'lodash'
2import { HttpStatusCode, ResultList, VideoComment, VideoCommentThreads, VideoCommentThreadTree } from '@shared/models'
3import { unwrapBody } from '../requests'
4import { AbstractCommand, OverrideCommandOptions } from '../shared'
5
6export class CommentsCommand extends AbstractCommand {
7
8 private lastVideoId: number | string
9 private lastThreadId: number
10 private lastReplyId: number
11
12 listForAdmin (options: OverrideCommandOptions & {
13 start?: number
14 count?: number
15 sort?: string
16 isLocal?: boolean
17 search?: string
18 searchAccount?: string
19 searchVideo?: string
20 } = {}) {
21 const { sort = '-createdAt' } = options
22 const path = '/api/v1/videos/comments'
23
24 const query = { sort, ...pick(options, [ 'start', 'count', 'isLocal', 'search', 'searchAccount', 'searchVideo' ]) }
25
26 return this.getRequestBody<ResultList<VideoComment>>({
27 ...options,
28
29 path,
30 query,
31 implicitToken: true,
32 defaultExpectedStatus: HttpStatusCode.OK_200
33 })
34 }
35
36 listThreads (options: OverrideCommandOptions & {
37 videoId: number | string
38 start?: number
39 count?: number
40 sort?: string
41 }) {
42 const { start, count, sort, videoId } = options
43 const path = '/api/v1/videos/' + videoId + '/comment-threads'
44
45 return this.getRequestBody<VideoCommentThreads>({
46 ...options,
47
48 path,
49 query: { start, count, sort },
50 implicitToken: false,
51 defaultExpectedStatus: HttpStatusCode.OK_200
52 })
53 }
54
55 getThread (options: OverrideCommandOptions & {
56 videoId: number | string
57 threadId: number
58 }) {
59 const { videoId, threadId } = options
60 const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId
61
62 return this.getRequestBody<VideoCommentThreadTree>({
63 ...options,
64
65 path,
66 implicitToken: false,
67 defaultExpectedStatus: HttpStatusCode.OK_200
68 })
69 }
70
71 async createThread (options: OverrideCommandOptions & {
72 videoId: number | string
73 text: string
74 }) {
75 const { videoId, text } = options
76 const path = '/api/v1/videos/' + videoId + '/comment-threads'
77
78 const body = await unwrapBody<{ comment: VideoComment }>(this.postBodyRequest({
79 ...options,
80
81 path,
82 fields: { text },
83 implicitToken: true,
84 defaultExpectedStatus: HttpStatusCode.OK_200
85 }))
86
87 this.lastThreadId = body.comment?.id
88 this.lastVideoId = videoId
89
90 return body.comment
91 }
92
93 async addReply (options: OverrideCommandOptions & {
94 videoId: number | string
95 toCommentId: number
96 text: string
97 }) {
98 const { videoId, toCommentId, text } = options
99 const path = '/api/v1/videos/' + videoId + '/comments/' + toCommentId
100
101 const body = await unwrapBody<{ comment: VideoComment }>(this.postBodyRequest({
102 ...options,
103
104 path,
105 fields: { text },
106 implicitToken: true,
107 defaultExpectedStatus: HttpStatusCode.OK_200
108 }))
109
110 this.lastReplyId = body.comment?.id
111
112 return body.comment
113 }
114
115 async addReplyToLastReply (options: OverrideCommandOptions & {
116 text: string
117 }) {
118 return this.addReply({ ...options, videoId: this.lastVideoId, toCommentId: this.lastReplyId })
119 }
120
121 async addReplyToLastThread (options: OverrideCommandOptions & {
122 text: string
123 }) {
124 return this.addReply({ ...options, videoId: this.lastVideoId, toCommentId: this.lastThreadId })
125 }
126
127 async findCommentId (options: OverrideCommandOptions & {
128 videoId: number | string
129 text: string
130 }) {
131 const { videoId, text } = options
132 const { data } = await this.listThreads({ videoId, count: 25, sort: '-createdAt' })
133
134 return data.find(c => c.text === text).id
135 }
136
137 delete (options: OverrideCommandOptions & {
138 videoId: number | string
139 commentId: number
140 }) {
141 const { videoId, commentId } = options
142 const path = '/api/v1/videos/' + videoId + '/comments/' + commentId
143
144 return this.deleteRequest({
145 ...options,
146
147 path,
148 implicitToken: true,
149 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
150 })
151 }
152}
diff --git a/shared/extra-utils/videos/history-command.ts b/shared/extra-utils/videos/history-command.ts
new file mode 100644
index 000000000..13b7150c1
--- /dev/null
+++ b/shared/extra-utils/videos/history-command.ts
@@ -0,0 +1,58 @@
1import { HttpStatusCode, ResultList, Video } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class HistoryCommand extends AbstractCommand {
5
6 wathVideo (options: OverrideCommandOptions & {
7 videoId: number | string
8 currentTime: number
9 }) {
10 const { videoId, currentTime } = options
11
12 const path = '/api/v1/videos/' + videoId + '/watching'
13 const fields = { currentTime }
14
15 return this.putBodyRequest({
16 ...options,
17
18 path,
19 fields,
20 implicitToken: true,
21 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
22 })
23 }
24
25 list (options: OverrideCommandOptions & {
26 search?: string
27 } = {}) {
28 const { search } = options
29 const path = '/api/v1/users/me/history/videos'
30
31 return this.getRequestBody<ResultList<Video>>({
32 ...options,
33
34 path,
35 query: {
36 search
37 },
38 implicitToken: true,
39 defaultExpectedStatus: HttpStatusCode.OK_200
40 })
41 }
42
43 remove (options: OverrideCommandOptions & {
44 beforeDate?: string
45 } = {}) {
46 const { beforeDate } = options
47 const path = '/api/v1/users/me/history/videos/remove'
48
49 return this.postBodyRequest({
50 ...options,
51
52 path,
53 fields: { beforeDate },
54 implicitToken: true,
55 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
56 })
57 }
58}
diff --git a/shared/extra-utils/videos/imports-command.ts b/shared/extra-utils/videos/imports-command.ts
new file mode 100644
index 000000000..e4944694d
--- /dev/null
+++ b/shared/extra-utils/videos/imports-command.ts
@@ -0,0 +1,47 @@
1
2import { HttpStatusCode, ResultList } from '@shared/models'
3import { VideoImport, VideoImportCreate } from '../../models/videos'
4import { unwrapBody } from '../requests'
5import { AbstractCommand, OverrideCommandOptions } from '../shared'
6
7export class ImportsCommand extends AbstractCommand {
8
9 importVideo (options: OverrideCommandOptions & {
10 attributes: VideoImportCreate & { torrentfile?: string }
11 }) {
12 const { attributes } = options
13 const path = '/api/v1/videos/imports'
14
15 let attaches: any = {}
16 if (attributes.torrentfile) attaches = { torrentfile: attributes.torrentfile }
17
18 return unwrapBody<VideoImport>(this.postUploadRequest({
19 ...options,
20
21 path,
22 attaches,
23 fields: options.attributes,
24 implicitToken: true,
25 defaultExpectedStatus: HttpStatusCode.OK_200
26 }))
27 }
28
29 getMyVideoImports (options: OverrideCommandOptions & {
30 sort?: string
31 } = {}) {
32 const { sort } = options
33 const path = '/api/v1/users/me/videos/imports'
34
35 const query = {}
36 if (sort) query['sort'] = sort
37
38 return this.getRequestBody<ResultList<VideoImport>>({
39 ...options,
40
41 path,
42 query: { sort },
43 implicitToken: true,
44 defaultExpectedStatus: HttpStatusCode.OK_200
45 })
46 }
47}
diff --git a/shared/extra-utils/videos/index.ts b/shared/extra-utils/videos/index.ts
new file mode 100644
index 000000000..26e663f46
--- /dev/null
+++ b/shared/extra-utils/videos/index.ts
@@ -0,0 +1,19 @@
1export * from './blacklist-command'
2export * from './captions-command'
3export * from './captions'
4export * from './change-ownership-command'
5export * from './channels'
6export * from './channels-command'
7export * from './comments-command'
8export * from './history-command'
9export * from './imports-command'
10export * from './live-command'
11export * from './live'
12export * from './playlists-command'
13export * from './playlists'
14export * from './services-command'
15export * from './streaming-playlists-command'
16export * from './streaming-playlists'
17export * from './comments-command'
18export * from './videos-command'
19export * from './videos'
diff --git a/shared/extra-utils/videos/live-command.ts b/shared/extra-utils/videos/live-command.ts
new file mode 100644
index 000000000..bf9486a05
--- /dev/null
+++ b/shared/extra-utils/videos/live-command.ts
@@ -0,0 +1,154 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { readdir } from 'fs-extra'
4import { omit } from 'lodash'
5import { join } from 'path'
6import { HttpStatusCode, LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoCreateResult, VideoDetails, VideoState } from '@shared/models'
7import { wait } from '../miscs'
8import { unwrapBody } from '../requests'
9import { AbstractCommand, OverrideCommandOptions } from '../shared'
10import { sendRTMPStream, testFfmpegStreamError } from './live'
11
12export class LiveCommand extends AbstractCommand {
13
14 get (options: OverrideCommandOptions & {
15 videoId: number | string
16 }) {
17 const path = '/api/v1/videos/live'
18
19 return this.getRequestBody<LiveVideo>({
20 ...options,
21
22 path: path + '/' + options.videoId,
23 implicitToken: true,
24 defaultExpectedStatus: HttpStatusCode.OK_200
25 })
26 }
27
28 update (options: OverrideCommandOptions & {
29 videoId: number | string
30 fields: LiveVideoUpdate
31 }) {
32 const { videoId, fields } = options
33 const path = '/api/v1/videos/live'
34
35 return this.putBodyRequest({
36 ...options,
37
38 path: path + '/' + videoId,
39 fields,
40 implicitToken: true,
41 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
42 })
43 }
44
45 async create (options: OverrideCommandOptions & {
46 fields: LiveVideoCreate
47 }) {
48 const { fields } = options
49 const path = '/api/v1/videos/live'
50
51 const attaches: any = {}
52 if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile
53 if (fields.previewfile) attaches.previewfile = fields.previewfile
54
55 const body = await unwrapBody<{ video: VideoCreateResult }>(this.postUploadRequest({
56 ...options,
57
58 path,
59 attaches,
60 fields: omit(fields, 'thumbnailfile', 'previewfile'),
61 implicitToken: true,
62 defaultExpectedStatus: HttpStatusCode.OK_200
63 }))
64
65 return body.video
66 }
67
68 async sendRTMPStreamInVideo (options: OverrideCommandOptions & {
69 videoId: number | string
70 fixtureName?: string
71 }) {
72 const { videoId, fixtureName } = options
73 const videoLive = await this.get({ videoId })
74
75 return sendRTMPStream(videoLive.rtmpUrl, videoLive.streamKey, fixtureName)
76 }
77
78 async runAndTestStreamError (options: OverrideCommandOptions & {
79 videoId: number | string
80 shouldHaveError: boolean
81 }) {
82 const command = await this.sendRTMPStreamInVideo(options)
83
84 return testFfmpegStreamError(command, options.shouldHaveError)
85 }
86
87 waitUntilPublished (options: OverrideCommandOptions & {
88 videoId: number | string
89 }) {
90 const { videoId } = options
91 return this.waitUntilState({ videoId, state: VideoState.PUBLISHED })
92 }
93
94 waitUntilWaiting (options: OverrideCommandOptions & {
95 videoId: number | string
96 }) {
97 const { videoId } = options
98 return this.waitUntilState({ videoId, state: VideoState.WAITING_FOR_LIVE })
99 }
100
101 waitUntilEnded (options: OverrideCommandOptions & {
102 videoId: number | string
103 }) {
104 const { videoId } = options
105 return this.waitUntilState({ videoId, state: VideoState.LIVE_ENDED })
106 }
107
108 waitUntilSegmentGeneration (options: OverrideCommandOptions & {
109 videoUUID: string
110 resolution: number
111 segment: number
112 }) {
113 const { resolution, segment, videoUUID } = options
114 const segmentName = `${resolution}-00000${segment}.ts`
115
116 return this.server.servers.waitUntilLog(`${videoUUID}/${segmentName}`, 2, false)
117 }
118
119 async waitUntilSaved (options: OverrideCommandOptions & {
120 videoId: number | string
121 }) {
122 let video: VideoDetails
123
124 do {
125 video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId })
126
127 await wait(500)
128 } while (video.isLive === true && video.state.id !== VideoState.PUBLISHED)
129 }
130
131 async countPlaylists (options: OverrideCommandOptions & {
132 videoUUID: string
133 }) {
134 const basePath = this.server.servers.buildDirectory('streaming-playlists')
135 const hlsPath = join(basePath, 'hls', options.videoUUID)
136
137 const files = await readdir(hlsPath)
138
139 return files.filter(f => f.endsWith('.m3u8')).length
140 }
141
142 private async waitUntilState (options: OverrideCommandOptions & {
143 videoId: number | string
144 state: VideoState
145 }) {
146 let video: VideoDetails
147
148 do {
149 video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId })
150
151 await wait(500)
152 } while (video.state.id !== options.state)
153 }
154}
diff --git a/shared/extra-utils/videos/live.ts b/shared/extra-utils/videos/live.ts
index c0384769b..94f5f5b59 100644
--- a/shared/extra-utils/videos/live.ts
+++ b/shared/extra-utils/videos/live.ts
@@ -3,69 +3,9 @@
3import { expect } from 'chai' 3import { expect } from 'chai'
4import * as ffmpeg from 'fluent-ffmpeg' 4import * as ffmpeg from 'fluent-ffmpeg'
5import { pathExists, readdir } from 'fs-extra' 5import { pathExists, readdir } from 'fs-extra'
6import { omit } from 'lodash'
7import { join } from 'path' 6import { join } from 'path'
8import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoDetails, VideoState } from '@shared/models' 7import { buildAbsoluteFixturePath, wait } from '../miscs'
9import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 8import { PeerTubeServer } from '../server/server'
10import { buildAbsoluteFixturePath, buildServerDirectory, wait } from '../miscs/miscs'
11import { makeGetRequest, makePutBodyRequest, makeUploadRequest } from '../requests/requests'
12import { ServerInfo, waitUntilLog } from '../server/servers'
13import { getVideoWithToken } from './videos'
14
15function getLive (url: string, token: string, videoId: number | string, statusCodeExpected = HttpStatusCode.OK_200) {
16 const path = '/api/v1/videos/live'
17
18 return makeGetRequest({
19 url,
20 token,
21 path: path + '/' + videoId,
22 statusCodeExpected
23 })
24}
25
26function updateLive (
27 url: string,
28 token: string,
29 videoId: number | string,
30 fields: LiveVideoUpdate,
31 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
32) {
33 const path = '/api/v1/videos/live'
34
35 return makePutBodyRequest({
36 url,
37 token,
38 path: path + '/' + videoId,
39 fields,
40 statusCodeExpected
41 })
42}
43
44function createLive (url: string, token: string, fields: LiveVideoCreate, statusCodeExpected = HttpStatusCode.OK_200) {
45 const path = '/api/v1/videos/live'
46
47 const attaches: any = {}
48 if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile
49 if (fields.previewfile) attaches.previewfile = fields.previewfile
50
51 const updatedFields = omit(fields, 'thumbnailfile', 'previewfile')
52
53 return makeUploadRequest({
54 url,
55 path,
56 token,
57 attaches,
58 fields: updatedFields,
59 statusCodeExpected
60 })
61}
62
63async function sendRTMPStreamInVideo (url: string, token: string, videoId: number | string, fixtureName?: string) {
64 const res = await getLive(url, token, videoId)
65 const videoLive = res.body as LiveVideo
66
67 return sendRTMPStream(videoLive.rtmpUrl, videoLive.streamKey, fixtureName)
68}
69 9
70function sendRTMPStream (rtmpBaseUrl: string, streamKey: string, fixtureName = 'video_short.mp4') { 10function sendRTMPStream (rtmpBaseUrl: string, streamKey: string, fixtureName = 'video_short.mp4') {
71 const fixture = buildAbsoluteFixturePath(fixtureName) 11 const fixture = buildAbsoluteFixturePath(fixtureName)
@@ -109,12 +49,6 @@ function waitFfmpegUntilError (command: ffmpeg.FfmpegCommand, successAfterMS = 1
109 }) 49 })
110} 50}
111 51
112async function runAndTestFfmpegStreamError (url: string, token: string, videoId: number | string, shouldHaveError: boolean) {
113 const command = await sendRTMPStreamInVideo(url, token, videoId)
114
115 return testFfmpegStreamError(command, shouldHaveError)
116}
117
118async function testFfmpegStreamError (command: ffmpeg.FfmpegCommand, shouldHaveError: boolean) { 52async function testFfmpegStreamError (command: ffmpeg.FfmpegCommand, shouldHaveError: boolean) {
119 let error: Error 53 let error: Error
120 54
@@ -136,53 +70,14 @@ async function stopFfmpeg (command: ffmpeg.FfmpegCommand) {
136 await wait(500) 70 await wait(500)
137} 71}
138 72
139function waitUntilLivePublished (url: string, token: string, videoId: number | string) { 73async function waitUntilLivePublishedOnAllServers (servers: PeerTubeServer[], videoId: string) {
140 return waitUntilLiveState(url, token, videoId, VideoState.PUBLISHED)
141}
142
143function waitUntilLiveWaiting (url: string, token: string, videoId: number | string) {
144 return waitUntilLiveState(url, token, videoId, VideoState.WAITING_FOR_LIVE)
145}
146
147function waitUntilLiveEnded (url: string, token: string, videoId: number | string) {
148 return waitUntilLiveState(url, token, videoId, VideoState.LIVE_ENDED)
149}
150
151function waitUntilLiveSegmentGeneration (server: ServerInfo, videoUUID: string, resolutionNum: number, segmentNum: number) {
152 const segmentName = `${resolutionNum}-00000${segmentNum}.ts`
153 return waitUntilLog(server, `${videoUUID}/${segmentName}`, 2, false)
154}
155
156async function waitUntilLiveState (url: string, token: string, videoId: number | string, state: VideoState) {
157 let video: VideoDetails
158
159 do {
160 const res = await getVideoWithToken(url, token, videoId)
161 video = res.body
162
163 await wait(500)
164 } while (video.state.id !== state)
165}
166
167async function waitUntilLiveSaved (url: string, token: string, videoId: number | string) {
168 let video: VideoDetails
169
170 do {
171 const res = await getVideoWithToken(url, token, videoId)
172 video = res.body
173
174 await wait(500)
175 } while (video.isLive === true && video.state.id !== VideoState.PUBLISHED)
176}
177
178async function waitUntilLivePublishedOnAllServers (servers: ServerInfo[], videoId: string) {
179 for (const server of servers) { 74 for (const server of servers) {
180 await waitUntilLivePublished(server.url, server.accessToken, videoId) 75 await server.live.waitUntilPublished({ videoId })
181 } 76 }
182} 77}
183 78
184async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resolutions: number[] = []) { 79async function checkLiveCleanupAfterSave (server: PeerTubeServer, videoUUID: string, resolutions: number[] = []) {
185 const basePath = buildServerDirectory(server, 'streaming-playlists') 80 const basePath = server.servers.buildDirectory('streaming-playlists')
186 const hlsPath = join(basePath, 'hls', videoUUID) 81 const hlsPath = join(basePath, 'hls', videoUUID)
187 82
188 if (resolutions.length === 0) { 83 if (resolutions.length === 0) {
@@ -198,41 +93,25 @@ async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resoluti
198 expect(files).to.have.lengthOf(resolutions.length * 2 + 2) 93 expect(files).to.have.lengthOf(resolutions.length * 2 + 2)
199 94
200 for (const resolution of resolutions) { 95 for (const resolution of resolutions) {
201 expect(files).to.contain(`${videoUUID}-${resolution}-fragmented.mp4`) 96 const fragmentedFile = files.find(f => f.endsWith(`-${resolution}-fragmented.mp4`))
202 expect(files).to.contain(`${resolution}.m3u8`) 97 expect(fragmentedFile).to.exist
203 }
204
205 expect(files).to.contain('master.m3u8')
206 expect(files).to.contain('segments-sha256.json')
207}
208 98
209async function getPlaylistsCount (server: ServerInfo, videoUUID: string) { 99 const playlistFile = files.find(f => f.endsWith(`${resolution}.m3u8`))
210 const basePath = buildServerDirectory(server, 'streaming-playlists') 100 expect(playlistFile).to.exist
211 const hlsPath = join(basePath, 'hls', videoUUID) 101 }
212 102
213 const files = await readdir(hlsPath) 103 const masterPlaylistFile = files.find(f => f.endsWith('-master.m3u8'))
104 expect(masterPlaylistFile).to.exist
214 105
215 return files.filter(f => f.endsWith('.m3u8')).length 106 const shaFile = files.find(f => f.endsWith('-segments-sha256.json'))
107 expect(shaFile).to.exist
216} 108}
217 109
218// ---------------------------------------------------------------------------
219
220export { 110export {
221 getLive, 111 sendRTMPStream,
222 getPlaylistsCount,
223 waitUntilLiveSaved,
224 waitUntilLivePublished,
225 updateLive,
226 createLive,
227 runAndTestFfmpegStreamError,
228 checkLiveCleanup,
229 waitUntilLiveSegmentGeneration,
230 stopFfmpeg,
231 waitUntilLiveWaiting,
232 sendRTMPStreamInVideo,
233 waitUntilLiveEnded,
234 waitFfmpegUntilError, 112 waitFfmpegUntilError,
113 testFfmpegStreamError,
114 stopFfmpeg,
235 waitUntilLivePublishedOnAllServers, 115 waitUntilLivePublishedOnAllServers,
236 sendRTMPStream, 116 checkLiveCleanupAfterSave
237 testFfmpegStreamError
238} 117}
diff --git a/shared/extra-utils/videos/playlists-command.ts b/shared/extra-utils/videos/playlists-command.ts
new file mode 100644
index 000000000..ce23900d3
--- /dev/null
+++ b/shared/extra-utils/videos/playlists-command.ts
@@ -0,0 +1,280 @@
1import { omit } from 'lodash'
2import { pick } from '@shared/core-utils'
3import {
4 BooleanBothQuery,
5 HttpStatusCode,
6 ResultList,
7 VideoExistInPlaylist,
8 VideoPlaylist,
9 VideoPlaylistCreate,
10 VideoPlaylistCreateResult,
11 VideoPlaylistElement,
12 VideoPlaylistElementCreate,
13 VideoPlaylistElementCreateResult,
14 VideoPlaylistElementUpdate,
15 VideoPlaylistReorder,
16 VideoPlaylistType,
17 VideoPlaylistUpdate
18} from '@shared/models'
19import { unwrapBody } from '../requests'
20import { AbstractCommand, OverrideCommandOptions } from '../shared'
21
22export class PlaylistsCommand extends AbstractCommand {
23
24 list (options: OverrideCommandOptions & {
25 start?: number
26 count?: number
27 sort?: string
28 }) {
29 const path = '/api/v1/video-playlists'
30 const query = pick(options, [ 'start', 'count', 'sort' ])
31
32 return this.getRequestBody<ResultList<VideoPlaylist>>({
33 ...options,
34
35 path,
36 query,
37 implicitToken: false,
38 defaultExpectedStatus: HttpStatusCode.OK_200
39 })
40 }
41
42 listByChannel (options: OverrideCommandOptions & {
43 handle: string
44 start?: number
45 count?: number
46 sort?: string
47 }) {
48 const path = '/api/v1/video-channels/' + options.handle + '/video-playlists'
49 const query = pick(options, [ 'start', 'count', 'sort' ])
50
51 return this.getRequestBody<ResultList<VideoPlaylist>>({
52 ...options,
53
54 path,
55 query,
56 implicitToken: false,
57 defaultExpectedStatus: HttpStatusCode.OK_200
58 })
59 }
60
61 listByAccount (options: OverrideCommandOptions & {
62 handle: string
63 start?: number
64 count?: number
65 sort?: string
66 search?: string
67 playlistType?: VideoPlaylistType
68 }) {
69 const path = '/api/v1/accounts/' + options.handle + '/video-playlists'
70 const query = pick(options, [ 'start', 'count', 'sort', 'search', 'playlistType' ])
71
72 return this.getRequestBody<ResultList<VideoPlaylist>>({
73 ...options,
74
75 path,
76 query,
77 implicitToken: false,
78 defaultExpectedStatus: HttpStatusCode.OK_200
79 })
80 }
81
82 get (options: OverrideCommandOptions & {
83 playlistId: number | string
84 }) {
85 const { playlistId } = options
86 const path = '/api/v1/video-playlists/' + playlistId
87
88 return this.getRequestBody<VideoPlaylist>({
89 ...options,
90
91 path,
92 implicitToken: false,
93 defaultExpectedStatus: HttpStatusCode.OK_200
94 })
95 }
96
97 listVideos (options: OverrideCommandOptions & {
98 playlistId: number | string
99 start?: number
100 count?: number
101 query?: { nsfw?: BooleanBothQuery }
102 }) {
103 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos'
104 const query = options.query ?? {}
105
106 return this.getRequestBody<ResultList<VideoPlaylistElement>>({
107 ...options,
108
109 path,
110 query: {
111 ...query,
112 start: options.start,
113 count: options.count
114 },
115 implicitToken: true,
116 defaultExpectedStatus: HttpStatusCode.OK_200
117 })
118 }
119
120 delete (options: OverrideCommandOptions & {
121 playlistId: number | string
122 }) {
123 const path = '/api/v1/video-playlists/' + options.playlistId
124
125 return this.deleteRequest({
126 ...options,
127
128 path,
129 implicitToken: true,
130 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
131 })
132 }
133
134 async create (options: OverrideCommandOptions & {
135 attributes: VideoPlaylistCreate
136 }) {
137 const path = '/api/v1/video-playlists'
138
139 const fields = omit(options.attributes, 'thumbnailfile')
140
141 const attaches = options.attributes.thumbnailfile
142 ? { thumbnailfile: options.attributes.thumbnailfile }
143 : {}
144
145 const body = await unwrapBody<{ videoPlaylist: VideoPlaylistCreateResult }>(this.postUploadRequest({
146 ...options,
147
148 path,
149 fields,
150 attaches,
151 implicitToken: true,
152 defaultExpectedStatus: HttpStatusCode.OK_200
153 }))
154
155 return body.videoPlaylist
156 }
157
158 update (options: OverrideCommandOptions & {
159 attributes: VideoPlaylistUpdate
160 playlistId: number | string
161 }) {
162 const path = '/api/v1/video-playlists/' + options.playlistId
163
164 const fields = omit(options.attributes, 'thumbnailfile')
165
166 const attaches = options.attributes.thumbnailfile
167 ? { thumbnailfile: options.attributes.thumbnailfile }
168 : {}
169
170 return this.putUploadRequest({
171 ...options,
172
173 path,
174 fields,
175 attaches,
176 implicitToken: true,
177 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
178 })
179 }
180
181 async addElement (options: OverrideCommandOptions & {
182 playlistId: number | string
183 attributes: VideoPlaylistElementCreate | { videoId: string }
184 }) {
185 const attributes = {
186 ...options.attributes,
187
188 videoId: await this.server.videos.getId({ ...options, uuid: options.attributes.videoId })
189 }
190
191 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos'
192
193 const body = await unwrapBody<{ videoPlaylistElement: VideoPlaylistElementCreateResult }>(this.postBodyRequest({
194 ...options,
195
196 path,
197 fields: attributes,
198 implicitToken: true,
199 defaultExpectedStatus: HttpStatusCode.OK_200
200 }))
201
202 return body.videoPlaylistElement
203 }
204
205 updateElement (options: OverrideCommandOptions & {
206 playlistId: number | string
207 elementId: number | string
208 attributes: VideoPlaylistElementUpdate
209 }) {
210 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.elementId
211
212 return this.putBodyRequest({
213 ...options,
214
215 path,
216 fields: options.attributes,
217 implicitToken: true,
218 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
219 })
220 }
221
222 removeElement (options: OverrideCommandOptions & {
223 playlistId: number | string
224 elementId: number
225 }) {
226 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.elementId
227
228 return this.deleteRequest({
229 ...options,
230
231 path,
232 implicitToken: true,
233 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
234 })
235 }
236
237 reorderElements (options: OverrideCommandOptions & {
238 playlistId: number | string
239 attributes: VideoPlaylistReorder
240 }) {
241 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/reorder'
242
243 return this.postBodyRequest({
244 ...options,
245
246 path,
247 fields: options.attributes,
248 implicitToken: true,
249 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
250 })
251 }
252
253 getPrivacies (options: OverrideCommandOptions = {}) {
254 const path = '/api/v1/video-playlists/privacies'
255
256 return this.getRequestBody<{ [ id: number ]: string }>({
257 ...options,
258
259 path,
260 implicitToken: false,
261 defaultExpectedStatus: HttpStatusCode.OK_200
262 })
263 }
264
265 videosExist (options: OverrideCommandOptions & {
266 videoIds: number[]
267 }) {
268 const { videoIds } = options
269 const path = '/api/v1/users/me/video-playlists/videos-exist'
270
271 return this.getRequestBody<VideoExistInPlaylist>({
272 ...options,
273
274 path,
275 query: { videoIds },
276 implicitToken: true,
277 defaultExpectedStatus: HttpStatusCode.OK_200
278 })
279 }
280}
diff --git a/shared/extra-utils/videos/playlists.ts b/shared/extra-utils/videos/playlists.ts
new file mode 100644
index 000000000..3dde52bb9
--- /dev/null
+++ b/shared/extra-utils/videos/playlists.ts
@@ -0,0 +1,25 @@
1import { expect } from 'chai'
2import { readdir } from 'fs-extra'
3import { join } from 'path'
4import { root } from '../miscs'
5
6async function checkPlaylistFilesWereRemoved (
7 playlistUUID: string,
8 internalServerNumber: number,
9 directories = [ 'thumbnails' ]
10) {
11 const testDirectory = 'test' + internalServerNumber
12
13 for (const directory of directories) {
14 const directoryPath = join(root(), testDirectory, directory)
15
16 const files = await readdir(directoryPath)
17 for (const file of files) {
18 expect(file).to.not.contain(playlistUUID)
19 }
20 }
21}
22
23export {
24 checkPlaylistFilesWereRemoved
25}
diff --git a/shared/extra-utils/videos/services-command.ts b/shared/extra-utils/videos/services-command.ts
new file mode 100644
index 000000000..06760df42
--- /dev/null
+++ b/shared/extra-utils/videos/services-command.ts
@@ -0,0 +1,29 @@
1import { HttpStatusCode } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class ServicesCommand extends AbstractCommand {
5
6 getOEmbed (options: OverrideCommandOptions & {
7 oembedUrl: string
8 format?: string
9 maxHeight?: number
10 maxWidth?: number
11 }) {
12 const path = '/services/oembed'
13 const query = {
14 url: options.oembedUrl,
15 format: options.format,
16 maxheight: options.maxHeight,
17 maxwidth: options.maxWidth
18 }
19
20 return this.getRequest({
21 ...options,
22
23 path,
24 query,
25 implicitToken: false,
26 defaultExpectedStatus: HttpStatusCode.OK_200
27 })
28 }
29}
diff --git a/shared/extra-utils/videos/services.ts b/shared/extra-utils/videos/services.ts
deleted file mode 100644
index e13a788bd..000000000
--- a/shared/extra-utils/videos/services.ts
+++ /dev/null
@@ -1,24 +0,0 @@
1import * as request from 'supertest'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function getOEmbed (url: string, oembedUrl: string, format?: string, maxHeight?: number, maxWidth?: number) {
5 const path = '/services/oembed'
6 const query = {
7 url: oembedUrl,
8 format,
9 maxheight: maxHeight,
10 maxwidth: maxWidth
11 }
12
13 return request(url)
14 .get(path)
15 .query(query)
16 .set('Accept', 'application/json')
17 .expect(HttpStatusCode.OK_200)
18}
19
20// ---------------------------------------------------------------------------
21
22export {
23 getOEmbed
24}
diff --git a/shared/extra-utils/videos/streaming-playlists-command.ts b/shared/extra-utils/videos/streaming-playlists-command.ts
new file mode 100644
index 000000000..9662685da
--- /dev/null
+++ b/shared/extra-utils/videos/streaming-playlists-command.ts
@@ -0,0 +1,44 @@
1import { HttpStatusCode } from '@shared/models'
2import { unwrapBody, unwrapText } from '../requests'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class StreamingPlaylistsCommand extends AbstractCommand {
6
7 get (options: OverrideCommandOptions & {
8 url: string
9 }) {
10 return unwrapText(this.getRawRequest({
11 ...options,
12
13 url: options.url,
14 implicitToken: false,
15 defaultExpectedStatus: HttpStatusCode.OK_200
16 }))
17 }
18
19 getSegment (options: OverrideCommandOptions & {
20 url: string
21 range?: string
22 }) {
23 return unwrapBody<Buffer>(this.getRawRequest({
24 ...options,
25
26 url: options.url,
27 range: options.range,
28 implicitToken: false,
29 defaultExpectedStatus: HttpStatusCode.OK_200
30 }))
31 }
32
33 getSegmentSha256 (options: OverrideCommandOptions & {
34 url: string
35 }) {
36 return unwrapBody<{ [ id: string ]: string }>(this.getRawRequest({
37 ...options,
38
39 url: options.url,
40 implicitToken: false,
41 defaultExpectedStatus: HttpStatusCode.OK_200
42 }))
43 }
44}
diff --git a/shared/extra-utils/videos/streaming-playlists.ts b/shared/extra-utils/videos/streaming-playlists.ts
new file mode 100644
index 000000000..a224b8f5f
--- /dev/null
+++ b/shared/extra-utils/videos/streaming-playlists.ts
@@ -0,0 +1,78 @@
1import { expect } from 'chai'
2import { basename } from 'path'
3import { sha256 } from '@server/helpers/core-utils'
4import { removeFragmentedMP4Ext } from '@shared/core-utils'
5import { HttpStatusCode, VideoStreamingPlaylist } from '@shared/models'
6import { PeerTubeServer } from '../server'
7
8async function checkSegmentHash (options: {
9 server: PeerTubeServer
10 baseUrlPlaylist: string
11 baseUrlSegment: string
12 videoUUID: string
13 resolution: number
14 hlsPlaylist: VideoStreamingPlaylist
15}) {
16 const { server, baseUrlPlaylist, baseUrlSegment, videoUUID, resolution, hlsPlaylist } = options
17 const command = server.streamingPlaylists
18
19 const file = hlsPlaylist.files.find(f => f.resolution.id === resolution)
20 const videoName = basename(file.fileUrl)
21
22 const playlist = await command.get({ url: `${baseUrlPlaylist}/${videoUUID}/${removeFragmentedMP4Ext(videoName)}.m3u8` })
23
24 const matches = /#EXT-X-BYTERANGE:(\d+)@(\d+)/.exec(playlist)
25
26 const length = parseInt(matches[1], 10)
27 const offset = parseInt(matches[2], 10)
28 const range = `${offset}-${offset + length - 1}`
29
30 const segmentBody = await command.getSegment({
31 url: `${baseUrlSegment}/${videoUUID}/${videoName}`,
32 expectedStatus: HttpStatusCode.PARTIAL_CONTENT_206,
33 range: `bytes=${range}`
34 })
35
36 const shaBody = await command.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url })
37 expect(sha256(segmentBody)).to.equal(shaBody[videoName][range])
38}
39
40async function checkLiveSegmentHash (options: {
41 server: PeerTubeServer
42 baseUrlSegment: string
43 videoUUID: string
44 segmentName: string
45 hlsPlaylist: VideoStreamingPlaylist
46}) {
47 const { server, baseUrlSegment, videoUUID, segmentName, hlsPlaylist } = options
48 const command = server.streamingPlaylists
49
50 const segmentBody = await command.getSegment({ url: `${baseUrlSegment}/${videoUUID}/${segmentName}` })
51 const shaBody = await command.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url })
52
53 expect(sha256(segmentBody)).to.equal(shaBody[segmentName])
54}
55
56async function checkResolutionsInMasterPlaylist (options: {
57 server: PeerTubeServer
58 playlistUrl: string
59 resolutions: number[]
60}) {
61 const { server, playlistUrl, resolutions } = options
62
63 const masterPlaylist = await server.streamingPlaylists.get({ url: playlistUrl })
64
65 for (const resolution of resolutions) {
66 const reg = new RegExp(
67 '#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + ',(FRAME-RATE=\\d+,)?CODECS="avc1.64001f,mp4a.40.2"'
68 )
69
70 expect(masterPlaylist).to.match(reg)
71 }
72}
73
74export {
75 checkSegmentHash,
76 checkLiveSegmentHash,
77 checkResolutionsInMasterPlaylist
78}
diff --git a/shared/extra-utils/videos/video-blacklist.ts b/shared/extra-utils/videos/video-blacklist.ts
deleted file mode 100644
index aa1548537..000000000
--- a/shared/extra-utils/videos/video-blacklist.ts
+++ /dev/null
@@ -1,79 +0,0 @@
1import * as request from 'supertest'
2import { VideoBlacklistType } from '../../models/videos'
3import { makeGetRequest } from '..'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
5
6function addVideoToBlacklist (
7 url: string,
8 token: string,
9 videoId: number | string,
10 reason?: string,
11 unfederate?: boolean,
12 specialStatus = HttpStatusCode.NO_CONTENT_204
13) {
14 const path = '/api/v1/videos/' + videoId + '/blacklist'
15
16 return request(url)
17 .post(path)
18 .send({ reason, unfederate })
19 .set('Accept', 'application/json')
20 .set('Authorization', 'Bearer ' + token)
21 .expect(specialStatus)
22}
23
24function updateVideoBlacklist (
25 url: string,
26 token: string,
27 videoId: number,
28 reason?: string,
29 specialStatus = HttpStatusCode.NO_CONTENT_204
30) {
31 const path = '/api/v1/videos/' + videoId + '/blacklist'
32
33 return request(url)
34 .put(path)
35 .send({ reason })
36 .set('Accept', 'application/json')
37 .set('Authorization', 'Bearer ' + token)
38 .expect(specialStatus)
39}
40
41function removeVideoFromBlacklist (url: string, token: string, videoId: number | string, specialStatus = HttpStatusCode.NO_CONTENT_204) {
42 const path = '/api/v1/videos/' + videoId + '/blacklist'
43
44 return request(url)
45 .delete(path)
46 .set('Accept', 'application/json')
47 .set('Authorization', 'Bearer ' + token)
48 .expect(specialStatus)
49}
50
51function getBlacklistedVideosList (parameters: {
52 url: string
53 token: string
54 sort?: string
55 type?: VideoBlacklistType
56 specialStatus?: HttpStatusCode
57}) {
58 const { url, token, sort, type, specialStatus = HttpStatusCode.OK_200 } = parameters
59 const path = '/api/v1/videos/blacklist/'
60
61 const query = { sort, type }
62
63 return makeGetRequest({
64 url,
65 path,
66 query,
67 token,
68 statusCodeExpected: specialStatus
69 })
70}
71
72// ---------------------------------------------------------------------------
73
74export {
75 addVideoToBlacklist,
76 removeVideoFromBlacklist,
77 getBlacklistedVideosList,
78 updateVideoBlacklist
79}
diff --git a/shared/extra-utils/videos/video-captions.ts b/shared/extra-utils/videos/video-captions.ts
deleted file mode 100644
index 62eec7b90..000000000
--- a/shared/extra-utils/videos/video-captions.ts
+++ /dev/null
@@ -1,72 +0,0 @@
1import { makeDeleteRequest, makeGetRequest, makeUploadRequest } from '../requests/requests'
2import * as request from 'supertest'
3import * as chai from 'chai'
4import { buildAbsoluteFixturePath } from '../miscs/miscs'
5import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
6
7const expect = chai.expect
8
9function createVideoCaption (args: {
10 url: string
11 accessToken: string
12 videoId: string | number
13 language: string
14 fixture: string
15 mimeType?: string
16 statusCodeExpected?: number
17}) {
18 const path = '/api/v1/videos/' + args.videoId + '/captions/' + args.language
19
20 const captionfile = buildAbsoluteFixturePath(args.fixture)
21 const captionfileAttach = args.mimeType ? [ captionfile, { contentType: args.mimeType } ] : captionfile
22
23 return makeUploadRequest({
24 method: 'PUT',
25 url: args.url,
26 path,
27 token: args.accessToken,
28 fields: {},
29 attaches: {
30 captionfile: captionfileAttach
31 },
32 statusCodeExpected: args.statusCodeExpected || HttpStatusCode.NO_CONTENT_204
33 })
34}
35
36function listVideoCaptions (url: string, videoId: string | number) {
37 const path = '/api/v1/videos/' + videoId + '/captions'
38
39 return makeGetRequest({
40 url,
41 path,
42 statusCodeExpected: HttpStatusCode.OK_200
43 })
44}
45
46function deleteVideoCaption (url: string, token: string, videoId: string | number, language: string) {
47 const path = '/api/v1/videos/' + videoId + '/captions/' + language
48
49 return makeDeleteRequest({
50 url,
51 token,
52 path,
53 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
54 })
55}
56
57async function testCaptionFile (url: string, captionPath: string, containsString: string) {
58 const res = await request(url)
59 .get(captionPath)
60 .expect(HttpStatusCode.OK_200)
61
62 expect(res.text).to.contain(containsString)
63}
64
65// ---------------------------------------------------------------------------
66
67export {
68 createVideoCaption,
69 listVideoCaptions,
70 testCaptionFile,
71 deleteVideoCaption
72}
diff --git a/shared/extra-utils/videos/video-change-ownership.ts b/shared/extra-utils/videos/video-change-ownership.ts
deleted file mode 100644
index ef82a7636..000000000
--- a/shared/extra-utils/videos/video-change-ownership.ts
+++ /dev/null
@@ -1,72 +0,0 @@
1import * as request from 'supertest'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function changeVideoOwnership (
5 url: string,
6 token: string,
7 videoId: number | string,
8 username,
9 expectedStatus = HttpStatusCode.NO_CONTENT_204
10) {
11 const path = '/api/v1/videos/' + videoId + '/give-ownership'
12
13 return request(url)
14 .post(path)
15 .set('Accept', 'application/json')
16 .set('Authorization', 'Bearer ' + token)
17 .send({ username })
18 .expect(expectedStatus)
19}
20
21function getVideoChangeOwnershipList (url: string, token: string) {
22 const path = '/api/v1/videos/ownership'
23
24 return request(url)
25 .get(path)
26 .query({ sort: '-createdAt' })
27 .set('Accept', 'application/json')
28 .set('Authorization', 'Bearer ' + token)
29 .expect(HttpStatusCode.OK_200)
30 .expect('Content-Type', /json/)
31}
32
33function acceptChangeOwnership (
34 url: string,
35 token: string,
36 ownershipId: string,
37 channelId: number,
38 expectedStatus = HttpStatusCode.NO_CONTENT_204
39) {
40 const path = '/api/v1/videos/ownership/' + ownershipId + '/accept'
41
42 return request(url)
43 .post(path)
44 .set('Accept', 'application/json')
45 .set('Authorization', 'Bearer ' + token)
46 .send({ channelId })
47 .expect(expectedStatus)
48}
49
50function refuseChangeOwnership (
51 url: string,
52 token: string,
53 ownershipId: string,
54 expectedStatus = HttpStatusCode.NO_CONTENT_204
55) {
56 const path = '/api/v1/videos/ownership/' + ownershipId + '/refuse'
57
58 return request(url)
59 .post(path)
60 .set('Accept', 'application/json')
61 .set('Authorization', 'Bearer ' + token)
62 .expect(expectedStatus)
63}
64
65// ---------------------------------------------------------------------------
66
67export {
68 changeVideoOwnership,
69 getVideoChangeOwnershipList,
70 acceptChangeOwnership,
71 refuseChangeOwnership
72}
diff --git a/shared/extra-utils/videos/video-channels.ts b/shared/extra-utils/videos/video-channels.ts
deleted file mode 100644
index 0aab93e52..000000000
--- a/shared/extra-utils/videos/video-channels.ts
+++ /dev/null
@@ -1,192 +0,0 @@
1/* eslint-disable @typescript-eslint/no-floating-promises */
2
3import * as request from 'supertest'
4import { VideoChannelUpdate } from '../../models/videos/channel/video-channel-update.model'
5import { VideoChannelCreate } from '../../models/videos/channel/video-channel-create.model'
6import { makeDeleteRequest, makeGetRequest, updateImageRequest } from '../requests/requests'
7import { ServerInfo } from '../server/servers'
8import { MyUser, User } from '../../models/users/user.model'
9import { getMyUserInformation } from '../users/users'
10import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
11
12function getVideoChannelsList (url: string, start: number, count: number, sort?: string, withStats?: boolean) {
13 const path = '/api/v1/video-channels'
14
15 const req = request(url)
16 .get(path)
17 .query({ start: start })
18 .query({ count: count })
19
20 if (sort) req.query({ sort })
21 if (withStats) req.query({ withStats })
22
23 return req.set('Accept', 'application/json')
24 .expect(HttpStatusCode.OK_200)
25 .expect('Content-Type', /json/)
26}
27
28function getAccountVideoChannelsList (parameters: {
29 url: string
30 accountName: string
31 start?: number
32 count?: number
33 sort?: string
34 specialStatus?: HttpStatusCode
35 withStats?: boolean
36 search?: string
37}) {
38 const {
39 url,
40 accountName,
41 start,
42 count,
43 sort = 'createdAt',
44 specialStatus = HttpStatusCode.OK_200,
45 withStats = false,
46 search
47 } = parameters
48
49 const path = '/api/v1/accounts/' + accountName + '/video-channels'
50
51 return makeGetRequest({
52 url,
53 path,
54 query: {
55 start,
56 count,
57 sort,
58 withStats,
59 search
60 },
61 statusCodeExpected: specialStatus
62 })
63}
64
65function addVideoChannel (
66 url: string,
67 token: string,
68 videoChannelAttributesArg: VideoChannelCreate,
69 expectedStatus = HttpStatusCode.OK_200
70) {
71 const path = '/api/v1/video-channels/'
72
73 // Default attributes
74 let attributes = {
75 displayName: 'my super video channel',
76 description: 'my super channel description',
77 support: 'my super channel support'
78 }
79 attributes = Object.assign(attributes, videoChannelAttributesArg)
80
81 return request(url)
82 .post(path)
83 .send(attributes)
84 .set('Accept', 'application/json')
85 .set('Authorization', 'Bearer ' + token)
86 .expect(expectedStatus)
87}
88
89function updateVideoChannel (
90 url: string,
91 token: string,
92 channelName: string,
93 attributes: VideoChannelUpdate,
94 expectedStatus = HttpStatusCode.NO_CONTENT_204
95) {
96 const body: any = {}
97 const path = '/api/v1/video-channels/' + channelName
98
99 if (attributes.displayName) body.displayName = attributes.displayName
100 if (attributes.description) body.description = attributes.description
101 if (attributes.support) body.support = attributes.support
102 if (attributes.bulkVideosSupportUpdate) body.bulkVideosSupportUpdate = attributes.bulkVideosSupportUpdate
103
104 return request(url)
105 .put(path)
106 .send(body)
107 .set('Accept', 'application/json')
108 .set('Authorization', 'Bearer ' + token)
109 .expect(expectedStatus)
110}
111
112function deleteVideoChannel (url: string, token: string, channelName: string, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
113 const path = '/api/v1/video-channels/' + channelName
114
115 return request(url)
116 .delete(path)
117 .set('Accept', 'application/json')
118 .set('Authorization', 'Bearer ' + token)
119 .expect(expectedStatus)
120}
121
122function getVideoChannel (url: string, channelName: string) {
123 const path = '/api/v1/video-channels/' + channelName
124
125 return request(url)
126 .get(path)
127 .set('Accept', 'application/json')
128 .expect(HttpStatusCode.OK_200)
129 .expect('Content-Type', /json/)
130}
131
132function updateVideoChannelImage (options: {
133 url: string
134 accessToken: string
135 fixture: string
136 videoChannelName: string | number
137 type: 'avatar' | 'banner'
138}) {
139 const path = `/api/v1/video-channels/${options.videoChannelName}/${options.type}/pick`
140
141 return updateImageRequest({ ...options, path, fieldname: options.type + 'file' })
142}
143
144function deleteVideoChannelImage (options: {
145 url: string
146 accessToken: string
147 videoChannelName: string | number
148 type: 'avatar' | 'banner'
149}) {
150 const path = `/api/v1/video-channels/${options.videoChannelName}/${options.type}`
151
152 return makeDeleteRequest({
153 url: options.url,
154 token: options.accessToken,
155 path,
156 statusCodeExpected: 204
157 })
158}
159
160function setDefaultVideoChannel (servers: ServerInfo[]) {
161 const tasks: Promise<any>[] = []
162
163 for (const server of servers) {
164 const p = getMyUserInformation(server.url, server.accessToken)
165 .then(res => { server.videoChannel = (res.body as User).videoChannels[0] })
166
167 tasks.push(p)
168 }
169
170 return Promise.all(tasks)
171}
172
173async function getDefaultVideoChannel (url: string, token: string) {
174 const res = await getMyUserInformation(url, token)
175
176 return (res.body as MyUser).videoChannels[0].id
177}
178
179// ---------------------------------------------------------------------------
180
181export {
182 updateVideoChannelImage,
183 getVideoChannelsList,
184 getAccountVideoChannelsList,
185 addVideoChannel,
186 updateVideoChannel,
187 deleteVideoChannel,
188 getVideoChannel,
189 setDefaultVideoChannel,
190 deleteVideoChannelImage,
191 getDefaultVideoChannel
192}
diff --git a/shared/extra-utils/videos/video-comments.ts b/shared/extra-utils/videos/video-comments.ts
deleted file mode 100644
index 71b9f875a..000000000
--- a/shared/extra-utils/videos/video-comments.ts
+++ /dev/null
@@ -1,138 +0,0 @@
1/* eslint-disable @typescript-eslint/no-floating-promises */
2
3import * as request from 'supertest'
4import { makeDeleteRequest, makeGetRequest } from '../requests/requests'
5import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
6
7function getAdminVideoComments (options: {
8 url: string
9 token: string
10 start: number
11 count: number
12 sort?: string
13 isLocal?: boolean
14 search?: string
15 searchAccount?: string
16 searchVideo?: string
17}) {
18 const { url, token, start, count, sort, isLocal, search, searchAccount, searchVideo } = options
19 const path = '/api/v1/videos/comments'
20
21 const query = {
22 start,
23 count,
24 sort: sort || '-createdAt'
25 }
26
27 if (isLocal !== undefined) Object.assign(query, { isLocal })
28 if (search !== undefined) Object.assign(query, { search })
29 if (searchAccount !== undefined) Object.assign(query, { searchAccount })
30 if (searchVideo !== undefined) Object.assign(query, { searchVideo })
31
32 return makeGetRequest({
33 url,
34 path,
35 token,
36 query,
37 statusCodeExpected: HttpStatusCode.OK_200
38 })
39}
40
41function getVideoCommentThreads (url: string, videoId: number | string, start: number, count: number, sort?: string, token?: string) {
42 const path = '/api/v1/videos/' + videoId + '/comment-threads'
43
44 const req = request(url)
45 .get(path)
46 .query({ start: start })
47 .query({ count: count })
48
49 if (sort) req.query({ sort })
50 if (token) req.set('Authorization', 'Bearer ' + token)
51
52 return req.set('Accept', 'application/json')
53 .expect(HttpStatusCode.OK_200)
54 .expect('Content-Type', /json/)
55}
56
57function getVideoThreadComments (url: string, videoId: number | string, threadId: number, token?: string) {
58 const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId
59
60 const req = request(url)
61 .get(path)
62 .set('Accept', 'application/json')
63
64 if (token) req.set('Authorization', 'Bearer ' + token)
65
66 return req.expect(HttpStatusCode.OK_200)
67 .expect('Content-Type', /json/)
68}
69
70function addVideoCommentThread (
71 url: string,
72 token: string,
73 videoId: number | string,
74 text: string,
75 expectedStatus = HttpStatusCode.OK_200
76) {
77 const path = '/api/v1/videos/' + videoId + '/comment-threads'
78
79 return request(url)
80 .post(path)
81 .send({ text })
82 .set('Accept', 'application/json')
83 .set('Authorization', 'Bearer ' + token)
84 .expect(expectedStatus)
85}
86
87function addVideoCommentReply (
88 url: string,
89 token: string,
90 videoId: number | string,
91 inReplyToCommentId: number,
92 text: string,
93 expectedStatus = HttpStatusCode.OK_200
94) {
95 const path = '/api/v1/videos/' + videoId + '/comments/' + inReplyToCommentId
96
97 return request(url)
98 .post(path)
99 .send({ text })
100 .set('Accept', 'application/json')
101 .set('Authorization', 'Bearer ' + token)
102 .expect(expectedStatus)
103}
104
105async function findCommentId (url: string, videoId: number | string, text: string) {
106 const res = await getVideoCommentThreads(url, videoId, 0, 25, '-createdAt')
107
108 return res.body.data.find(c => c.text === text).id as number
109}
110
111function deleteVideoComment (
112 url: string,
113 token: string,
114 videoId: number | string,
115 commentId: number,
116 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
117) {
118 const path = '/api/v1/videos/' + videoId + '/comments/' + commentId
119
120 return makeDeleteRequest({
121 url,
122 path,
123 token,
124 statusCodeExpected
125 })
126}
127
128// ---------------------------------------------------------------------------
129
130export {
131 getVideoCommentThreads,
132 getAdminVideoComments,
133 getVideoThreadComments,
134 addVideoCommentThread,
135 addVideoCommentReply,
136 findCommentId,
137 deleteVideoComment
138}
diff --git a/shared/extra-utils/videos/video-history.ts b/shared/extra-utils/videos/video-history.ts
deleted file mode 100644
index b989e14dc..000000000
--- a/shared/extra-utils/videos/video-history.ts
+++ /dev/null
@@ -1,49 +0,0 @@
1import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function userWatchVideo (
5 url: string,
6 token: string,
7 videoId: number | string,
8 currentTime: number,
9 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
10) {
11 const path = '/api/v1/videos/' + videoId + '/watching'
12 const fields = { currentTime }
13
14 return makePutBodyRequest({ url, path, token, fields, statusCodeExpected })
15}
16
17function listMyVideosHistory (url: string, token: string, search?: string) {
18 const path = '/api/v1/users/me/history/videos'
19
20 return makeGetRequest({
21 url,
22 path,
23 token,
24 query: {
25 search
26 },
27 statusCodeExpected: HttpStatusCode.OK_200
28 })
29}
30
31function removeMyVideosHistory (url: string, token: string, beforeDate?: string) {
32 const path = '/api/v1/users/me/history/videos/remove'
33
34 return makePostBodyRequest({
35 url,
36 path,
37 token,
38 fields: beforeDate ? { beforeDate } : {},
39 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
40 })
41}
42
43// ---------------------------------------------------------------------------
44
45export {
46 userWatchVideo,
47 listMyVideosHistory,
48 removeMyVideosHistory
49}
diff --git a/shared/extra-utils/videos/video-imports.ts b/shared/extra-utils/videos/video-imports.ts
deleted file mode 100644
index 81c0163cb..000000000
--- a/shared/extra-utils/videos/video-imports.ts
+++ /dev/null
@@ -1,90 +0,0 @@
1
2import { VideoImportCreate } from '../../models/videos'
3import { makeGetRequest, makeUploadRequest } from '../requests/requests'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
5
6function getYoutubeVideoUrl () {
7 return 'https://www.youtube.com/watch?v=msX3jv1XdvM'
8}
9
10function getYoutubeHDRVideoUrl () {
11 /**
12 * The video is used to check format-selection correctness wrt. HDR,
13 * which brings its own set of oddities outside of a MediaSource.
14 * FIXME: refactor once HDR is supported at playback
15 *
16 * The video needs to have the following format_ids:
17 * (which you can check by using `youtube-dl <url> -F`):
18 * - 303 (1080p webm vp9)
19 * - 299 (1080p mp4 avc1)
20 * - 335 (1080p webm vp9.2 HDR)
21 *
22 * 15 jan. 2021: TEST VIDEO NOT CURRENTLY PROVIDING
23 * - 400 (1080p mp4 av01)
24 * - 315 (2160p webm vp9 HDR)
25 * - 337 (2160p webm vp9.2 HDR)
26 * - 401 (2160p mp4 av01 HDR)
27 */
28 return 'https://www.youtube.com/watch?v=qR5vOXbZsI4'
29}
30
31function getMagnetURI () {
32 // eslint-disable-next-line max-len
33 return 'magnet:?xs=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Ftorrents%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.torrent&xt=urn:btih:0f498834733e8057ed5c6f2ee2b4efd8d84a76ee&dn=super+peertube2+video&tr=wss%3A%2F%2Fpeertube2.cpy.re%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube2.cpy.re%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fwebseed%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.mp4'
34}
35
36function getBadVideoUrl () {
37 return 'https://download.cpy.re/peertube/bad_video.mp4'
38}
39
40function getGoodVideoUrl () {
41 return 'https://download.cpy.re/peertube/good_video.mp4'
42}
43
44function importVideo (
45 url: string,
46 token: string,
47 attributes: VideoImportCreate & { torrentfile?: string },
48 statusCodeExpected = HttpStatusCode.OK_200
49) {
50 const path = '/api/v1/videos/imports'
51
52 let attaches: any = {}
53 if (attributes.torrentfile) attaches = { torrentfile: attributes.torrentfile }
54
55 return makeUploadRequest({
56 url,
57 path,
58 token,
59 attaches,
60 fields: attributes,
61 statusCodeExpected
62 })
63}
64
65function getMyVideoImports (url: string, token: string, sort?: string) {
66 const path = '/api/v1/users/me/videos/imports'
67
68 const query = {}
69 if (sort) query['sort'] = sort
70
71 return makeGetRequest({
72 url,
73 query,
74 path,
75 token,
76 statusCodeExpected: HttpStatusCode.OK_200
77 })
78}
79
80// ---------------------------------------------------------------------------
81
82export {
83 getBadVideoUrl,
84 getYoutubeVideoUrl,
85 getYoutubeHDRVideoUrl,
86 importVideo,
87 getMagnetURI,
88 getMyVideoImports,
89 getGoodVideoUrl
90}
diff --git a/shared/extra-utils/videos/video-playlists.ts b/shared/extra-utils/videos/video-playlists.ts
deleted file mode 100644
index c6f799e5d..000000000
--- a/shared/extra-utils/videos/video-playlists.ts
+++ /dev/null
@@ -1,320 +0,0 @@
1import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest, makeUploadRequest } from '../requests/requests'
2import { VideoPlaylistCreate } from '../../models/videos/playlist/video-playlist-create.model'
3import { omit } from 'lodash'
4import { VideoPlaylistUpdate } from '../../models/videos/playlist/video-playlist-update.model'
5import { VideoPlaylistElementCreate } from '../../models/videos/playlist/video-playlist-element-create.model'
6import { VideoPlaylistElementUpdate } from '../../models/videos/playlist/video-playlist-element-update.model'
7import { videoUUIDToId } from './videos'
8import { join } from 'path'
9import { root } from '..'
10import { readdir } from 'fs-extra'
11import { expect } from 'chai'
12import { VideoPlaylistType } from '../../models/videos/playlist/video-playlist-type.model'
13import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
14
15function getVideoPlaylistsList (url: string, start: number, count: number, sort?: string) {
16 const path = '/api/v1/video-playlists'
17
18 const query = {
19 start,
20 count,
21 sort
22 }
23
24 return makeGetRequest({
25 url,
26 path,
27 query,
28 statusCodeExpected: HttpStatusCode.OK_200
29 })
30}
31
32function getVideoChannelPlaylistsList (url: string, videoChannelName: string, start: number, count: number, sort?: string) {
33 const path = '/api/v1/video-channels/' + videoChannelName + '/video-playlists'
34
35 const query = {
36 start,
37 count,
38 sort
39 }
40
41 return makeGetRequest({
42 url,
43 path,
44 query,
45 statusCodeExpected: HttpStatusCode.OK_200
46 })
47}
48
49function getAccountPlaylistsList (url: string, accountName: string, start: number, count: number, sort?: string, search?: string) {
50 const path = '/api/v1/accounts/' + accountName + '/video-playlists'
51
52 const query = {
53 start,
54 count,
55 sort,
56 search
57 }
58
59 return makeGetRequest({
60 url,
61 path,
62 query,
63 statusCodeExpected: HttpStatusCode.OK_200
64 })
65}
66
67function getAccountPlaylistsListWithToken (
68 url: string,
69 token: string,
70 accountName: string,
71 start: number,
72 count: number,
73 playlistType?: VideoPlaylistType,
74 sort?: string
75) {
76 const path = '/api/v1/accounts/' + accountName + '/video-playlists'
77
78 const query = {
79 start,
80 count,
81 playlistType,
82 sort
83 }
84
85 return makeGetRequest({
86 url,
87 token,
88 path,
89 query,
90 statusCodeExpected: HttpStatusCode.OK_200
91 })
92}
93
94function getVideoPlaylist (url: string, playlistId: number | string, statusCodeExpected = HttpStatusCode.OK_200) {
95 const path = '/api/v1/video-playlists/' + playlistId
96
97 return makeGetRequest({
98 url,
99 path,
100 statusCodeExpected
101 })
102}
103
104function getVideoPlaylistWithToken (url: string, token: string, playlistId: number | string, statusCodeExpected = HttpStatusCode.OK_200) {
105 const path = '/api/v1/video-playlists/' + playlistId
106
107 return makeGetRequest({
108 url,
109 token,
110 path,
111 statusCodeExpected
112 })
113}
114
115function deleteVideoPlaylist (url: string, token: string, playlistId: number | string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
116 const path = '/api/v1/video-playlists/' + playlistId
117
118 return makeDeleteRequest({
119 url,
120 path,
121 token,
122 statusCodeExpected
123 })
124}
125
126function createVideoPlaylist (options: {
127 url: string
128 token: string
129 playlistAttrs: VideoPlaylistCreate
130 expectedStatus?: number
131}) {
132 const path = '/api/v1/video-playlists'
133
134 const fields = omit(options.playlistAttrs, 'thumbnailfile')
135
136 const attaches = options.playlistAttrs.thumbnailfile
137 ? { thumbnailfile: options.playlistAttrs.thumbnailfile }
138 : {}
139
140 return makeUploadRequest({
141 method: 'POST',
142 url: options.url,
143 path,
144 token: options.token,
145 fields,
146 attaches,
147 statusCodeExpected: options.expectedStatus || HttpStatusCode.OK_200
148 })
149}
150
151function updateVideoPlaylist (options: {
152 url: string
153 token: string
154 playlistAttrs: VideoPlaylistUpdate
155 playlistId: number | string
156 expectedStatus?: number
157}) {
158 const path = '/api/v1/video-playlists/' + options.playlistId
159
160 const fields = omit(options.playlistAttrs, 'thumbnailfile')
161
162 const attaches = options.playlistAttrs.thumbnailfile
163 ? { thumbnailfile: options.playlistAttrs.thumbnailfile }
164 : {}
165
166 return makeUploadRequest({
167 method: 'PUT',
168 url: options.url,
169 path,
170 token: options.token,
171 fields,
172 attaches,
173 statusCodeExpected: options.expectedStatus || HttpStatusCode.NO_CONTENT_204
174 })
175}
176
177async function addVideoInPlaylist (options: {
178 url: string
179 token: string
180 playlistId: number | string
181 elementAttrs: VideoPlaylistElementCreate | { videoId: string }
182 expectedStatus?: number
183}) {
184 options.elementAttrs.videoId = await videoUUIDToId(options.url, options.elementAttrs.videoId)
185
186 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos'
187
188 return makePostBodyRequest({
189 url: options.url,
190 path,
191 token: options.token,
192 fields: options.elementAttrs,
193 statusCodeExpected: options.expectedStatus || HttpStatusCode.OK_200
194 })
195}
196
197function updateVideoPlaylistElement (options: {
198 url: string
199 token: string
200 playlistId: number | string
201 playlistElementId: number | string
202 elementAttrs: VideoPlaylistElementUpdate
203 expectedStatus?: number
204}) {
205 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.playlistElementId
206
207 return makePutBodyRequest({
208 url: options.url,
209 path,
210 token: options.token,
211 fields: options.elementAttrs,
212 statusCodeExpected: options.expectedStatus || HttpStatusCode.NO_CONTENT_204
213 })
214}
215
216function removeVideoFromPlaylist (options: {
217 url: string
218 token: string
219 playlistId: number | string
220 playlistElementId: number
221 expectedStatus?: number
222}) {
223 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.playlistElementId
224
225 return makeDeleteRequest({
226 url: options.url,
227 path,
228 token: options.token,
229 statusCodeExpected: options.expectedStatus || HttpStatusCode.NO_CONTENT_204
230 })
231}
232
233function reorderVideosPlaylist (options: {
234 url: string
235 token: string
236 playlistId: number | string
237 elementAttrs: {
238 startPosition: number
239 insertAfterPosition: number
240 reorderLength?: number
241 }
242 expectedStatus?: number
243}) {
244 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/reorder'
245
246 return makePostBodyRequest({
247 url: options.url,
248 path,
249 token: options.token,
250 fields: options.elementAttrs,
251 statusCodeExpected: options.expectedStatus || HttpStatusCode.NO_CONTENT_204
252 })
253}
254
255async function checkPlaylistFilesWereRemoved (
256 playlistUUID: string,
257 internalServerNumber: number,
258 directories = [ 'thumbnails' ]
259) {
260 const testDirectory = 'test' + internalServerNumber
261
262 for (const directory of directories) {
263 const directoryPath = join(root(), testDirectory, directory)
264
265 const files = await readdir(directoryPath)
266 for (const file of files) {
267 expect(file).to.not.contain(playlistUUID)
268 }
269 }
270}
271
272function getVideoPlaylistPrivacies (url: string) {
273 const path = '/api/v1/video-playlists/privacies'
274
275 return makeGetRequest({
276 url,
277 path,
278 statusCodeExpected: HttpStatusCode.OK_200
279 })
280}
281
282function doVideosExistInMyPlaylist (url: string, token: string, videoIds: number[]) {
283 const path = '/api/v1/users/me/video-playlists/videos-exist'
284
285 return makeGetRequest({
286 url,
287 token,
288 path,
289 query: { videoIds },
290 statusCodeExpected: HttpStatusCode.OK_200
291 })
292}
293
294// ---------------------------------------------------------------------------
295
296export {
297 getVideoPlaylistPrivacies,
298
299 getVideoPlaylistsList,
300 getVideoChannelPlaylistsList,
301 getAccountPlaylistsList,
302 getAccountPlaylistsListWithToken,
303
304 getVideoPlaylist,
305 getVideoPlaylistWithToken,
306
307 createVideoPlaylist,
308 updateVideoPlaylist,
309 deleteVideoPlaylist,
310
311 addVideoInPlaylist,
312 updateVideoPlaylistElement,
313 removeVideoFromPlaylist,
314
315 reorderVideosPlaylist,
316
317 checkPlaylistFilesWereRemoved,
318
319 doVideosExistInMyPlaylist
320}
diff --git a/shared/extra-utils/videos/video-streaming-playlists.ts b/shared/extra-utils/videos/video-streaming-playlists.ts
deleted file mode 100644
index 99c2e1880..000000000
--- a/shared/extra-utils/videos/video-streaming-playlists.ts
+++ /dev/null
@@ -1,82 +0,0 @@
1import { makeRawRequest } from '../requests/requests'
2import { sha256 } from '../../../server/helpers/core-utils'
3import { VideoStreamingPlaylist } from '../../models/videos/video-streaming-playlist.model'
4import { expect } from 'chai'
5import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
6
7function getPlaylist (url: string, statusCodeExpected = HttpStatusCode.OK_200) {
8 return makeRawRequest(url, statusCodeExpected)
9}
10
11function getSegment (url: string, statusCodeExpected = HttpStatusCode.OK_200, range?: string) {
12 return makeRawRequest(url, statusCodeExpected, range)
13}
14
15function getSegmentSha256 (url: string, statusCodeExpected = HttpStatusCode.OK_200) {
16 return makeRawRequest(url, statusCodeExpected)
17}
18
19async function checkSegmentHash (
20 baseUrlPlaylist: string,
21 baseUrlSegment: string,
22 videoUUID: string,
23 resolution: number,
24 hlsPlaylist: VideoStreamingPlaylist
25) {
26 const res = await getPlaylist(`${baseUrlPlaylist}/${videoUUID}/${resolution}.m3u8`)
27 const playlist = res.text
28
29 const videoName = `${videoUUID}-${resolution}-fragmented.mp4`
30
31 const matches = /#EXT-X-BYTERANGE:(\d+)@(\d+)/.exec(playlist)
32
33 const length = parseInt(matches[1], 10)
34 const offset = parseInt(matches[2], 10)
35 const range = `${offset}-${offset + length - 1}`
36
37 const res2 = await getSegment(`${baseUrlSegment}/${videoUUID}/${videoName}`, HttpStatusCode.PARTIAL_CONTENT_206, `bytes=${range}`)
38
39 const resSha = await getSegmentSha256(hlsPlaylist.segmentsSha256Url)
40
41 const sha256Server = resSha.body[videoName][range]
42 expect(sha256(res2.body)).to.equal(sha256Server)
43}
44
45async function checkLiveSegmentHash (
46 baseUrlSegment: string,
47 videoUUID: string,
48 segmentName: string,
49 hlsPlaylist: VideoStreamingPlaylist
50) {
51 const res2 = await getSegment(`${baseUrlSegment}/${videoUUID}/${segmentName}`)
52
53 const resSha = await getSegmentSha256(hlsPlaylist.segmentsSha256Url)
54
55 const sha256Server = resSha.body[segmentName]
56 expect(sha256(res2.body)).to.equal(sha256Server)
57}
58
59async function checkResolutionsInMasterPlaylist (playlistUrl: string, resolutions: number[]) {
60 const res = await getPlaylist(playlistUrl)
61
62 const masterPlaylist = res.text
63
64 for (const resolution of resolutions) {
65 const reg = new RegExp(
66 '#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + ',(FRAME-RATE=\\d+,)?CODECS="avc1.64001f,mp4a.40.2"'
67 )
68
69 expect(masterPlaylist).to.match(reg)
70 }
71}
72
73// ---------------------------------------------------------------------------
74
75export {
76 getPlaylist,
77 getSegment,
78 checkResolutionsInMasterPlaylist,
79 getSegmentSha256,
80 checkLiveSegmentHash,
81 checkSegmentHash
82}
diff --git a/shared/extra-utils/videos/videos-command.ts b/shared/extra-utils/videos/videos-command.ts
new file mode 100644
index 000000000..33725bfdc
--- /dev/null
+++ b/shared/extra-utils/videos/videos-command.ts
@@ -0,0 +1,599 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */
2
3import { expect } from 'chai'
4import { createReadStream, stat } from 'fs-extra'
5import got, { Response as GotResponse } from 'got'
6import { omit } from 'lodash'
7import validator from 'validator'
8import { buildUUID } from '@server/helpers/uuid'
9import { loadLanguages } from '@server/initializers/constants'
10import { pick } from '@shared/core-utils'
11import {
12 HttpStatusCode,
13 ResultList,
14 UserVideoRateType,
15 Video,
16 VideoCreate,
17 VideoCreateResult,
18 VideoDetails,
19 VideoFileMetadata,
20 VideoPrivacy,
21 VideosCommonQuery,
22 VideosWithSearchCommonQuery
23} from '@shared/models'
24import { buildAbsoluteFixturePath, wait } from '../miscs'
25import { unwrapBody } from '../requests'
26import { PeerTubeServer, waitJobs } from '../server'
27import { AbstractCommand, OverrideCommandOptions } from '../shared'
28
29export type VideoEdit = Partial<Omit<VideoCreate, 'thumbnailfile' | 'previewfile'>> & {
30 fixture?: string
31 thumbnailfile?: string
32 previewfile?: string
33}
34
35export class VideosCommand extends AbstractCommand {
36
37 constructor (server: PeerTubeServer) {
38 super(server)
39
40 loadLanguages()
41 }
42
43 getCategories (options: OverrideCommandOptions = {}) {
44 const path = '/api/v1/videos/categories'
45
46 return this.getRequestBody<{ [id: number]: string }>({
47 ...options,
48 path,
49
50 implicitToken: false,
51 defaultExpectedStatus: HttpStatusCode.OK_200
52 })
53 }
54
55 getLicences (options: OverrideCommandOptions = {}) {
56 const path = '/api/v1/videos/licences'
57
58 return this.getRequestBody<{ [id: number]: string }>({
59 ...options,
60 path,
61
62 implicitToken: false,
63 defaultExpectedStatus: HttpStatusCode.OK_200
64 })
65 }
66
67 getLanguages (options: OverrideCommandOptions = {}) {
68 const path = '/api/v1/videos/languages'
69
70 return this.getRequestBody<{ [id: string]: string }>({
71 ...options,
72 path,
73
74 implicitToken: false,
75 defaultExpectedStatus: HttpStatusCode.OK_200
76 })
77 }
78
79 getPrivacies (options: OverrideCommandOptions = {}) {
80 const path = '/api/v1/videos/privacies'
81
82 return this.getRequestBody<{ [id in VideoPrivacy]: string }>({
83 ...options,
84 path,
85
86 implicitToken: false,
87 defaultExpectedStatus: HttpStatusCode.OK_200
88 })
89 }
90
91 // ---------------------------------------------------------------------------
92
93 getDescription (options: OverrideCommandOptions & {
94 descriptionPath: string
95 }) {
96 return this.getRequestBody<{ description: string }>({
97 ...options,
98 path: options.descriptionPath,
99
100 implicitToken: false,
101 defaultExpectedStatus: HttpStatusCode.OK_200
102 })
103 }
104
105 getFileMetadata (options: OverrideCommandOptions & {
106 url: string
107 }) {
108 return unwrapBody<VideoFileMetadata>(this.getRawRequest({
109 ...options,
110
111 url: options.url,
112 implicitToken: false,
113 defaultExpectedStatus: HttpStatusCode.OK_200
114 }))
115 }
116
117 // ---------------------------------------------------------------------------
118
119 view (options: OverrideCommandOptions & {
120 id: number | string
121 xForwardedFor?: string
122 }) {
123 const { id, xForwardedFor } = options
124 const path = '/api/v1/videos/' + id + '/views'
125
126 return this.postBodyRequest({
127 ...options,
128
129 path,
130 xForwardedFor,
131 implicitToken: false,
132 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
133 })
134 }
135
136 rate (options: OverrideCommandOptions & {
137 id: number | string
138 rating: UserVideoRateType
139 }) {
140 const { id, rating } = options
141 const path = '/api/v1/videos/' + id + '/rate'
142
143 return this.putBodyRequest({
144 ...options,
145
146 path,
147 fields: { rating },
148 implicitToken: true,
149 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
150 })
151 }
152
153 // ---------------------------------------------------------------------------
154
155 get (options: OverrideCommandOptions & {
156 id: number | string
157 }) {
158 const path = '/api/v1/videos/' + options.id
159
160 return this.getRequestBody<VideoDetails>({
161 ...options,
162
163 path,
164 implicitToken: false,
165 defaultExpectedStatus: HttpStatusCode.OK_200
166 })
167 }
168
169 getWithToken (options: OverrideCommandOptions & {
170 id: number | string
171 }) {
172 return this.get({
173 ...options,
174
175 token: this.buildCommonRequestToken({ ...options, implicitToken: true })
176 })
177 }
178
179 async getId (options: OverrideCommandOptions & {
180 uuid: number | string
181 }) {
182 const { uuid } = options
183
184 if (validator.isUUID('' + uuid) === false) return uuid as number
185
186 const { id } = await this.get({ ...options, id: uuid })
187
188 return id
189 }
190
191 // ---------------------------------------------------------------------------
192
193 listMyVideos (options: OverrideCommandOptions & {
194 start?: number
195 count?: number
196 sort?: string
197 search?: string
198 isLive?: boolean
199 } = {}) {
200 const path = '/api/v1/users/me/videos'
201
202 return this.getRequestBody<ResultList<Video>>({
203 ...options,
204
205 path,
206 query: pick(options, [ 'start', 'count', 'sort', 'search', 'isLive' ]),
207 implicitToken: true,
208 defaultExpectedStatus: HttpStatusCode.OK_200
209 })
210 }
211
212 // ---------------------------------------------------------------------------
213
214 list (options: OverrideCommandOptions & VideosCommonQuery = {}) {
215 const path = '/api/v1/videos'
216
217 const query = this.buildListQuery(options)
218
219 return this.getRequestBody<ResultList<Video>>({
220 ...options,
221
222 path,
223 query: { sort: 'name', ...query },
224 implicitToken: false,
225 defaultExpectedStatus: HttpStatusCode.OK_200
226 })
227 }
228
229 listWithToken (options: OverrideCommandOptions & VideosCommonQuery = {}) {
230 return this.list({
231 ...options,
232
233 token: this.buildCommonRequestToken({ ...options, implicitToken: true })
234 })
235 }
236
237 listByAccount (options: OverrideCommandOptions & VideosWithSearchCommonQuery & {
238 handle: string
239 }) {
240 const { handle, search } = options
241 const path = '/api/v1/accounts/' + handle + '/videos'
242
243 return this.getRequestBody<ResultList<Video>>({
244 ...options,
245
246 path,
247 query: { search, ...this.buildListQuery(options) },
248 implicitToken: true,
249 defaultExpectedStatus: HttpStatusCode.OK_200
250 })
251 }
252
253 listByChannel (options: OverrideCommandOptions & VideosWithSearchCommonQuery & {
254 handle: string
255 }) {
256 const { handle } = options
257 const path = '/api/v1/video-channels/' + handle + '/videos'
258
259 return this.getRequestBody<ResultList<Video>>({
260 ...options,
261
262 path,
263 query: this.buildListQuery(options),
264 implicitToken: true,
265 defaultExpectedStatus: HttpStatusCode.OK_200
266 })
267 }
268
269 // ---------------------------------------------------------------------------
270
271 async find (options: OverrideCommandOptions & {
272 name: string
273 }) {
274 const { data } = await this.list(options)
275
276 return data.find(v => v.name === options.name)
277 }
278
279 // ---------------------------------------------------------------------------
280
281 update (options: OverrideCommandOptions & {
282 id: number | string
283 attributes?: VideoEdit
284 }) {
285 const { id, attributes = {} } = options
286 const path = '/api/v1/videos/' + id
287
288 // Upload request
289 if (attributes.thumbnailfile || attributes.previewfile) {
290 const attaches: any = {}
291 if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile
292 if (attributes.previewfile) attaches.previewfile = attributes.previewfile
293
294 return this.putUploadRequest({
295 ...options,
296
297 path,
298 fields: options.attributes,
299 attaches: {
300 thumbnailfile: attributes.thumbnailfile,
301 previewfile: attributes.previewfile
302 },
303 implicitToken: true,
304 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
305 })
306 }
307
308 return this.putBodyRequest({
309 ...options,
310
311 path,
312 fields: options.attributes,
313 implicitToken: true,
314 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
315 })
316 }
317
318 remove (options: OverrideCommandOptions & {
319 id: number | string
320 }) {
321 const path = '/api/v1/videos/' + options.id
322
323 return unwrapBody(this.deleteRequest({
324 ...options,
325
326 path,
327 implicitToken: true,
328 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
329 }))
330 }
331
332 async removeAll () {
333 const { data } = await this.list()
334
335 for (const v of data) {
336 await this.remove({ id: v.id })
337 }
338 }
339
340 // ---------------------------------------------------------------------------
341
342 async upload (options: OverrideCommandOptions & {
343 attributes?: VideoEdit
344 mode?: 'legacy' | 'resumable' // default legacy
345 } = {}) {
346 const { mode = 'legacy' } = options
347 let defaultChannelId = 1
348
349 try {
350 const { videoChannels } = await this.server.users.getMyInfo({ token: options.token })
351 defaultChannelId = videoChannels[0].id
352 } catch (e) { /* empty */ }
353
354 // Override default attributes
355 const attributes = {
356 name: 'my super video',
357 category: 5,
358 licence: 4,
359 language: 'zh',
360 channelId: defaultChannelId,
361 nsfw: true,
362 waitTranscoding: false,
363 description: 'my super description',
364 support: 'my super support text',
365 tags: [ 'tag' ],
366 privacy: VideoPrivacy.PUBLIC,
367 commentsEnabled: true,
368 downloadEnabled: true,
369 fixture: 'video_short.webm',
370
371 ...options.attributes
372 }
373
374 const created = mode === 'legacy'
375 ? await this.buildLegacyUpload({ ...options, attributes })
376 : await this.buildResumeUpload({ ...options, attributes })
377
378 // Wait torrent generation
379 const expectedStatus = this.buildExpectedStatus({ ...options, defaultExpectedStatus: HttpStatusCode.OK_200 })
380 if (expectedStatus === HttpStatusCode.OK_200) {
381 let video: VideoDetails
382
383 do {
384 video = await this.getWithToken({ ...options, id: created.uuid })
385
386 await wait(50)
387 } while (!video.files[0].torrentUrl)
388 }
389
390 return created
391 }
392
393 async buildLegacyUpload (options: OverrideCommandOptions & {
394 attributes: VideoEdit
395 }): Promise<VideoCreateResult> {
396 const path = '/api/v1/videos/upload'
397
398 return unwrapBody<{ video: VideoCreateResult }>(this.postUploadRequest({
399 ...options,
400
401 path,
402 fields: this.buildUploadFields(options.attributes),
403 attaches: this.buildUploadAttaches(options.attributes),
404 implicitToken: true,
405 defaultExpectedStatus: HttpStatusCode.OK_200
406 })).then(body => body.video || body as any)
407 }
408
409 async buildResumeUpload (options: OverrideCommandOptions & {
410 attributes: VideoEdit
411 }): Promise<VideoCreateResult> {
412 const { attributes, expectedStatus } = options
413
414 let size = 0
415 let videoFilePath: string
416 let mimetype = 'video/mp4'
417
418 if (attributes.fixture) {
419 videoFilePath = buildAbsoluteFixturePath(attributes.fixture)
420 size = (await stat(videoFilePath)).size
421
422 if (videoFilePath.endsWith('.mkv')) {
423 mimetype = 'video/x-matroska'
424 } else if (videoFilePath.endsWith('.webm')) {
425 mimetype = 'video/webm'
426 }
427 }
428
429 // Do not check status automatically, we'll check it manually
430 const initializeSessionRes = await this.prepareResumableUpload({ ...options, expectedStatus: null, attributes, size, mimetype })
431 const initStatus = initializeSessionRes.status
432
433 if (videoFilePath && initStatus === HttpStatusCode.CREATED_201) {
434 const locationHeader = initializeSessionRes.header['location']
435 expect(locationHeader).to.not.be.undefined
436
437 const pathUploadId = locationHeader.split('?')[1]
438
439 const result = await this.sendResumableChunks({ ...options, pathUploadId, videoFilePath, size })
440
441 return result.body?.video || result.body as any
442 }
443
444 const expectedInitStatus = expectedStatus === HttpStatusCode.OK_200
445 ? HttpStatusCode.CREATED_201
446 : expectedStatus
447
448 expect(initStatus).to.equal(expectedInitStatus)
449
450 return initializeSessionRes.body.video || initializeSessionRes.body
451 }
452
453 async prepareResumableUpload (options: OverrideCommandOptions & {
454 attributes: VideoEdit
455 size: number
456 mimetype: string
457 }) {
458 const { attributes, size, mimetype } = options
459
460 const path = '/api/v1/videos/upload-resumable'
461
462 return this.postUploadRequest({
463 ...options,
464
465 path,
466 headers: {
467 'X-Upload-Content-Type': mimetype,
468 'X-Upload-Content-Length': size.toString()
469 },
470 fields: { filename: attributes.fixture, ...this.buildUploadFields(options.attributes) },
471 // Fixture will be sent later
472 attaches: this.buildUploadAttaches(omit(options.attributes, 'fixture')),
473 implicitToken: true,
474
475 defaultExpectedStatus: null
476 })
477 }
478
479 sendResumableChunks (options: OverrideCommandOptions & {
480 pathUploadId: string
481 videoFilePath: string
482 size: number
483 contentLength?: number
484 contentRangeBuilder?: (start: number, chunk: any) => string
485 }) {
486 const { pathUploadId, videoFilePath, size, contentLength, contentRangeBuilder, expectedStatus = HttpStatusCode.OK_200 } = options
487
488 const path = '/api/v1/videos/upload-resumable'
489 let start = 0
490
491 const token = this.buildCommonRequestToken({ ...options, implicitToken: true })
492 const url = this.server.url
493
494 const readable = createReadStream(videoFilePath, { highWaterMark: 8 * 1024 })
495 return new Promise<GotResponse<{ video: VideoCreateResult }>>((resolve, reject) => {
496 readable.on('data', async function onData (chunk) {
497 readable.pause()
498
499 const headers = {
500 'Authorization': 'Bearer ' + token,
501 'Content-Type': 'application/octet-stream',
502 'Content-Range': contentRangeBuilder
503 ? contentRangeBuilder(start, chunk)
504 : `bytes ${start}-${start + chunk.length - 1}/${size}`,
505 'Content-Length': contentLength ? contentLength + '' : chunk.length + ''
506 }
507
508 const res = await got<{ video: VideoCreateResult }>({
509 url,
510 method: 'put',
511 headers,
512 path: path + '?' + pathUploadId,
513 body: chunk,
514 responseType: 'json',
515 throwHttpErrors: false
516 })
517
518 start += chunk.length
519
520 if (res.statusCode === expectedStatus) {
521 return resolve(res)
522 }
523
524 if (res.statusCode !== HttpStatusCode.PERMANENT_REDIRECT_308) {
525 readable.off('data', onData)
526 return reject(new Error('Incorrect transient behaviour sending intermediary chunks'))
527 }
528
529 readable.resume()
530 })
531 })
532 }
533
534 quickUpload (options: OverrideCommandOptions & {
535 name: string
536 nsfw?: boolean
537 privacy?: VideoPrivacy
538 fixture?: string
539 }) {
540 const attributes: VideoEdit = { name: options.name }
541 if (options.nsfw) attributes.nsfw = options.nsfw
542 if (options.privacy) attributes.privacy = options.privacy
543 if (options.fixture) attributes.fixture = options.fixture
544
545 return this.upload({ ...options, attributes })
546 }
547
548 async randomUpload (options: OverrideCommandOptions & {
549 wait?: boolean // default true
550 additionalParams?: VideoEdit & { prefixName?: string }
551 } = {}) {
552 const { wait = true, additionalParams } = options
553 const prefixName = additionalParams?.prefixName || ''
554 const name = prefixName + buildUUID()
555
556 const attributes = { name, ...additionalParams }
557
558 const result = await this.upload({ ...options, attributes })
559
560 if (wait) await waitJobs([ this.server ])
561
562 return { ...result, name }
563 }
564
565 // ---------------------------------------------------------------------------
566
567 private buildListQuery (options: VideosCommonQuery) {
568 return pick(options, [
569 'start',
570 'count',
571 'sort',
572 'nsfw',
573 'isLive',
574 'categoryOneOf',
575 'licenceOneOf',
576 'languageOneOf',
577 'tagsOneOf',
578 'tagsAllOf',
579 'filter',
580 'skipCount'
581 ])
582 }
583
584 private buildUploadFields (attributes: VideoEdit) {
585 return omit(attributes, [ 'fixture', 'thumbnailfile', 'previewfile' ])
586 }
587
588 private buildUploadAttaches (attributes: VideoEdit) {
589 const attaches: { [ name: string ]: string } = {}
590
591 for (const key of [ 'thumbnailfile', 'previewfile' ]) {
592 if (attributes[key]) attaches[key] = buildAbsoluteFixturePath(attributes[key])
593 }
594
595 if (attributes.fixture) attaches.videofile = buildAbsoluteFixturePath(attributes.fixture)
596
597 return attaches
598 }
599}
diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts
index 469ea4d63..a1d2ba0fc 100644
--- a/shared/extra-utils/videos/videos.ts
+++ b/shared/extra-utils/videos/videos.ts
@@ -1,646 +1,92 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */
2 2
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { createReadStream, pathExists, readdir, readFile, stat } from 'fs-extra' 4import { pathExists, readdir } from 'fs-extra'
5import got, { Response as GotResponse } from 'got/dist/source' 5import { basename, join } from 'path'
6import * as parseTorrent from 'parse-torrent'
7import { join } from 'path'
8import * as request from 'supertest'
9import validator from 'validator'
10import { getLowercaseExtension } from '@server/helpers/core-utils' 6import { getLowercaseExtension } from '@server/helpers/core-utils'
11import { buildUUID } from '@server/helpers/uuid' 7import { uuidRegex } from '@shared/core-utils'
12import { HttpStatusCode } from '@shared/core-utils' 8import { HttpStatusCode, VideoCaption, VideoDetails } from '@shared/models'
13import { VideosCommonQuery } from '@shared/models' 9import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants'
14import { loadLanguages, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants' 10import { dateIsValid, testImage, webtorrentAdd } from '../miscs'
15import { VideoDetails, VideoPrivacy } from '../../models/videos' 11import { makeRawRequest } from '../requests/requests'
16import { 12import { waitJobs } from '../server'
17 buildAbsoluteFixturePath, 13import { PeerTubeServer } from '../server/server'
18 buildServerDirectory, 14import { VideoEdit } from './videos-command'
19 dateIsValid, 15
20 immutableAssign, 16async function checkVideoFilesWereRemoved (options: {
21 testImage, 17 server: PeerTubeServer
22 wait, 18 video: VideoDetails
23 webtorrentAdd 19 captions?: VideoCaption[]
24} from '../miscs/miscs' 20 onlyVideoFiles?: boolean // default false
25import { makeGetRequest, makePutBodyRequest, makeRawRequest, makeUploadRequest } from '../requests/requests' 21}) {
26import { waitJobs } from '../server/jobs' 22 const { video, server, captions = [], onlyVideoFiles = false } = options
27import { ServerInfo } from '../server/servers'
28import { getMyUserInformation } from '../users/users'
29
30loadLanguages()
31
32type VideoAttributes = {
33 name?: string
34 category?: number
35 licence?: number
36 language?: string
37 nsfw?: boolean
38 commentsEnabled?: boolean
39 downloadEnabled?: boolean
40 waitTranscoding?: boolean
41 description?: string
42 originallyPublishedAt?: string
43 tags?: string[]
44 channelId?: number
45 privacy?: VideoPrivacy
46 fixture?: string
47 support?: string
48 thumbnailfile?: string
49 previewfile?: string
50 scheduleUpdate?: {
51 updateAt: string
52 privacy?: VideoPrivacy
53 }
54}
55
56function getVideoCategories (url: string) {
57 const path = '/api/v1/videos/categories'
58
59 return makeGetRequest({
60 url,
61 path,
62 statusCodeExpected: HttpStatusCode.OK_200
63 })
64}
65
66function getVideoLicences (url: string) {
67 const path = '/api/v1/videos/licences'
68
69 return makeGetRequest({
70 url,
71 path,
72 statusCodeExpected: HttpStatusCode.OK_200
73 })
74}
75
76function getVideoLanguages (url: string) {
77 const path = '/api/v1/videos/languages'
78
79 return makeGetRequest({
80 url,
81 path,
82 statusCodeExpected: HttpStatusCode.OK_200
83 })
84}
85
86function getVideoPrivacies (url: string) {
87 const path = '/api/v1/videos/privacies'
88
89 return makeGetRequest({
90 url,
91 path,
92 statusCodeExpected: HttpStatusCode.OK_200
93 })
94}
95
96function getVideo (url: string, id: number | string, expectedStatus = HttpStatusCode.OK_200) {
97 const path = '/api/v1/videos/' + id
98
99 return request(url)
100 .get(path)
101 .set('Accept', 'application/json')
102 .expect(expectedStatus)
103}
104 23
105async function getVideoIdFromUUID (url: string, uuid: string) { 24 const webtorrentFiles = video.files || []
106 const res = await getVideo(url, uuid) 25 const hlsFiles = video.streamingPlaylists[0]?.files || []
107 26
108 return res.body.id 27 const thumbnailName = basename(video.thumbnailPath)
109} 28 const previewName = basename(video.previewPath)
110 29
111function getVideoFileMetadataUrl (url: string) { 30 const torrentNames = webtorrentFiles.concat(hlsFiles).map(f => basename(f.torrentUrl))
112 return request(url)
113 .get('/')
114 .set('Accept', 'application/json')
115 .expect(HttpStatusCode.OK_200)
116 .expect('Content-Type', /json/)
117}
118 31
119function viewVideo (url: string, id: number | string, expectedStatus = HttpStatusCode.NO_CONTENT_204, xForwardedFor?: string) { 32 const captionNames = captions.map(c => basename(c.captionPath))
120 const path = '/api/v1/videos/' + id + '/views'
121 33
122 const req = request(url) 34 const webtorrentFilenames = webtorrentFiles.map(f => basename(f.fileUrl))
123 .post(path) 35 const hlsFilenames = hlsFiles.map(f => basename(f.fileUrl))
124 .set('Accept', 'application/json')
125 36
126 if (xForwardedFor) { 37 let directories: { [ directory: string ]: string[] } = {
127 req.set('X-Forwarded-For', xForwardedFor) 38 videos: webtorrentFilenames,
39 redundancy: webtorrentFilenames,
40 [join('playlists', 'hls')]: hlsFilenames,
41 [join('redundancy', 'hls')]: hlsFilenames
128 } 42 }
129 43
130 return req.expect(expectedStatus) 44 if (onlyVideoFiles !== true) {
131} 45 directories = {
132 46 ...directories,
133function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = HttpStatusCode.OK_200) {
134 const path = '/api/v1/videos/' + id
135
136 return request(url)
137 .get(path)
138 .set('Authorization', 'Bearer ' + token)
139 .set('Accept', 'application/json')
140 .expect(expectedStatus)
141}
142
143function getVideoDescription (url: string, descriptionPath: string) {
144 return request(url)
145 .get(descriptionPath)
146 .set('Accept', 'application/json')
147 .expect(HttpStatusCode.OK_200)
148 .expect('Content-Type', /json/)
149}
150
151function getVideosList (url: string) {
152 const path = '/api/v1/videos'
153
154 return request(url)
155 .get(path)
156 .query({ sort: 'name' })
157 .set('Accept', 'application/json')
158 .expect(HttpStatusCode.OK_200)
159 .expect('Content-Type', /json/)
160}
161
162function getVideosListWithToken (url: string, token: string, query: { nsfw?: boolean } = {}) {
163 const path = '/api/v1/videos'
164
165 return request(url)
166 .get(path)
167 .set('Authorization', 'Bearer ' + token)
168 .query(immutableAssign(query, { sort: 'name' }))
169 .set('Accept', 'application/json')
170 .expect(HttpStatusCode.OK_200)
171 .expect('Content-Type', /json/)
172}
173
174function getLocalVideos (url: string) {
175 const path = '/api/v1/videos'
176
177 return request(url)
178 .get(path)
179 .query({ sort: 'name', filter: 'local' })
180 .set('Accept', 'application/json')
181 .expect(HttpStatusCode.OK_200)
182 .expect('Content-Type', /json/)
183}
184
185function getMyVideos (url: string, accessToken: string, start: number, count: number, sort?: string, search?: string) {
186 const path = '/api/v1/users/me/videos'
187
188 const req = request(url)
189 .get(path)
190 .query({ start: start })
191 .query({ count: count })
192 .query({ search: search })
193
194 if (sort) req.query({ sort })
195
196 return req.set('Accept', 'application/json')
197 .set('Authorization', 'Bearer ' + accessToken)
198 .expect(HttpStatusCode.OK_200)
199 .expect('Content-Type', /json/)
200}
201
202function getMyVideosWithFilter (url: string, accessToken: string, query: { isLive?: boolean }) {
203 const path = '/api/v1/users/me/videos'
204
205 return makeGetRequest({
206 url,
207 path,
208 token: accessToken,
209 query,
210 statusCodeExpected: HttpStatusCode.OK_200
211 })
212}
213
214function getAccountVideos (
215 url: string,
216 accessToken: string,
217 accountName: string,
218 start: number,
219 count: number,
220 sort?: string,
221 query: {
222 nsfw?: boolean
223 search?: string
224 } = {}
225) {
226 const path = '/api/v1/accounts/' + accountName + '/videos'
227
228 return makeGetRequest({
229 url,
230 path,
231 query: immutableAssign(query, {
232 start,
233 count,
234 sort
235 }),
236 token: accessToken,
237 statusCodeExpected: HttpStatusCode.OK_200
238 })
239}
240
241function getVideoChannelVideos (
242 url: string,
243 accessToken: string,
244 videoChannelName: string,
245 start: number,
246 count: number,
247 sort?: string,
248 query: { nsfw?: boolean } = {}
249) {
250 const path = '/api/v1/video-channels/' + videoChannelName + '/videos'
251
252 return makeGetRequest({
253 url,
254 path,
255 query: immutableAssign(query, {
256 start,
257 count,
258 sort
259 }),
260 token: accessToken,
261 statusCodeExpected: HttpStatusCode.OK_200
262 })
263}
264
265function getPlaylistVideos (
266 url: string,
267 accessToken: string,
268 playlistId: number | string,
269 start: number,
270 count: number,
271 query: { nsfw?: boolean } = {}
272) {
273 const path = '/api/v1/video-playlists/' + playlistId + '/videos'
274
275 return makeGetRequest({
276 url,
277 path,
278 query: immutableAssign(query, {
279 start,
280 count
281 }),
282 token: accessToken,
283 statusCodeExpected: HttpStatusCode.OK_200
284 })
285}
286
287function getVideosListPagination (url: string, start: number, count: number, sort?: string, skipCount?: boolean) {
288 const path = '/api/v1/videos'
289
290 const req = request(url)
291 .get(path)
292 .query({ start: start })
293 .query({ count: count })
294
295 if (sort) req.query({ sort })
296 if (skipCount) req.query({ skipCount })
297
298 return req.set('Accept', 'application/json')
299 .expect(HttpStatusCode.OK_200)
300 .expect('Content-Type', /json/)
301}
302
303function getVideosListSort (url: string, sort: string) {
304 const path = '/api/v1/videos'
305
306 return request(url)
307 .get(path)
308 .query({ sort: sort })
309 .set('Accept', 'application/json')
310 .expect(HttpStatusCode.OK_200)
311 .expect('Content-Type', /json/)
312}
313
314function getVideosWithFilters (url: string, query: VideosCommonQuery) {
315 const path = '/api/v1/videos'
316
317 return request(url)
318 .get(path)
319 .query(query)
320 .set('Accept', 'application/json')
321 .expect(HttpStatusCode.OK_200)
322 .expect('Content-Type', /json/)
323}
324
325function removeVideo (url: string, token: string, id: number | string, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
326 const path = '/api/v1/videos'
327
328 return request(url)
329 .delete(path + '/' + id)
330 .set('Accept', 'application/json')
331 .set('Authorization', 'Bearer ' + token)
332 .expect(expectedStatus)
333}
334 47
335async function removeAllVideos (server: ServerInfo) { 48 thumbnails: [ thumbnailName ],
336 const resVideos = await getVideosList(server.url) 49 previews: [ previewName ],
337 50 torrents: torrentNames,
338 for (const v of resVideos.body.data) { 51 captions: captionNames
339 await removeVideo(server.url, server.accessToken, v.id) 52 }
340 } 53 }
341}
342 54
343async function checkVideoFilesWereRemoved ( 55 for (const directory of Object.keys(directories)) {
344 videoUUID: string, 56 const directoryPath = server.servers.buildDirectory(directory)
345 serverNumber: number,
346 directories = [
347 'redundancy',
348 'videos',
349 'thumbnails',
350 'torrents',
351 'previews',
352 'captions',
353 join('playlists', 'hls'),
354 join('redundancy', 'hls')
355 ]
356) {
357 for (const directory of directories) {
358 const directoryPath = buildServerDirectory({ internalServerNumber: serverNumber }, directory)
359 57
360 const directoryExists = await pathExists(directoryPath) 58 const directoryExists = await pathExists(directoryPath)
361 if (directoryExists === false) continue 59 if (directoryExists === false) continue
362 60
363 const files = await readdir(directoryPath) 61 const existingFiles = await readdir(directoryPath)
364 for (const file of files) { 62 for (const existingFile of existingFiles) {
365 expect(file, `File ${file} should not exist in ${directoryPath}`).to.not.contain(videoUUID) 63 for (const shouldNotExist of directories[directory]) {
64 expect(existingFile, `File ${existingFile} should not exist in ${directoryPath}`).to.not.contain(shouldNotExist)
65 }
366 } 66 }
367 } 67 }
368} 68}
369 69
370async function uploadVideo ( 70async function saveVideoInServers (servers: PeerTubeServer[], uuid: string) {
371 url: string, 71 for (const server of servers) {
372 accessToken: string, 72 server.store.videoDetails = await server.videos.get({ id: uuid })
373 videoAttributesArg: VideoAttributes,
374 specialStatus = HttpStatusCode.OK_200,
375 mode: 'legacy' | 'resumable' = 'legacy'
376) {
377 let defaultChannelId = '1'
378
379 try {
380 const res = await getMyUserInformation(url, accessToken)
381 defaultChannelId = res.body.videoChannels[0].id
382 } catch (e) { /* empty */ }
383
384 // Override default attributes
385 const attributes = Object.assign({
386 name: 'my super video',
387 category: 5,
388 licence: 4,
389 language: 'zh',
390 channelId: defaultChannelId,
391 nsfw: true,
392 waitTranscoding: false,
393 description: 'my super description',
394 support: 'my super support text',
395 tags: [ 'tag' ],
396 privacy: VideoPrivacy.PUBLIC,
397 commentsEnabled: true,
398 downloadEnabled: true,
399 fixture: 'video_short.webm'
400 }, videoAttributesArg)
401
402 const res = mode === 'legacy'
403 ? await buildLegacyUpload(url, accessToken, attributes, specialStatus)
404 : await buildResumeUpload(url, accessToken, attributes, specialStatus)
405
406 // Wait torrent generation
407 if (specialStatus === HttpStatusCode.OK_200) {
408 let video: VideoDetails
409 do {
410 const resVideo = await getVideoWithToken(url, accessToken, res.body.video.uuid)
411 video = resVideo.body
412
413 await wait(50)
414 } while (!video.files[0].torrentUrl)
415 } 73 }
416
417 return res
418} 74}
419 75
420function checkUploadVideoParam ( 76function checkUploadVideoParam (
421 url: string, 77 server: PeerTubeServer,
422 token: string, 78 token: string,
423 attributes: Partial<VideoAttributes>, 79 attributes: Partial<VideoEdit>,
424 specialStatus = HttpStatusCode.OK_200, 80 expectedStatus = HttpStatusCode.OK_200,
425 mode: 'legacy' | 'resumable' = 'legacy' 81 mode: 'legacy' | 'resumable' = 'legacy'
426) { 82) {
427 return mode === 'legacy' 83 return mode === 'legacy'
428 ? buildLegacyUpload(url, token, attributes, specialStatus) 84 ? server.videos.buildLegacyUpload({ token, attributes, expectedStatus })
429 : buildResumeUpload(url, token, attributes, specialStatus) 85 : server.videos.buildResumeUpload({ token, attributes, expectedStatus })
430}
431
432async function buildLegacyUpload (url: string, token: string, attributes: VideoAttributes, specialStatus = HttpStatusCode.OK_200) {
433 const path = '/api/v1/videos/upload'
434 const req = request(url)
435 .post(path)
436 .set('Accept', 'application/json')
437 .set('Authorization', 'Bearer ' + token)
438
439 buildUploadReq(req, attributes)
440
441 if (attributes.fixture !== undefined) {
442 req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
443 }
444
445 return req.expect(specialStatus)
446}
447
448async function buildResumeUpload (url: string, token: string, attributes: VideoAttributes, specialStatus = HttpStatusCode.OK_200) {
449 let size = 0
450 let videoFilePath: string
451 let mimetype = 'video/mp4'
452
453 if (attributes.fixture) {
454 videoFilePath = buildAbsoluteFixturePath(attributes.fixture)
455 size = (await stat(videoFilePath)).size
456
457 if (videoFilePath.endsWith('.mkv')) {
458 mimetype = 'video/x-matroska'
459 } else if (videoFilePath.endsWith('.webm')) {
460 mimetype = 'video/webm'
461 }
462 }
463
464 const initializeSessionRes = await prepareResumableUpload({ url, token, attributes, size, mimetype })
465 const initStatus = initializeSessionRes.status
466
467 if (videoFilePath && initStatus === HttpStatusCode.CREATED_201) {
468 const locationHeader = initializeSessionRes.header['location']
469 expect(locationHeader).to.not.be.undefined
470
471 const pathUploadId = locationHeader.split('?')[1]
472
473 return sendResumableChunks({ url, token, pathUploadId, videoFilePath, size, specialStatus })
474 }
475
476 const expectedInitStatus = specialStatus === HttpStatusCode.OK_200
477 ? HttpStatusCode.CREATED_201
478 : specialStatus
479
480 expect(initStatus).to.equal(expectedInitStatus)
481
482 return initializeSessionRes
483}
484
485async function prepareResumableUpload (options: {
486 url: string
487 token: string
488 attributes: VideoAttributes
489 size: number
490 mimetype: string
491}) {
492 const { url, token, attributes, size, mimetype } = options
493
494 const path = '/api/v1/videos/upload-resumable'
495
496 const req = request(url)
497 .post(path)
498 .set('Authorization', 'Bearer ' + token)
499 .set('X-Upload-Content-Type', mimetype)
500 .set('X-Upload-Content-Length', size.toString())
501
502 buildUploadReq(req, attributes)
503
504 if (attributes.fixture) {
505 req.field('filename', attributes.fixture)
506 }
507
508 return req
509}
510
511function sendResumableChunks (options: {
512 url: string
513 token: string
514 pathUploadId: string
515 videoFilePath: string
516 size: number
517 specialStatus?: HttpStatusCode
518 contentLength?: number
519 contentRangeBuilder?: (start: number, chunk: any) => string
520}) {
521 const { url, token, pathUploadId, videoFilePath, size, specialStatus, contentLength, contentRangeBuilder } = options
522
523 const expectedStatus = specialStatus || HttpStatusCode.OK_200
524
525 const path = '/api/v1/videos/upload-resumable'
526 let start = 0
527
528 const readable = createReadStream(videoFilePath, { highWaterMark: 8 * 1024 })
529 return new Promise<GotResponse>((resolve, reject) => {
530 readable.on('data', async function onData (chunk) {
531 readable.pause()
532
533 const headers = {
534 'Authorization': 'Bearer ' + token,
535 'Content-Type': 'application/octet-stream',
536 'Content-Range': contentRangeBuilder
537 ? contentRangeBuilder(start, chunk)
538 : `bytes ${start}-${start + chunk.length - 1}/${size}`,
539 'Content-Length': contentLength ? contentLength + '' : chunk.length + ''
540 }
541
542 const res = await got({
543 url,
544 method: 'put',
545 headers,
546 path: path + '?' + pathUploadId,
547 body: chunk,
548 responseType: 'json',
549 throwHttpErrors: false
550 })
551
552 start += chunk.length
553
554 if (res.statusCode === expectedStatus) {
555 return resolve(res)
556 }
557
558 if (res.statusCode !== HttpStatusCode.PERMANENT_REDIRECT_308) {
559 readable.off('data', onData)
560 return reject(new Error('Incorrect transient behaviour sending intermediary chunks'))
561 }
562
563 readable.resume()
564 })
565 })
566}
567
568function updateVideo (
569 url: string,
570 accessToken: string,
571 id: number | string,
572 attributes: VideoAttributes,
573 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
574) {
575 const path = '/api/v1/videos/' + id
576 const body = {}
577
578 if (attributes.name) body['name'] = attributes.name
579 if (attributes.category) body['category'] = attributes.category
580 if (attributes.licence) body['licence'] = attributes.licence
581 if (attributes.language) body['language'] = attributes.language
582 if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
583 if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
584 if (attributes.downloadEnabled !== undefined) body['downloadEnabled'] = JSON.stringify(attributes.downloadEnabled)
585 if (attributes.originallyPublishedAt !== undefined) body['originallyPublishedAt'] = attributes.originallyPublishedAt
586 if (attributes.description) body['description'] = attributes.description
587 if (attributes.tags) body['tags'] = attributes.tags
588 if (attributes.privacy) body['privacy'] = attributes.privacy
589 if (attributes.channelId) body['channelId'] = attributes.channelId
590 if (attributes.scheduleUpdate) body['scheduleUpdate'] = attributes.scheduleUpdate
591
592 // Upload request
593 if (attributes.thumbnailfile || attributes.previewfile) {
594 const attaches: any = {}
595 if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile
596 if (attributes.previewfile) attaches.previewfile = attributes.previewfile
597
598 return makeUploadRequest({
599 url,
600 method: 'PUT',
601 path,
602 token: accessToken,
603 fields: body,
604 attaches,
605 statusCodeExpected
606 })
607 }
608
609 return makePutBodyRequest({
610 url,
611 path,
612 fields: body,
613 token: accessToken,
614 statusCodeExpected
615 })
616}
617
618function rateVideo (url: string, accessToken: string, id: number | string, rating: string, specialStatus = HttpStatusCode.NO_CONTENT_204) {
619 const path = '/api/v1/videos/' + id + '/rate'
620
621 return request(url)
622 .put(path)
623 .set('Accept', 'application/json')
624 .set('Authorization', 'Bearer ' + accessToken)
625 .send({ rating })
626 .expect(specialStatus)
627}
628
629function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
630 return new Promise<any>((res, rej) => {
631 const torrentName = videoUUID + '-' + resolution + '.torrent'
632 const torrentPath = buildServerDirectory(server, join('torrents', torrentName))
633
634 readFile(torrentPath, (err, data) => {
635 if (err) return rej(err)
636
637 return res(parseTorrent(data))
638 })
639 })
640} 86}
641 87
642async function completeVideoCheck ( 88async function completeVideoCheck (
643 url: string, 89 server: PeerTubeServer,
644 video: any, 90 video: any,
645 attributes: { 91 attributes: {
646 name: string 92 name: string
@@ -682,7 +128,7 @@ async function completeVideoCheck (
682 if (!attributes.likes) attributes.likes = 0 128 if (!attributes.likes) attributes.likes = 0
683 if (!attributes.dislikes) attributes.dislikes = 0 129 if (!attributes.dislikes) attributes.dislikes = 0
684 130
685 const host = new URL(url).host 131 const host = new URL(server.url).host
686 const originHost = attributes.account.host 132 const originHost = attributes.account.host
687 133
688 expect(video.name).to.equal(attributes.name) 134 expect(video.name).to.equal(attributes.name)
@@ -719,8 +165,7 @@ async function completeVideoCheck (
719 expect(video.originallyPublishedAt).to.be.null 165 expect(video.originallyPublishedAt).to.be.null
720 } 166 }
721 167
722 const res = await getVideo(url, video.uuid) 168 const videoDetails = await server.videos.get({ id: video.uuid })
723 const videoDetails: VideoDetails = res.body
724 169
725 expect(videoDetails.files).to.have.lengthOf(attributes.files.length) 170 expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
726 expect(videoDetails.tags).to.deep.equal(attributes.tags) 171 expect(videoDetails.tags).to.deep.equal(attributes.tags)
@@ -745,18 +190,16 @@ async function completeVideoCheck (
745 190
746 expect(file.magnetUri).to.have.lengthOf.above(2) 191 expect(file.magnetUri).to.have.lengthOf.above(2)
747 192
748 expect(file.torrentDownloadUrl).to.equal(`http://${host}/download/torrents/${videoDetails.uuid}-${file.resolution.id}.torrent`) 193 expect(file.torrentDownloadUrl).to.match(new RegExp(`http://${host}/download/torrents/${uuidRegex}-${file.resolution.id}.torrent`))
749 expect(file.torrentUrl).to.equal(`http://${host}/lazy-static/torrents/${videoDetails.uuid}-${file.resolution.id}.torrent`) 194 expect(file.torrentUrl).to.match(new RegExp(`http://${host}/lazy-static/torrents/${uuidRegex}-${file.resolution.id}.torrent`))
750 195
751 expect(file.fileUrl).to.equal(`http://${originHost}/static/webseed/${videoDetails.uuid}-${file.resolution.id}${extension}`) 196 expect(file.fileUrl).to.match(new RegExp(`http://${originHost}/static/webseed/${uuidRegex}-${file.resolution.id}${extension}`))
752 expect(file.fileDownloadUrl).to.equal(`http://${originHost}/download/videos/${videoDetails.uuid}-${file.resolution.id}${extension}`) 197 expect(file.fileDownloadUrl).to.match(new RegExp(`http://${originHost}/download/videos/${uuidRegex}-${file.resolution.id}${extension}`))
753 198
754 await Promise.all([ 199 await Promise.all([
755 makeRawRequest(file.torrentUrl, 200), 200 makeRawRequest(file.torrentUrl, 200),
756 makeRawRequest(file.torrentDownloadUrl, 200), 201 makeRawRequest(file.torrentDownloadUrl, 200),
757 makeRawRequest(file.metadataUrl, 200), 202 makeRawRequest(file.metadataUrl, 200)
758 // Backward compatibility
759 makeRawRequest(`http://${originHost}/static/torrents/${videoDetails.uuid}-${file.resolution.id}.torrent`, 200)
760 ]) 203 ])
761 204
762 expect(file.resolution.id).to.equal(attributeFile.resolution) 205 expect(file.resolution.id).to.equal(attributeFile.resolution)
@@ -776,149 +219,34 @@ async function completeVideoCheck (
776 } 219 }
777 220
778 expect(videoDetails.thumbnailPath).to.exist 221 expect(videoDetails.thumbnailPath).to.exist
779 await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath) 222 await testImage(server.url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
780 223
781 if (attributes.previewfile) { 224 if (attributes.previewfile) {
782 expect(videoDetails.previewPath).to.exist 225 expect(videoDetails.previewPath).to.exist
783 await testImage(url, attributes.previewfile, videoDetails.previewPath) 226 await testImage(server.url, attributes.previewfile, videoDetails.previewPath)
784 } 227 }
785} 228}
786 229
787async function videoUUIDToId (url: string, id: number | string) {
788 if (validator.isUUID('' + id) === false) return id
789
790 const res = await getVideo(url, id)
791 return res.body.id
792}
793
794async function uploadVideoAndGetId (options: {
795 server: ServerInfo
796 videoName: string
797 nsfw?: boolean
798 privacy?: VideoPrivacy
799 token?: string
800 fixture?: string
801}) {
802 const videoAttrs: any = { name: options.videoName }
803 if (options.nsfw) videoAttrs.nsfw = options.nsfw
804 if (options.privacy) videoAttrs.privacy = options.privacy
805 if (options.fixture) videoAttrs.fixture = options.fixture
806
807 const res = await uploadVideo(options.server.url, options.token || options.server.accessToken, videoAttrs)
808
809 return res.body.video as { id: number, uuid: string, shortUUID: string }
810}
811
812async function getLocalIdByUUID (url: string, uuid: string) {
813 const res = await getVideo(url, uuid)
814
815 return res.body.id
816}
817
818// serverNumber starts from 1 230// serverNumber starts from 1
819async function uploadRandomVideoOnServers (servers: ServerInfo[], serverNumber: number, additionalParams: any = {}) { 231async function uploadRandomVideoOnServers (
232 servers: PeerTubeServer[],
233 serverNumber: number,
234 additionalParams?: VideoEdit & { prefixName?: string }
235) {
820 const server = servers.find(s => s.serverNumber === serverNumber) 236 const server = servers.find(s => s.serverNumber === serverNumber)
821 const res = await uploadRandomVideo(server, false, additionalParams) 237 const res = await server.videos.randomUpload({ wait: false, additionalParams })
822 238
823 await waitJobs(servers) 239 await waitJobs(servers)
824 240
825 return res 241 return res
826} 242}
827 243
828async function uploadRandomVideo (server: ServerInfo, wait = true, additionalParams: any = {}) {
829 const prefixName = additionalParams.prefixName || ''
830 const name = prefixName + buildUUID()
831
832 const data = Object.assign({ name }, additionalParams)
833 const res = await uploadVideo(server.url, server.accessToken, data)
834
835 if (wait) await waitJobs([ server ])
836
837 return { uuid: res.body.video.uuid, name }
838}
839
840// --------------------------------------------------------------------------- 244// ---------------------------------------------------------------------------
841 245
842export { 246export {
843 getVideoDescription,
844 getVideoCategories,
845 uploadRandomVideo,
846 getVideoLicences,
847 videoUUIDToId,
848 getVideoPrivacies,
849 getVideoLanguages,
850 getMyVideos,
851 getAccountVideos,
852 getVideoChannelVideos,
853 getVideo,
854 getVideoFileMetadataUrl,
855 getVideoWithToken,
856 getVideosList,
857 removeAllVideos,
858 checkUploadVideoParam, 247 checkUploadVideoParam,
859 getVideosListPagination,
860 getVideosListSort,
861 removeVideo,
862 getVideosListWithToken,
863 uploadVideo,
864 sendResumableChunks,
865 getVideosWithFilters,
866 uploadRandomVideoOnServers,
867 updateVideo,
868 rateVideo,
869 viewVideo,
870 parseTorrentVideo,
871 getLocalVideos,
872 completeVideoCheck, 248 completeVideoCheck,
249 uploadRandomVideoOnServers,
873 checkVideoFilesWereRemoved, 250 checkVideoFilesWereRemoved,
874 getPlaylistVideos, 251 saveVideoInServers
875 getMyVideosWithFilter,
876 uploadVideoAndGetId,
877 getLocalIdByUUID,
878 getVideoIdFromUUID,
879 prepareResumableUpload
880}
881
882// ---------------------------------------------------------------------------
883
884function buildUploadReq (req: request.Test, attributes: VideoAttributes) {
885
886 for (const key of [ 'name', 'support', 'channelId', 'description', 'originallyPublishedAt' ]) {
887 if (attributes[key] !== undefined) {
888 req.field(key, attributes[key])
889 }
890 }
891
892 for (const key of [ 'nsfw', 'commentsEnabled', 'downloadEnabled', 'waitTranscoding' ]) {
893 if (attributes[key] !== undefined) {
894 req.field(key, JSON.stringify(attributes[key]))
895 }
896 }
897
898 for (const key of [ 'language', 'privacy', 'category', 'licence' ]) {
899 if (attributes[key] !== undefined) {
900 req.field(key, attributes[key].toString())
901 }
902 }
903
904 const tags = attributes.tags || []
905 for (let i = 0; i < tags.length; i++) {
906 req.field('tags[' + i + ']', attributes.tags[i])
907 }
908
909 for (const key of [ 'thumbnailfile', 'previewfile' ]) {
910 if (attributes[key] !== undefined) {
911 req.attach(key, buildAbsoluteFixturePath(attributes[key]))
912 }
913 }
914
915 if (attributes.scheduleUpdate) {
916 if (attributes.scheduleUpdate.updateAt) {
917 req.field('scheduleUpdate[updateAt]', attributes.scheduleUpdate.updateAt)
918 }
919
920 if (attributes.scheduleUpdate.privacy) {
921 req.field('scheduleUpdate[privacy]', attributes.scheduleUpdate.privacy)
922 }
923 }
924} 252}
diff --git a/shared/core-utils/miscs/http-error-codes.ts b/shared/models/http/http-error-codes.ts
index b2fbdfc5a..b2fbdfc5a 100644
--- a/shared/core-utils/miscs/http-error-codes.ts
+++ b/shared/models/http/http-error-codes.ts
diff --git a/shared/core-utils/miscs/http-methods.ts b/shared/models/http/http-methods.ts
index 1cfa458b9..1cfa458b9 100644
--- a/shared/core-utils/miscs/http-methods.ts
+++ b/shared/models/http/http-methods.ts
diff --git a/shared/models/http/index.ts b/shared/models/http/index.ts
new file mode 100644
index 000000000..ec991afe0
--- /dev/null
+++ b/shared/models/http/index.ts
@@ -0,0 +1,2 @@
1export * from './http-error-codes'
2export * from './http-methods'
diff --git a/shared/models/index.ts b/shared/models/index.ts
index 5c2bc480e..78723d830 100644
--- a/shared/models/index.ts
+++ b/shared/models/index.ts
@@ -4,6 +4,7 @@ export * from './bulk'
4export * from './common' 4export * from './common'
5export * from './custom-markup' 5export * from './custom-markup'
6export * from './feeds' 6export * from './feeds'
7export * from './http'
7export * from './joinpeertube' 8export * from './joinpeertube'
8export * from './moderation' 9export * from './moderation'
9export * from './overviews' 10export * from './overviews'
diff --git a/shared/models/plugins/server/managers/plugin-playlist-privacy-manager.model.ts b/shared/models/plugins/server/managers/plugin-playlist-privacy-manager.model.ts
index 4703c0a8b..35247c1e3 100644
--- a/shared/models/plugins/server/managers/plugin-playlist-privacy-manager.model.ts
+++ b/shared/models/plugins/server/managers/plugin-playlist-privacy-manager.model.ts
@@ -1,8 +1,12 @@
1import { VideoPlaylistPrivacy } from '../../../videos/playlist/video-playlist-privacy.model' 1import { VideoPlaylistPrivacy } from '../../../videos/playlist/video-playlist-privacy.model'
2import { ConstantManager } from '../plugin-constant-manager.model'
2 3
3export interface PluginPlaylistPrivacyManager { 4export interface PluginPlaylistPrivacyManager extends ConstantManager<VideoPlaylistPrivacy> {
4 // PUBLIC = 1, 5 /**
5 // UNLISTED = 2, 6 * PUBLIC = 1,
6 // PRIVATE = 3 7 * UNLISTED = 2,
8 * PRIVATE = 3
9 * @deprecated use `deleteConstant` instead
10 */
7 deletePlaylistPrivacy: (privacyKey: VideoPlaylistPrivacy) => boolean 11 deletePlaylistPrivacy: (privacyKey: VideoPlaylistPrivacy) => boolean
8} 12}
diff --git a/shared/models/plugins/server/managers/plugin-video-category-manager.model.ts b/shared/models/plugins/server/managers/plugin-video-category-manager.model.ts
index 201bfa979..cf3d828fe 100644
--- a/shared/models/plugins/server/managers/plugin-video-category-manager.model.ts
+++ b/shared/models/plugins/server/managers/plugin-video-category-manager.model.ts
@@ -1,5 +1,13 @@
1export interface PluginVideoCategoryManager { 1import { ConstantManager } from '../plugin-constant-manager.model'
2
3export interface PluginVideoCategoryManager extends ConstantManager<number> {
4 /**
5 * @deprecated use `addConstant` instead
6 */
2 addCategory: (categoryKey: number, categoryLabel: string) => boolean 7 addCategory: (categoryKey: number, categoryLabel: string) => boolean
3 8
9 /**
10 * @deprecated use `deleteConstant` instead
11 */
4 deleteCategory: (categoryKey: number) => boolean 12 deleteCategory: (categoryKey: number) => boolean
5} 13}
diff --git a/shared/models/plugins/server/managers/plugin-video-language-manager.model.ts b/shared/models/plugins/server/managers/plugin-video-language-manager.model.ts
index 3fd577a79..69fc8e503 100644
--- a/shared/models/plugins/server/managers/plugin-video-language-manager.model.ts
+++ b/shared/models/plugins/server/managers/plugin-video-language-manager.model.ts
@@ -1,5 +1,13 @@
1export interface PluginVideoLanguageManager { 1import { ConstantManager } from '../plugin-constant-manager.model'
2
3export interface PluginVideoLanguageManager extends ConstantManager<string> {
4 /**
5 * @deprecated use `addConstant` instead
6 */
2 addLanguage: (languageKey: string, languageLabel: string) => boolean 7 addLanguage: (languageKey: string, languageLabel: string) => boolean
3 8
9 /**
10 * @deprecated use `deleteConstant` instead
11 */
4 deleteLanguage: (languageKey: string) => boolean 12 deleteLanguage: (languageKey: string) => boolean
5} 13}
diff --git a/shared/models/plugins/server/managers/plugin-video-licence-manager.model.ts b/shared/models/plugins/server/managers/plugin-video-licence-manager.model.ts
index 82a634d3a..6efeadd7d 100644
--- a/shared/models/plugins/server/managers/plugin-video-licence-manager.model.ts
+++ b/shared/models/plugins/server/managers/plugin-video-licence-manager.model.ts
@@ -1,5 +1,13 @@
1export interface PluginVideoLicenceManager { 1import { ConstantManager } from '../plugin-constant-manager.model'
2
3export interface PluginVideoLicenceManager extends ConstantManager<number> {
4 /**
5 * @deprecated use `addLicence` instead
6 */
2 addLicence: (licenceKey: number, licenceLabel: string) => boolean 7 addLicence: (licenceKey: number, licenceLabel: string) => boolean
3 8
9 /**
10 * @deprecated use `deleteLicence` instead
11 */
4 deleteLicence: (licenceKey: number) => boolean 12 deleteLicence: (licenceKey: number) => boolean
5} 13}
diff --git a/shared/models/plugins/server/managers/plugin-video-privacy-manager.model.ts b/shared/models/plugins/server/managers/plugin-video-privacy-manager.model.ts
index 7717115e3..a237037db 100644
--- a/shared/models/plugins/server/managers/plugin-video-privacy-manager.model.ts
+++ b/shared/models/plugins/server/managers/plugin-video-privacy-manager.model.ts
@@ -1,9 +1,13 @@
1import { VideoPrivacy } from '../../../videos/video-privacy.enum' 1import { VideoPrivacy } from '../../../videos/video-privacy.enum'
2import { ConstantManager } from '../plugin-constant-manager.model'
2 3
3export interface PluginVideoPrivacyManager { 4export interface PluginVideoPrivacyManager extends ConstantManager<VideoPrivacy> {
4 // PUBLIC = 1 5 /**
5 // UNLISTED = 2 6 * PUBLIC = 1,
6 // PRIVATE = 3 7 * UNLISTED = 2,
7 // INTERNAL = 4 8 * PRIVATE = 3
9 * INTERNAL = 4
10 * @deprecated use `deleteConstant` instead
11 */
8 deletePrivacy: (privacyKey: VideoPrivacy) => boolean 12 deletePrivacy: (privacyKey: VideoPrivacy) => boolean
9} 13}
diff --git a/shared/models/plugins/server/plugin-constant-manager.model.ts b/shared/models/plugins/server/plugin-constant-manager.model.ts
new file mode 100644
index 000000000..4de3ce38f
--- /dev/null
+++ b/shared/models/plugins/server/plugin-constant-manager.model.ts
@@ -0,0 +1,7 @@
1export interface ConstantManager <K extends string | number> {
2 addConstant: (key: K, label: string) => boolean
3 deleteConstant: (key: K) => boolean
4 getConstantValue: (key: K) => string
5 getConstants: () => Record<K, string>
6 resetConstants: () => void
7}
diff --git a/shared/models/plugins/server/server-hook.model.ts b/shared/models/plugins/server/server-hook.model.ts
index 5f29534b5..562c6eb12 100644
--- a/shared/models/plugins/server/server-hook.model.ts
+++ b/shared/models/plugins/server/server-hook.model.ts
@@ -18,6 +18,10 @@ export const serverFilterHookObject = {
18 'filter:api.user.me.videos.list.params': true, 18 'filter:api.user.me.videos.list.params': true,
19 'filter:api.user.me.videos.list.result': true, 19 'filter:api.user.me.videos.list.result': true,
20 20
21 // Filter params/result used to list overview videos for the REST API
22 'filter:api.overviews.videos.list.params': true,
23 'filter:api.overviews.videos.list.result': true,
24
21 // Filter params/results to search videos/channels in the DB or on the remote index 25 // Filter params/results to search videos/channels in the DB or on the remote index
22 'filter:api.search.videos.local.list.params': true, 26 'filter:api.search.videos.local.list.params': true,
23 'filter:api.search.videos.local.list.result': true, 27 'filter:api.search.videos.local.list.result': true,
diff --git a/shared/models/search/video-channels-search-query.model.ts b/shared/models/search/video-channels-search-query.model.ts
index 8f93c4bd5..b68a1e80b 100644
--- a/shared/models/search/video-channels-search-query.model.ts
+++ b/shared/models/search/video-channels-search-query.model.ts
@@ -1,9 +1,18 @@
1import { SearchTargetQuery } from './search-target-query.model' 1import { SearchTargetQuery } from './search-target-query.model'
2 2
3export interface VideoChannelsSearchQuery extends SearchTargetQuery { 3export interface VideoChannelsSearchQuery extends SearchTargetQuery {
4 search: string 4 search?: string
5 5
6 start?: number 6 start?: number
7 count?: number 7 count?: number
8 sort?: string 8 sort?: string
9
10 host?: string
11 handles?: string[]
12}
13
14export interface VideoChannelsSearchQueryAfterSanitize extends VideoChannelsSearchQuery {
15 start: number
16 count: number
17 sort: string
9} 18}
diff --git a/shared/models/search/video-playlists-search-query.model.ts b/shared/models/search/video-playlists-search-query.model.ts
index 31f05218e..d9027eb5b 100644
--- a/shared/models/search/video-playlists-search-query.model.ts
+++ b/shared/models/search/video-playlists-search-query.model.ts
@@ -1,9 +1,20 @@
1import { SearchTargetQuery } from './search-target-query.model' 1import { SearchTargetQuery } from './search-target-query.model'
2 2
3export interface VideoPlaylistsSearchQuery extends SearchTargetQuery { 3export interface VideoPlaylistsSearchQuery extends SearchTargetQuery {
4 search: string 4 search?: string
5 5
6 start?: number 6 start?: number
7 count?: number 7 count?: number
8 sort?: string 8 sort?: string
9
10 host?: string
11
12 // UUIDs or short UUIDs
13 uuids?: string[]
14}
15
16export interface VideoPlaylistsSearchQueryAfterSanitize extends VideoPlaylistsSearchQuery {
17 start: number
18 count: number
19 sort: string
9} 20}
diff --git a/shared/models/search/videos-common-query.model.ts b/shared/models/search/videos-common-query.model.ts
index bd02489ea..2f2e9a934 100644
--- a/shared/models/search/videos-common-query.model.ts
+++ b/shared/models/search/videos-common-query.model.ts
@@ -21,6 +21,14 @@ export interface VideosCommonQuery {
21 tagsAllOf?: string[] 21 tagsAllOf?: string[]
22 22
23 filter?: VideoFilter 23 filter?: VideoFilter
24
25 skipCount?: boolean
26}
27
28export interface VideosCommonQueryAfterSanitize extends VideosCommonQuery {
29 start: number
30 count: number
31 sort: string
24} 32}
25 33
26export interface VideosWithSearchCommonQuery extends VideosCommonQuery { 34export interface VideosWithSearchCommonQuery extends VideosCommonQuery {
diff --git a/shared/models/search/videos-search-query.model.ts b/shared/models/search/videos-search-query.model.ts
index 406f6cab2..a5436879d 100644
--- a/shared/models/search/videos-search-query.model.ts
+++ b/shared/models/search/videos-search-query.model.ts
@@ -4,6 +4,8 @@ import { VideosCommonQuery } from './videos-common-query.model'
4export interface VideosSearchQuery extends SearchTargetQuery, VideosCommonQuery { 4export interface VideosSearchQuery extends SearchTargetQuery, VideosCommonQuery {
5 search?: string 5 search?: string
6 6
7 host?: string
8
7 startDate?: string // ISO 8601 9 startDate?: string // ISO 8601
8 endDate?: string // ISO 8601 10 endDate?: string // ISO 8601
9 11
@@ -12,4 +14,13 @@ export interface VideosSearchQuery extends SearchTargetQuery, VideosCommonQuery
12 14
13 durationMin?: number // seconds 15 durationMin?: number // seconds
14 durationMax?: number // seconds 16 durationMax?: number // seconds
17
18 // UUIDs or short UUIDs
19 uuids?: string[]
20}
21
22export interface VideosSearchQueryAfterSanitize extends VideosSearchQuery {
23 start: number
24 count: number
25 sort: string
15} 26}
diff --git a/shared/models/server/debug.model.ts b/shared/models/server/debug.model.ts
index 7ceff9137..2ecabdeca 100644
--- a/shared/models/server/debug.model.ts
+++ b/shared/models/server/debug.model.ts
@@ -1,5 +1,6 @@
1export interface Debug { 1export interface Debug {
2 ip: string 2 ip: string
3 activityPubMessagesWaiting: number
3} 4}
4 5
5export interface SendDebugCommand { 6export interface SendDebugCommand {
diff --git a/shared/models/server/index.ts b/shared/models/server/index.ts
index 06bf5c599..0f7646c7a 100644
--- a/shared/models/server/index.ts
+++ b/shared/models/server/index.ts
@@ -10,4 +10,5 @@ export * from './peertube-problem-document.model'
10export * from './server-config.model' 10export * from './server-config.model'
11export * from './server-debug.model' 11export * from './server-debug.model'
12export * from './server-error-code.enum' 12export * from './server-error-code.enum'
13export * from './server-follow-create.model'
13export * from './server-stats.model' 14export * from './server-stats.model'
diff --git a/shared/models/server/peertube-problem-document.model.ts b/shared/models/server/peertube-problem-document.model.ts
index e391d5aad..8dd96f7a3 100644
--- a/shared/models/server/peertube-problem-document.model.ts
+++ b/shared/models/server/peertube-problem-document.model.ts
@@ -1,4 +1,4 @@
1import { HttpStatusCode } from '../../core-utils' 1import { HttpStatusCode } from '../../models'
2import { OAuth2ErrorCode, ServerErrorCode } from './server-error-code.enum' 2import { OAuth2ErrorCode, ServerErrorCode } from './server-error-code.enum'
3 3
4export interface PeerTubeProblemDocumentData { 4export interface PeerTubeProblemDocumentData {
diff --git a/shared/models/server/server-follow-create.model.ts b/shared/models/server/server-follow-create.model.ts
new file mode 100644
index 000000000..3f90c7d6f
--- /dev/null
+++ b/shared/models/server/server-follow-create.model.ts
@@ -0,0 +1,4 @@
1export interface ServerFollowCreate {
2 hosts?: string[]
3 handles?: string[]
4}
diff --git a/shared/models/users/index.ts b/shared/models/users/index.ts
index a9d578054..b61a8cd40 100644
--- a/shared/models/users/index.ts
+++ b/shared/models/users/index.ts
@@ -1,3 +1,4 @@
1export * from './user-create-result.model'
1export * from './user-create.model' 2export * from './user-create.model'
2export * from './user-flag.model' 3export * from './user-flag.model'
3export * from './user-login.model' 4export * from './user-login.model'
diff --git a/shared/models/users/user-create-result.model.ts b/shared/models/users/user-create-result.model.ts
new file mode 100644
index 000000000..835b241ed
--- /dev/null
+++ b/shared/models/users/user-create-result.model.ts
@@ -0,0 +1,7 @@
1export interface UserCreateResult {
2 id: number
3
4 account: {
5 id: number
6 }
7}
diff --git a/shared/models/users/user-notification.model.ts b/shared/models/users/user-notification.model.ts
index 8b33e3fbd..5820589fe 100644
--- a/shared/models/users/user-notification.model.ts
+++ b/shared/models/users/user-notification.model.ts
@@ -36,6 +36,7 @@ export const enum UserNotificationType {
36export interface VideoInfo { 36export interface VideoInfo {
37 id: number 37 id: number
38 uuid: string 38 uuid: string
39 shortUUID: string
39 name: string 40 name: string
40} 41}
41 42
@@ -82,11 +83,7 @@ export interface UserNotification {
82 comment?: { 83 comment?: {
83 threadId: number 84 threadId: number
84 85
85 video: { 86 video: VideoInfo
86 id: number
87 uuid: string
88 name: string
89 }
90 } 87 }
91 88
92 account?: ActorInfo 89 account?: ActorInfo
diff --git a/shared/models/videos/channel/index.ts b/shared/models/videos/channel/index.ts
index 9dbaa42da..6cdabffbd 100644
--- a/shared/models/videos/channel/index.ts
+++ b/shared/models/videos/channel/index.ts
@@ -1,3 +1,4 @@
1export * from './video-channel-create-result.model'
1export * from './video-channel-create.model' 2export * from './video-channel-create.model'
2export * from './video-channel-update.model' 3export * from './video-channel-update.model'
3export * from './video-channel.model' 4export * from './video-channel.model'
diff --git a/shared/models/videos/channel/video-channel-create-result.model.ts b/shared/models/videos/channel/video-channel-create-result.model.ts
new file mode 100644
index 000000000..e3d7aeb4c
--- /dev/null
+++ b/shared/models/videos/channel/video-channel-create-result.model.ts
@@ -0,0 +1,3 @@
1export interface VideoChannelCreateResult {
2 id: number
3}
diff --git a/shared/models/videos/comment/index.ts b/shared/models/videos/comment/index.ts
index 7b9261a36..80c6c0724 100644
--- a/shared/models/videos/comment/index.ts
+++ b/shared/models/videos/comment/index.ts
@@ -1 +1,2 @@
1export * from './video-comment-create.model'
1export * from './video-comment.model' 2export * from './video-comment.model'
diff --git a/shared/models/videos/comment/video-comment-create.model.ts b/shared/models/videos/comment/video-comment-create.model.ts
new file mode 100644
index 000000000..1f0135405
--- /dev/null
+++ b/shared/models/videos/comment/video-comment-create.model.ts
@@ -0,0 +1,3 @@
1export interface VideoCommentCreate {
2 text: string
3}
diff --git a/shared/models/videos/comment/video-comment.model.ts b/shared/models/videos/comment/video-comment.model.ts
index 79c0e4c0a..737cfe098 100644
--- a/shared/models/videos/comment/video-comment.model.ts
+++ b/shared/models/videos/comment/video-comment.model.ts
@@ -1,3 +1,4 @@
1import { ResultList } from '../../common'
1import { Account } from '../../actors' 2import { Account } from '../../actors'
2 3
3export interface VideoComment { 4export interface VideoComment {
@@ -36,11 +37,9 @@ export interface VideoCommentAdmin {
36 } 37 }
37} 38}
38 39
40export type VideoCommentThreads = ResultList<VideoComment> & { totalNotDeletedComments: number }
41
39export interface VideoCommentThreadTree { 42export interface VideoCommentThreadTree {
40 comment: VideoComment 43 comment: VideoComment
41 children: VideoCommentThreadTree[] 44 children: VideoCommentThreadTree[]
42} 45}
43
44export interface VideoCommentCreate {
45 text: string
46}
diff --git a/shared/models/videos/playlist/index.ts b/shared/models/videos/playlist/index.ts
index f11a4bd28..a9e8ce496 100644
--- a/shared/models/videos/playlist/index.ts
+++ b/shared/models/videos/playlist/index.ts
@@ -1,6 +1,7 @@
1export * from './video-exist-in-playlist.model' 1export * from './video-exist-in-playlist.model'
2export * from './video-playlist-create-result.model' 2export * from './video-playlist-create-result.model'
3export * from './video-playlist-create.model' 3export * from './video-playlist-create.model'
4export * from './video-playlist-element-create-result.model'
4export * from './video-playlist-element-create.model' 5export * from './video-playlist-element-create.model'
5export * from './video-playlist-element-update.model' 6export * from './video-playlist-element-update.model'
6export * from './video-playlist-element.model' 7export * from './video-playlist-element.model'
diff --git a/shared/models/videos/playlist/video-playlist-element-create-result.model.ts b/shared/models/videos/playlist/video-playlist-element-create-result.model.ts
new file mode 100644
index 000000000..dc475e7d8
--- /dev/null
+++ b/shared/models/videos/playlist/video-playlist-element-create-result.model.ts
@@ -0,0 +1,3 @@
1export interface VideoPlaylistElementCreateResult {
2 id: number
3}
diff --git a/shared/models/videos/video-update.model.ts b/shared/models/videos/video-update.model.ts
index e21ccae04..86653b959 100644
--- a/shared/models/videos/video-update.model.ts
+++ b/shared/models/videos/video-update.model.ts
@@ -1,5 +1,6 @@
1import { VideoPrivacy } from './video-privacy.enum' 1import { VideoPrivacy } from './video-privacy.enum'
2import { VideoScheduleUpdate } from './video-schedule-update.model' 2import { VideoScheduleUpdate } from './video-schedule-update.model'
3
3export interface VideoUpdate { 4export interface VideoUpdate {
4 name?: string 5 name?: string
5 category?: number 6 category?: number
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml
index 99a725ead..76e78fe53 100644
--- a/support/doc/api/openapi.yaml
+++ b/support/doc/api/openapi.yaml
@@ -716,7 +716,7 @@ paths:
716 - admin 716 - admin
717 tags: 717 tags:
718 - Instance Follows 718 - Instance Follows
719 summary: Follow a list of servers 719 summary: Follow a list of actors (PeerTube instance, channel or account)
720 responses: 720 responses:
721 '204': 721 '204':
722 description: successful operation 722 description: successful operation
@@ -734,28 +734,32 @@ paths:
734 type: string 734 type: string
735 format: hostname 735 format: hostname
736 uniqueItems: true 736 uniqueItems: true
737 handles:
738 type: array
739 items:
740 type: string
741 uniqueItems: true
737 742
738 '/server/following/{host}': 743 '/server/following/{hostOrHandle}':
739 delete: 744 delete:
740 summary: Unfollow a server 745 summary: Unfollow an actor (PeerTube instance, channel or account)
741 security: 746 security:
742 - OAuth2: 747 - OAuth2:
743 - admin 748 - admin
744 tags: 749 tags:
745 - Instance Follows 750 - Instance Follows
746 parameters: 751 parameters:
747 - name: host 752 - name: hostOrHandle
748 in: path 753 in: path
749 required: true 754 required: true
750 description: The host to unfollow 755 description: The hostOrHandle to unfollow
751 schema: 756 schema:
752 type: string 757 type: string
753 format: hostname
754 responses: 758 responses:
755 '204': 759 '204':
756 description: successful operation 760 description: successful operation
757 '404': 761 '404':
758 description: host not found 762 description: host or handle not found
759 763
760 /users: 764 /users:
761 post: 765 post:
diff --git a/support/doc/dependencies.md b/support/doc/dependencies.md
index 8ea0c047d..d6c084cd7 100644
--- a/support/doc/dependencies.md
+++ b/support/doc/dependencies.md
@@ -151,6 +151,51 @@ sudo systemctl enable --now redis
151sudo systemctl enable --now postgresql 151sudo systemctl enable --now postgresql
152``` 152```
153 153
154## Rocky Linux 8.4
155
1561. Pull the latest updates:
157```
158sudo dnf update -y
159```
160
1612. Install NodeJS 12.x (why 12 and not 14? Not sure...):
162```
163sudo dnf module install -y nodejs:12
164```
165
1663. Install yarn:
167```
168sudo npm install --global yarn
169```
170
1714. Install or compile ffmpeg (if you want to compile... enjoy):
172```
173sudo dnf install -y epel-release
174sudo dnf --enablerepo=powertools install -y SDL2 SDL2-devel
175sudo dnf install -y --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpm https://download1.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-8.noarch.rpm
176sudo dnf install -y ffmpeg
177sudo dnf update -y
178```
179
1805. Install PostgreSQL and Python3 and other stuff:
181```
182sudo dnf install -y nginx postgresql postgresql-server postgresql-contrib openssl gcc-c++ make wget redis git python3
183sudo ln -s /usr/bin/python3 /usr/bin/python
184sudo PGSETUP_INITDB_OPTIONS='--auth-host=md5' postgresql-setup --initdb --unit postgresql
185sudo systemctl enable --now redis
186sudo systemctl enable --now postgresql
187```
188
1896. Configure the peertube user:
190```
191sudo useradd -m -d /var/www/peertube -s /bin/bash -p peertube peertube
192```
193
1947. Unknown missing steps:
195- Steps missing here... these were adapted from the CentOS 8 steps which abruptly ended.
196- /var/www/peertube does not exist yet (expected? done in future steps? documentation?).
197- Nothing about Certbot, NGINX, Firewall settings, and etc.
198- Hopefully someone can suggest what is missing here with some hints so I can add it?
154 199
155## Fedora 200## Fedora
156 201
diff --git a/support/doc/plugins/guide.md b/support/doc/plugins/guide.md
index 568c0662f..85aaf9f02 100644
--- a/support/doc/plugins/guide.md
+++ b/support/doc/plugins/guide.md
@@ -234,21 +234,29 @@ function register ({
234 234
235#### Update video constants 235#### Update video constants
236 236
237You can add/delete video categories, licences or languages using the appropriate managers: 237You can add/delete video categories, licences or languages using the appropriate constant managers:
238 238
239```js 239```js
240function register (...) { 240function register ({
241 videoLanguageManager.addLanguage('al_bhed', 'Al Bhed') 241 videoLanguageManager,
242 videoLanguageManager.deleteLanguage('fr') 242 videoCategoryManager,
243 videoLicenceManager,
244 videoPrivacyManager,
245 playlistPrivacyManager
246}) {
247 videoLanguageManager.addConstant('al_bhed', 'Al Bhed')
248 videoLanguageManager.deleteConstant('fr')
243 249
244 videoCategoryManager.addCategory(42, 'Best category') 250 videoCategoryManager.addConstant(42, 'Best category')
245 videoCategoryManager.deleteCategory(1) // Music 251 videoCategoryManager.deleteConstant(1) // Music
252 videoCategoryManager.resetConstants() // Reset to initial categories
253 videoCategoryManager.getConstants() // Retrieve all category constants
246 254
247 videoLicenceManager.addLicence(42, 'Best licence') 255 videoLicenceManager.addConstant(42, 'Best licence')
248 videoLicenceManager.deleteLicence(7) // Public domain 256 videoLicenceManager.deleteConstant(7) // Public domain
249 257
250 videoPrivacyManager.deletePrivacy(2) // Remove Unlisted video privacy 258 videoPrivacyManager.deleteConstant(2) // Remove Unlisted video privacy
251 playlistPrivacyManager.deletePlaylistPrivacy(3) // Remove Private video playlist privacy 259 playlistPrivacyManager.deleteConstant(3) // Remove Private video playlist privacy
252} 260}
253``` 261```
254 262
diff --git a/tsconfig.json b/tsconfig.json
index d305722c4..32e4a42e4 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -8,6 +8,7 @@
8 "emitDecoratorMetadata": true, 8 "emitDecoratorMetadata": true,
9 "importHelpers": true, 9 "importHelpers": true,
10 "removeComments": true, 10 "removeComments": true,
11 "strictBindCallApply": true,
11 "outDir": "./dist", 12 "outDir": "./dist",
12 "lib": [ 13 "lib": [
13 "dom", 14 "dom",
diff --git a/yarn.lock b/yarn.lock
index f68741038..68f17d414 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -63,10 +63,10 @@
63 dependencies: 63 dependencies:
64 "@babel/highlight" "^7.14.5" 64 "@babel/highlight" "^7.14.5"
65 65
66"@babel/helper-validator-identifier@^7.14.5": 66"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.8":
67 version "7.14.5" 67 version "7.14.8"
68 resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" 68 resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz#32be33a756f29e278a0d644fa08a2c9e0f88a34c"
69 integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== 69 integrity sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==
70 70
71"@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": 71"@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5":
72 version "7.14.5" 72 version "7.14.5"
@@ -78,23 +78,23 @@
78 js-tokens "^4.0.0" 78 js-tokens "^4.0.0"
79 79
80"@babel/parser@^7.6.0", "@babel/parser@^7.9.6": 80"@babel/parser@^7.6.0", "@babel/parser@^7.9.6":
81 version "7.14.7" 81 version "7.14.8"
82 resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595" 82 resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.8.tgz#66fd41666b2d7b840bd5ace7f7416d5ac60208d4"
83 integrity sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA== 83 integrity sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA==
84 84
85"@babel/runtime@^7.7.2": 85"@babel/runtime@^7.7.2":
86 version "7.14.6" 86 version "7.14.8"
87 resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" 87 resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446"
88 integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== 88 integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==
89 dependencies: 89 dependencies:
90 regenerator-runtime "^0.13.4" 90 regenerator-runtime "^0.13.4"
91 91
92"@babel/types@^7.6.1", "@babel/types@^7.9.6": 92"@babel/types@^7.6.1", "@babel/types@^7.9.6":
93 version "7.14.5" 93 version "7.14.8"
94 resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff" 94 resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.8.tgz#38109de8fcadc06415fbd9b74df0065d4d41c728"
95 integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg== 95 integrity sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q==
96 dependencies: 96 dependencies:
97 "@babel/helper-validator-identifier" "^7.14.5" 97 "@babel/helper-validator-identifier" "^7.14.8"
98 to-fast-properties "^2.0.0" 98 to-fast-properties "^2.0.0"
99 99
100"@dabh/diagnostics@^2.0.2": 100"@dabh/diagnostics@^2.0.2":
@@ -107,18 +107,18 @@
107 kuler "^2.0.0" 107 kuler "^2.0.0"
108 108
109"@digitalbazaar/http-client@^1.1.0": 109"@digitalbazaar/http-client@^1.1.0":
110 version "1.1.0" 110 version "1.2.0"
111 resolved "https://registry.yarnpkg.com/@digitalbazaar/http-client/-/http-client-1.1.0.tgz#cac383b24ace04b18b919deab773462b03d3d7b0" 111 resolved "https://registry.yarnpkg.com/@digitalbazaar/http-client/-/http-client-1.2.0.tgz#1ea3661e77000a15bd892a294f20dc6cc5d1c93b"
112 integrity sha512-ks7hqa6hm9NyULdbm9qL6TRS8rADzBw8R0lETvUgvdNXu9H62XG2YqoKRDThtfgWzWxLwRJ3Z2o4ev81dZZbyQ== 112 integrity sha512-W9KQQ5pUJcaR0I4c2HPJC0a7kRbZApIorZgPnEDwMBgj16iQzutGLrCXYaZOmxqVLVNqqlQ4aUJh+HBQZy4W6Q==
113 dependencies: 113 dependencies:
114 esm "^3.2.22" 114 esm "^3.2.22"
115 ky "^0.25.1" 115 ky "^0.25.1"
116 ky-universal "^0.8.2" 116 ky-universal "^0.8.2"
117 117
118"@eslint/eslintrc@^0.4.2": 118"@eslint/eslintrc@^0.4.3":
119 version "0.4.2" 119 version "0.4.3"
120 resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179" 120 resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
121 integrity sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg== 121 integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==
122 dependencies: 122 dependencies:
123 ajv "^6.12.4" 123 ajv "^6.12.4"
124 debug "^4.1.1" 124 debug "^4.1.1"
@@ -131,9 +131,9 @@
131 strip-json-comments "^3.1.1" 131 strip-json-comments "^3.1.1"
132 132
133"@hapi/boom@^9.1.2": 133"@hapi/boom@^9.1.2":
134 version "9.1.2" 134 version "9.1.3"
135 resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.2.tgz#48bd41d67437164a2d636e3b5bc954f8c8dc5e38" 135 resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.3.tgz#22cad56e39b7a4819161a99b1db19eaaa9b6cc6e"
136 integrity sha512-uJEJtiNHzKw80JpngDGBCGAmWjBtzxDCz17A9NO2zCi8LLBlb5Frpq4pXwyN+2JQMod4pKz5BALwyneCgDg89Q== 136 integrity sha512-RlrGyZ603hE/eRTZtTltocRm50HHmrmL3kGOP0SQ9MasazlW1mt/fkv4C5P/6rnpFXjwld/POFX1C8tMZE3ldg==
137 dependencies: 137 dependencies:
138 "@hapi/hoek" "9.x.x" 138 "@hapi/hoek" "9.x.x"
139 139
@@ -142,6 +142,20 @@
142 resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131" 142 resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131"
143 integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug== 143 integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==
144 144
145"@humanwhocodes/config-array@^0.5.0":
146 version "0.5.0"
147 resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9"
148 integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==
149 dependencies:
150 "@humanwhocodes/object-schema" "^1.2.0"
151 debug "^4.1.1"
152 minimatch "^3.0.4"
153
154"@humanwhocodes/object-schema@^1.2.0":
155 version "1.2.0"
156 resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf"
157 integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==
158
145"@jimp/bmp@^0.16.1": 159"@jimp/bmp@^0.16.1":
146 version "0.16.1" 160 version "0.16.1"
147 resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.16.1.tgz#6e2da655b2ba22e721df0795423f34e92ef13768" 161 resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.16.1.tgz#6e2da655b2ba22e721df0795423f34e92ef13768"
@@ -515,9 +529,9 @@
515 integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== 529 integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
516 530
517"@nodelib/fs.walk@^1.2.3": 531"@nodelib/fs.walk@^1.2.3":
518 version "1.2.7" 532 version "1.2.8"
519 resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz#94c23db18ee4653e129abd26fb06f870ac9e1ee2" 533 resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
520 integrity sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA== 534 integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
521 dependencies: 535 dependencies:
522 "@nodelib/fs.scandir" "2.1.5" 536 "@nodelib/fs.scandir" "2.1.5"
523 fastq "^1.6.0" 537 fastq "^1.6.0"
@@ -532,9 +546,9 @@
532 node-fetch "^2.6.1" 546 node-fetch "^2.6.1"
533 547
534"@openapitools/openapi-generator-cli@^2.1.4": 548"@openapitools/openapi-generator-cli@^2.1.4":
535 version "2.3.5" 549 version "2.3.7"
536 resolved "https://registry.yarnpkg.com/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.3.5.tgz#20f3974879ae22beb18a0f5a3685cabacf380cef" 550 resolved "https://registry.yarnpkg.com/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.3.7.tgz#5aaf9d178545874828db7c7c84a6f9a5b8ea00a4"
537 integrity sha512-b9dX47j3+g08qM/EMg/Ftw2qBOpfhKB31xyPJ7+kBvGvcoNoMed3aPyojv1iWNfU1KlJvp6k9zJvViOND0ckGg== 551 integrity sha512-4B93yPXop44fhAw6CT0eciaA0dRbPw5W7p2ZZ+HZ1uk4UD50zgaTAa9pnrsnCDnNtw2cvbZM0uCOf8xQyUy8Vg==
538 dependencies: 552 dependencies:
539 "@nestjs/common" "7.6.18" 553 "@nestjs/common" "7.6.18"
540 "@nestjs/core" "7.6.18" 554 "@nestjs/core" "7.6.18"
@@ -546,10 +560,10 @@
546 console.table "0.10.0" 560 console.table "0.10.0"
547 fs-extra "10.0.0" 561 fs-extra "10.0.0"
548 glob "7.1.6" 562 glob "7.1.6"
549 inquirer "8.1.1" 563 inquirer "8.1.2"
550 lodash "4.17.21" 564 lodash "4.17.21"
551 reflect-metadata "0.1.13" 565 reflect-metadata "0.1.13"
552 rxjs "7.1.0" 566 rxjs "7.2.0"
553 tslib "1.13.0" 567 tslib "1.13.0"
554 568
555"@sindresorhus/is@^0.14.0": 569"@sindresorhus/is@^0.14.0":
@@ -570,9 +584,9 @@
570 defer-to-connect "^1.0.1" 584 defer-to-connect "^1.0.1"
571 585
572"@szmarczak/http-timer@^4.0.5": 586"@szmarczak/http-timer@^4.0.5":
573 version "4.0.5" 587 version "4.0.6"
574 resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" 588 resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807"
575 integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== 589 integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==
576 dependencies: 590 dependencies:
577 defer-to-connect "^2.0.0" 591 defer-to-connect "^2.0.0"
578 592
@@ -592,26 +606,19 @@
592 integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== 606 integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==
593 607
594"@tsconfig/node16@^1.0.1": 608"@tsconfig/node16@^1.0.1":
595 version "1.0.1" 609 version "1.0.2"
596 resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1" 610 resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e"
597 integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA== 611 integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==
598
599"@types/apicache@^1.2.0":
600 version "1.2.2"
601 resolved "https://registry.yarnpkg.com/@types/apicache/-/apicache-1.2.2.tgz#b820659b1d95e66ec0e71dcd0317e9d30f0c154b"
602 integrity sha512-+rjhMdbx7m7pKnNiQCSiKE21mGmMzsfCU871h5BCj4guhAj423j61Dq0Yrr9CnLiDwUhSKtkXWudr9SQE0/IoA==
603 dependencies:
604 "@types/redis" "*"
605 612
606"@types/async-lock@^1.1.0": 613"@types/async-lock@^1.1.0":
607 version "1.1.2" 614 version "1.1.3"
608 resolved "https://registry.yarnpkg.com/@types/async-lock/-/async-lock-1.1.2.tgz#cbc26a34b11b83b28f7783a843c393b443ef8bef" 615 resolved "https://registry.yarnpkg.com/@types/async-lock/-/async-lock-1.1.3.tgz#0d86017cf87abbcb941c55360e533d37a3f23b3d"
609 integrity sha512-j9n4bb6RhgFIydBe0+kpjnBPYumDaDyU8zvbWykyVMkku+c2CSu31MZkLeaBfqIwU+XCxlDpYDfyMQRkM0AkeQ== 616 integrity sha512-UpeDcjGKsYEQMeqEbfESm8OWJI305I7b9KE4ji3aBjoKWyN5CTdn8izcA1FM1DVDne30R5fNEnIy89vZw5LXJQ==
610 617
611"@types/async@^3.0.0": 618"@types/async@^3.0.0":
612 version "3.2.6" 619 version "3.2.7"
613 resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.6.tgz#1d49339846c6aa0b0a8a08303c21176f1b64dc4f" 620 resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.7.tgz#f784478440d313941e7b12c2e4db53b0ed55637b"
614 integrity sha512-ZkrXnZLC1mc4b9QLKaSrsxV4oxTRs10OI2kgSApT8G0v1jrmqppSHUVQ15kLorzsFBTjvf7OKF4kAibuuNQ+xA== 621 integrity sha512-a+MBBfOTs3ShFMlbH9qsRVFkjIUunEtxrBT0gxRx1cntjKRg2WApuGmNYzHkwKaIhMi3SMbKktaD/rLObQMwIw==
615 622
616"@types/bcrypt@^5.0.0": 623"@types/bcrypt@^5.0.0":
617 version "5.0.0" 624 version "5.0.0"
@@ -628,34 +635,34 @@
628 "@types/node" "*" 635 "@types/node" "*"
629 636
630"@types/bluebird@^3.5.33": 637"@types/bluebird@^3.5.33":
631 version "3.5.35" 638 version "3.5.36"
632 resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.35.tgz#3964c48372bf62d60616d8673dd77a9719ebac9b" 639 resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.36.tgz#00d9301d4dc35c2f6465a8aec634bb533674c652"
633 integrity sha512-2WeeXK7BuQo7yPI4WGOBum90SzF/f8rqlvpaXx4rjeTmNssGRDHWf7fgDUH90xMB3sUOu716fUK5d+OVx0+ncQ== 640 integrity sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q==
634 641
635"@types/body-parser@*", "@types/body-parser@^1.16.3": 642"@types/body-parser@*", "@types/body-parser@^1.16.3":
636 version "1.19.0" 643 version "1.19.1"
637 resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" 644 resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.1.tgz#0c0174c42a7d017b818303d4b5d969cb0b75929c"
638 integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== 645 integrity sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==
639 dependencies: 646 dependencies:
640 "@types/connect" "*" 647 "@types/connect" "*"
641 "@types/node" "*" 648 "@types/node" "*"
642 649
643"@types/bull@^3.15.0": 650"@types/bull@^3.15.0":
644 version "3.15.1" 651 version "3.15.2"
645 resolved "https://registry.yarnpkg.com/@types/bull/-/bull-3.15.1.tgz#3c3fd665b43ef383ca95a91b8d1448461fae0898" 652 resolved "https://registry.yarnpkg.com/@types/bull/-/bull-3.15.2.tgz#b824b0b4fc8d1d9294a20973f6ceedcba1a7f3e8"
646 integrity sha512-thZyjxikoyuDa/ptZEqtTEPUjwlDenkpPigpIyad1X5UMp7U0fXTLiDHJjZ/5yXmVPuWx0cXFXj3drmva/UJRA== 653 integrity sha512-uMQ7u/4GxY2bSTMd4P2yLkyqu3GoKbwTCDkMHJJ2g9OkiMq0Vxw+C7lF4w+oNkwZzZ2k4Kw76Ncxjd6GMnc+CA==
647 dependencies: 654 dependencies:
648 "@types/ioredis" "*" 655 "@types/ioredis" "*"
649 656
650"@types/bytes@^3.0.0": 657"@types/bytes@^3.0.0":
651 version "3.1.0" 658 version "3.1.1"
652 resolved "https://registry.yarnpkg.com/@types/bytes/-/bytes-3.1.0.tgz#835a3e4aea3b4d7604aca216a78de372bff3ecc3" 659 resolved "https://registry.yarnpkg.com/@types/bytes/-/bytes-3.1.1.tgz#67a876422e660dc4c10a27f3e5bcfbd5455f01d0"
653 integrity sha512-5YG1AiIC8HPPXRvYAIa7ehK3YMAwd0DWiPCtpuL9sgKceWLyWsVtLRA+lT4NkoanDNF9slwQ66lPizWDpgRlWA== 660 integrity sha512-lOGyCnw+2JVPKU3wIV0srU0NyALwTBJlVSx5DfMQOFuuohA8y9S8orImpuIQikZ0uIQ8gehrRjxgQC1rLRi11w==
654 661
655"@types/cacheable-request@^6.0.1": 662"@types/cacheable-request@^6.0.1":
656 version "6.0.1" 663 version "6.0.2"
657 resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" 664 resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9"
658 integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== 665 integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==
659 dependencies: 666 dependencies:
660 "@types/http-cache-semantics" "*" 667 "@types/http-cache-semantics" "*"
661 "@types/keyv" "*" 668 "@types/keyv" "*"
@@ -682,62 +689,62 @@
682 "@types/chai" "*" 689 "@types/chai" "*"
683 690
684"@types/chai@*", "@types/chai@^4.0.4": 691"@types/chai@*", "@types/chai@^4.0.4":
685 version "4.2.19" 692 version "4.2.21"
686 resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.19.tgz#80f286b515897413c7a35bdda069cc80f2344233" 693 resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.21.tgz#9f35a5643129df132cf3b5c1ec64046ea1af0650"
687 integrity sha512-jRJgpRBuY+7izT7/WNXP/LsMO9YonsstuL+xuvycDyESpoDoIAsMd7suwpB4h9oEWB+ZlPTqJJ8EHomzNhwTPQ== 694 integrity sha512-yd+9qKmJxm496BOV9CMNaey8TWsikaZOwMRwPHQIjcOJM9oV+fi9ZMNw3JsVnbEEbo2gRTDnGEBv8pjyn67hNg==
688 695
689"@types/component-emitter@^1.2.10": 696"@types/component-emitter@^1.2.10":
690 version "1.2.10" 697 version "1.2.10"
691 resolved "https://registry.yarnpkg.com/@types/component-emitter/-/component-emitter-1.2.10.tgz#ef5b1589b9f16544642e473db5ea5639107ef3ea" 698 resolved "https://registry.yarnpkg.com/@types/component-emitter/-/component-emitter-1.2.10.tgz#ef5b1589b9f16544642e473db5ea5639107ef3ea"
692 integrity sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg== 699 integrity sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==
693 700
694"@types/config@^0.0.38": 701"@types/config@^0.0.39":
695 version "0.0.38" 702 version "0.0.39"
696 resolved "https://registry.yarnpkg.com/@types/config/-/config-0.0.38.tgz#ca30679b21b5b297299467e3a3f1c8e2e64b9170" 703 resolved "https://registry.yarnpkg.com/@types/config/-/config-0.0.39.tgz#aad18ceb9439329adc3d4c6b91a908a72c715612"
697 integrity sha512-z2WizAfIFgSv8SQfQ8c0LlbDAcK47D/o93XW6bxZ9t3bs4fmmfAPjk1nhAIBTG84PBBCHfSPM+8g7vhLdbFokg== 704 integrity sha512-EBHj9lSIyw62vwqCwkeJXjiV6C2m2o+RJZlRWLkHduGYiNBoMXcY6AhSLqjQQ+uPdrPYrOMYvVa41zjo00LbFQ==
698 705
699"@types/connect@*": 706"@types/connect@*":
700 version "3.4.34" 707 version "3.4.35"
701 resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901" 708 resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
702 integrity sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ== 709 integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==
703 dependencies: 710 dependencies:
704 "@types/node" "*" 711 "@types/node" "*"
705 712
706"@types/cookie@^0.4.0": 713"@types/cookie@^0.4.0":
707 version "0.4.0" 714 version "0.4.1"
708 resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.0.tgz#14f854c0f93d326e39da6e3b6f34f7d37513d108" 715 resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d"
709 integrity sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg== 716 integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==
710 717
711"@types/cookiejar@*": 718"@types/cookiejar@*":
712 version "2.1.2" 719 version "2.1.2"
713 resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8" 720 resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8"
714 integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog== 721 integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==
715 722
716"@types/cors@^2.8.8": 723"@types/cors@^2.8.10":
717 version "2.8.10" 724 version "2.8.12"
718 resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.10.tgz#61cc8469849e5bcdd0c7044122265c39cec10cf4" 725 resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080"
719 integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ== 726 integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==
720 727
721"@types/express-rate-limit@^5.0.0": 728"@types/express-rate-limit@^5.0.0":
722 version "5.1.2" 729 version "5.1.3"
723 resolved "https://registry.yarnpkg.com/@types/express-rate-limit/-/express-rate-limit-5.1.2.tgz#51f030ce722fe298269f85378b49a34837a1d2ca" 730 resolved "https://registry.yarnpkg.com/@types/express-rate-limit/-/express-rate-limit-5.1.3.tgz#79f2ca40d90455a5798da6f8e06d8a3d35f4a1d6"
724 integrity sha512-loN1dcr0WEKsbVtXNQKDef4Fmh25prfy+gESrwTDofIhAt17ngTxrsDiEZxLemmfHH279x206CdUB9XHXS9E6Q== 731 integrity sha512-H+TYy3K53uPU2TqPGFYaiWc2xJV6+bIFkDd/Ma2/h67Pa6ARk9kWE0p/K9OH1Okm0et9Sfm66fmXoAxsH2PHXg==
725 dependencies: 732 dependencies:
726 "@types/express" "*" 733 "@types/express" "*"
727 734
728"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18": 735"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18":
729 version "4.17.21" 736 version "4.17.24"
730 resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz#a427278e106bca77b83ad85221eae709a3414d42" 737 resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz#ea41f93bf7e0d59cd5a76665068ed6aab6815c07"
731 integrity sha512-gwCiEZqW6f7EoR8TTEfalyEhb1zA5jQJnRngr97+3pzMaO1RKoI1w2bw07TK72renMUVWcWS5mLI6rk1NqN0nA== 738 integrity sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA==
732 dependencies: 739 dependencies:
733 "@types/node" "*" 740 "@types/node" "*"
734 "@types/qs" "*" 741 "@types/qs" "*"
735 "@types/range-parser" "*" 742 "@types/range-parser" "*"
736 743
737"@types/express@*": 744"@types/express@*":
738 version "4.17.12" 745 version "4.17.13"
739 resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.12.tgz#4bc1bf3cd0cfe6d3f6f2853648b40db7d54de350" 746 resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034"
740 integrity sha512-pTYas6FrP15B1Oa0bkN5tQMNqOcVXa9j4FTFtO8DWI9kppKib+6NJtfTOOLcwxuuYvcX2+dVG6et1SxW/Kc17Q== 747 integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==
741 dependencies: 748 dependencies:
742 "@types/body-parser" "*" 749 "@types/body-parser" "*"
743 "@types/express-serve-static-core" "^4.17.18" 750 "@types/express-serve-static-core" "^4.17.18"
@@ -755,76 +762,71 @@
755 "@types/serve-static" "*" 762 "@types/serve-static" "*"
756 763
757"@types/fluent-ffmpeg@^2.1.16": 764"@types/fluent-ffmpeg@^2.1.16":
758 version "2.1.17" 765 version "2.1.18"
759 resolved "https://registry.yarnpkg.com/@types/fluent-ffmpeg/-/fluent-ffmpeg-2.1.17.tgz#6958dda400fe1b33c21f3683db76905cb210d053" 766 resolved "https://registry.yarnpkg.com/@types/fluent-ffmpeg/-/fluent-ffmpeg-2.1.18.tgz#72246c2f8c0f0a590aefc1cdbe13736c73f22a81"
760 integrity sha512-/bdvjKw/mtBHlJ2370d04nt4CsWqU5MrwS/NtO96V01jxitJ4+iq8OFNcqc5CegeV3TQOK3uueK02kvRK+zjUg== 767 integrity sha512-LTteOx3RUmnPlKkvhvW9aGOHdJYyEtIiRBVbYVO/zPU65ZYQelbPJ+zBBT+IXup7doMvxVstX7NMoUTWKZOhww==
761 dependencies: 768 dependencies:
762 "@types/node" "*" 769 "@types/node" "*"
763 770
764"@types/fs-extra@^9.0.1": 771"@types/fs-extra@^9.0.1":
765 version "9.0.11" 772 version "9.0.12"
766 resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.11.tgz#8cc99e103499eab9f347dbc6ca4e99fb8d2c2b87" 773 resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.12.tgz#9b8f27973df8a7a3920e8461517ebf8a7d4fdfaf"
767 integrity sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA== 774 integrity sha512-I+bsBr67CurCGnSenZZ7v94gd3tc3+Aj2taxMT4yu4ABLuOgOjeFxX3dokG24ztSRg5tnT00sL8BszO7gSMoIw==
768 dependencies: 775 dependencies:
769 "@types/node" "*" 776 "@types/node" "*"
770 777
771"@types/http-cache-semantics@*": 778"@types/http-cache-semantics@*":
772 version "4.0.0" 779 version "4.0.1"
773 resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" 780 resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812"
774 integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== 781 integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==
775 782
776"@types/ioredis@*": 783"@types/ioredis@*":
777 version "4.26.4" 784 version "4.26.6"
778 resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.26.4.tgz#a2b1ed51ddd2c707d7eaac5017cc34a0fe51558a" 785 resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.26.6.tgz#7e332d6d24f12d79a1099834ccfa0c169ef667ed"
779 integrity sha512-QFbjNq7EnOGw6d1gZZt2h26OFXjx7z+eqEnbCHSrDI1OOLEgOHMKdtIajJbuCr9uO+X9kQQRe7Lz6uxqxl5XKg== 786 integrity sha512-Q9ydXL/5Mot751i7WLCm9OGTj5jlW3XBdkdEW21SkXZ8Y03srbkluFGbM3q8c+vzPW30JOLJ+NsZWHoly0+13A==
780 dependencies: 787 dependencies:
781 "@types/node" "*" 788 "@types/node" "*"
782 789
783"@types/json-schema@^7.0.6", "@types/json-schema@^7.0.7": 790"@types/json-schema@^7.0.6", "@types/json-schema@^7.0.7":
784 version "7.0.7" 791 version "7.0.8"
785 resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" 792 resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818"
786 integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== 793 integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg==
787
788"@types/json5@^0.0.29":
789 version "0.0.29"
790 resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
791 integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
792 794
793"@types/keyv@*": 795"@types/keyv@*":
794 version "3.1.1" 796 version "3.1.2"
795 resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" 797 resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.2.tgz#5d97bb65526c20b6e0845f6b0d2ade4f28604ee5"
796 integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== 798 integrity sha512-/FvAK2p4jQOaJ6CGDHJTqZcUtbZe820qIeTg7o0Shg7drB4JHeL+V/dhSaly7NXx6u8eSee+r7coT+yuJEvDLg==
797 dependencies: 799 dependencies:
798 "@types/node" "*" 800 "@types/node" "*"
799 801
800"@types/lodash@^4.14.64": 802"@types/lodash@^4.14.64":
801 version "4.14.170" 803 version "4.14.171"
802 resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.170.tgz#0d67711d4bf7f4ca5147e9091b847479b87925d6" 804 resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.171.tgz#f01b3a5fe3499e34b622c362a46a609fdb23573b"
803 integrity sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q== 805 integrity sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg==
804 806
805"@types/lru-cache@^5.1.0": 807"@types/lru-cache@^5.1.0":
806 version "5.1.0" 808 version "5.1.1"
807 resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03" 809 resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef"
808 integrity sha512-RaE0B+14ToE4l6UqdarKPnXwVDuigfFv+5j9Dze/Nqr23yyuqdNvzcZi3xB+3Agvi5R4EOgAksfv3lXX4vBt9w== 810 integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==
809 811
810"@types/magnet-uri@*", "@types/magnet-uri@^5.1.1": 812"@types/magnet-uri@*", "@types/magnet-uri@^5.1.1":
811 version "5.1.2" 813 version "5.1.3"
812 resolved "https://registry.yarnpkg.com/@types/magnet-uri/-/magnet-uri-5.1.2.tgz#7860417399d52ddc0be1021d570b4ac93ffc133e" 814 resolved "https://registry.yarnpkg.com/@types/magnet-uri/-/magnet-uri-5.1.3.tgz#cdf974721012bd758c0f559cabcad7bab87f9008"
813 integrity sha512-bXFPXskwHoEYP6t8rq4nWchOlbUzXkyhnfCVZmq+zb25R5pWkasw7BmTIqDKQ6RAQmq89jll1v23yLa/SvPfAw== 815 integrity sha512-FvJN1yYdLhvU6zWJ2YnWQ2GnpFLsA8bt+85WY0tLh6ehzGNrvBorjlcc53/zY43r/IKn+ctFs1nt7andwGnQCQ==
814 dependencies: 816 dependencies:
815 "@types/node" "*" 817 "@types/node" "*"
816 818
817"@types/maildev@^0.0.2": 819"@types/maildev@^0.0.3":
818 version "0.0.2" 820 version "0.0.3"
819 resolved "https://registry.yarnpkg.com/@types/maildev/-/maildev-0.0.2.tgz#936f21d66d2c38fafdd653d5bee8b642eb1dab89" 821 resolved "https://registry.yarnpkg.com/@types/maildev/-/maildev-0.0.3.tgz#8a7e3cc640d5388d86bcd11f6c18e40926244b87"
820 integrity sha512-ITMKrdajIgqe5lz0BrU2xFB3yN4gX/+a2vemuPfgURlcxLn7V5i9AuzGQl2wiH2cg7zcZBqh8EHX+z3ufF7AUA== 822 integrity sha512-fY5WoW3zsW686UFKf5ISIWiolaYoo+kbFE/B1rOHNJ768gdyIgu3Wol02vwanq2gFQO420iTUDaZ7ZpQbjZ57Q==
821 dependencies: 823 dependencies:
822 "@types/node" "*" 824 "@types/node" "*"
823 825
824"@types/memoizee@^0.4.2": 826"@types/memoizee@^0.4.2":
825 version "0.4.5" 827 version "0.4.6"
826 resolved "https://registry.yarnpkg.com/@types/memoizee/-/memoizee-0.4.5.tgz#cb4e7031decf698c52c4f57c348180b0385aa7da" 828 resolved "https://registry.yarnpkg.com/@types/memoizee/-/memoizee-0.4.6.tgz#a4ba7a3ea61fa45be916f148763bec2ca38c34cf"
827 integrity sha512-+ZzZZ3+0a7/ajBPeAAD4+LxrBsCat0EFZQtO3o0rwpIeLmDmSaM8KF/oYPuFxeUFAMiHIHFcGucFnY/8S4Hszg== 829 integrity sha512-qJezGqoi3pW9Pset2w1Gfv8jATvmHHHnpO9Dq8x8pJGyYIpiUZJqRU0NM7xenmN0AcXEe7vqshI8H98KeFLYcg==
828 830
829"@types/mime@^1": 831"@types/mime@^1":
830 version "1.3.2" 832 version "1.3.2"
@@ -832,109 +834,114 @@
832 integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== 834 integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
833 835
834"@types/minimatch@^3.0.3": 836"@types/minimatch@^3.0.3":
835 version "3.0.4" 837 version "3.0.5"
836 resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" 838 resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
837 integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== 839 integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
838 840
839"@types/mkdirp@^1.0.0": 841"@types/mkdirp@^1.0.0":
840 version "1.0.1" 842 version "1.0.2"
841 resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-1.0.1.tgz#0930b948914a78587de35458b86c907b6e98bbf6" 843 resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-1.0.2.tgz#8d0bad7aa793abe551860be1f7ae7f3198c16666"
842 integrity sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q== 844 integrity sha512-o0K1tSO0Dx5X6xlU5F1D6625FawhC3dU3iqr25lluNv/+/QIVH8RLNEiVokgIZo+mz+87w/3Mkg/VvQS+J51fQ==
843 dependencies: 845 dependencies:
844 "@types/node" "*" 846 "@types/node" "*"
845 847
846"@types/mocha@^8.0.3": 848"@types/mocha@^8.0.3":
847 version "8.2.2" 849 version "8.2.3"
848 resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.2.tgz#91daa226eb8c2ff261e6a8cbf8c7304641e095e0" 850 resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.3.tgz#bbeb55fbc73f28ea6de601fbfa4613f58d785323"
849 integrity sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw== 851 integrity sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==
850 852
851"@types/morgan@^1.7.32": 853"@types/morgan@^1.7.32":
852 version "1.9.2" 854 version "1.9.3"
853 resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.9.2.tgz#450f958a4d3fb0694a3ba012b09c8106f9a2885e" 855 resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.9.3.tgz#ae04180dff02c437312bc0cfb1e2960086b2f540"
854 integrity sha512-edtGMEdit146JwwIeyQeHHg9yID4WSolQPxpEorHmN3KuytuCHyn2ELNr5Uxy8SerniFbbkmgKMrGM933am5BQ== 856 integrity sha512-BiLcfVqGBZCyNCnCH3F4o2GmDLrpy0HeBVnNlyZG4fo88ZiE9SoiBe3C+2ezuwbjlEyT+PDZ17//TAlRxAn75Q==
855 dependencies: 857 dependencies:
856 "@types/node" "*" 858 "@types/node" "*"
857 859
858"@types/multer@^1.3.3": 860"@types/multer@^1.3.3":
859 version "1.4.6" 861 version "1.4.7"
860 resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.6.tgz#411950b7a99ba0de6ee8f6e3713f4628980cdc73" 862 resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.7.tgz#89cf03547c28c7bbcc726f029e2a76a7232cc79e"
861 integrity sha512-F4EZ+KRrzdiSm3jSFj1GVUlw3zWXus5nXYBbrQW/0MGIUv9YHw1dM0cJOxq++v2+Gl4IBdSDvQ3YCORLdyCU+Q== 863 integrity sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==
862 dependencies: 864 dependencies:
863 "@types/express" "*" 865 "@types/express" "*"
864 866
865"@types/node@*", "@types/node@>=10.0.0", "@types/node@^15.0.1": 867"@types/node@*", "@types/node@>=10.0.0":
866 version "15.12.4" 868 version "16.4.0"
867 resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.4.tgz#e1cf817d70a1e118e81922c4ff6683ce9d422e26" 869 resolved "https://registry.yarnpkg.com/@types/node/-/node-16.4.0.tgz#2c219eaa3b8d1e4d04f4dd6e40bc68c7467d5272"
868 integrity sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA== 870 integrity sha512-HrJuE7Mlqcjj+00JqMWpZ3tY8w7EUd+S0U3L1+PQSWiXZbOgyQDvi+ogoUxaHApPJq5diKxYBQwA3iIlNcPqOg==
869 871
870"@types/node@^14.14.31": 872"@types/node@^14.14.31":
871 version "14.17.4" 873 version "14.17.5"
872 resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.4.tgz#218712242446fc868d0e007af29a4408c7765bc0" 874 resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.5.tgz#b59daf6a7ffa461b5648456ca59050ba8e40ed54"
873 integrity sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A== 875 integrity sha512-bjqH2cX/O33jXT/UmReo2pM7DIJREPMnarixbQ57DOOzzFaI6D2+IcwaJQaJpv0M1E9TIhPCYVxrkcityLjlqA==
876
877"@types/node@^15.0.1":
878 version "15.14.2"
879 resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.2.tgz#7af8ab20156586f076f4760bc1b3c5ddfffd1ff2"
880 integrity sha512-dvMUE/m2LbXPwlvVuzCyslTEtQ2ZwuuFClDrOQ6mp2CenCg971719PTILZ4I6bTP27xfFFc+o7x2TkLuun/MPw==
874 881
875"@types/nodemailer@^6.2.0": 882"@types/nodemailer@^6.2.0":
876 version "6.4.2" 883 version "6.4.4"
877 resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.2.tgz#d8ee254c969e6ad83fb9a0a0df3a817406a3fa3b" 884 resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.4.tgz#c265f7e7a51df587597b3a49a023acaf0c741f4b"
878 integrity sha512-yhsqg5Xbr8aWdwjFS3QjkniW5/tLpWXtOYQcJdo9qE3DolBxsKzgRCQrteaMY0hos8MklJNSEsMqDpZynGzMNg== 885 integrity sha512-Ksw4t7iliXeYGvIQcSIgWQ5BLuC/mljIEbjf615svhZL10PE9t+ei8O9gDaD3FPCasUJn9KTLwz2JFJyiiyuqw==
879 dependencies: 886 dependencies:
880 "@types/node" "*" 887 "@types/node" "*"
881 888
882"@types/normalize-package-data@^2.4.0": 889"@types/normalize-package-data@^2.4.0":
883 version "2.4.0" 890 version "2.4.1"
884 resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" 891 resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
885 integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== 892 integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==
886 893
887"@types/oauth2-server@^3.0.8": 894"@types/oauth2-server@^3.0.8":
888 version "3.0.12" 895 version "3.0.13"
889 resolved "https://registry.yarnpkg.com/@types/oauth2-server/-/oauth2-server-3.0.12.tgz#3c404055c2c88068a3ee8f5e5a0c5a12bca8c365" 896 resolved "https://registry.yarnpkg.com/@types/oauth2-server/-/oauth2-server-3.0.13.tgz#e93baf99a923ffbb9ef09dea9978ee63d706b96c"
890 integrity sha512-biRndg8t05UxbW1Aqe9kqDbzoi3wSgKITQxtLKR2eK0SwWTgF6AS32IyriFX6qf8KZWhruViVat7MuFfuAUrZQ== 897 integrity sha512-thC6D0vgqUh2LFeOV7AzuWaAjZfciFmnh2tM10Nw4ZllMnTP7jw8PYMY6ti7PHAkwp4Cz9YFZs2LFwlXlA87Bw==
891 dependencies: 898 dependencies:
892 "@types/express" "*" 899 "@types/express" "*"
893 900
894"@types/parse-torrent-file@*": 901"@types/parse-torrent-file@*":
895 version "4.0.2" 902 version "4.0.3"
896 resolved "https://registry.yarnpkg.com/@types/parse-torrent-file/-/parse-torrent-file-4.0.2.tgz#40c96fc075aec256514807c6c381d11d9035bd9e" 903 resolved "https://registry.yarnpkg.com/@types/parse-torrent-file/-/parse-torrent-file-4.0.3.tgz#045b023426d168e0253c932cb782b231b1ee2d62"
897 integrity sha512-EzdzpcN0sStQ35sUV2SChTJErLsbotsxZ/RYeR9gf3zXKlPLKaA7aIAoS/nuLRvfxH8mbrWQmXSw76alKecSdg== 904 integrity sha512-dFkPnJPKiFWiGX+HXmyTVt2js3k0d9dThmUxX8nfGC22hbyZ5BTmetsEl45sQhHLcFo43njVrIKMXM3F1ahXRw==
898 dependencies: 905 dependencies:
899 "@types/node" "*" 906 "@types/node" "*"
900 907
901"@types/parse-torrent@*": 908"@types/parse-torrent@*":
902 version "5.8.3" 909 version "5.8.4"
903 resolved "https://registry.yarnpkg.com/@types/parse-torrent/-/parse-torrent-5.8.3.tgz#ff4e987d09ad27ccc1c8893b3a2c6a31a3bc4042" 910 resolved "https://registry.yarnpkg.com/@types/parse-torrent/-/parse-torrent-5.8.4.tgz#c095834a9a815507c59014a79517ad403e4329d0"
904 integrity sha512-c0xAjnpov+Xk/2HTtpaBm0tukNIAoZoxrqgTDwSaIu6IVCynY+2YD9zcQNk2P6H4atcXzD78/LI2CQzLlMmAJg== 911 integrity sha512-FdKs5yN5iYO5Cu9gVz1Zl30CbZe6HTsqloWmCf+LfbImgSzlsUkov2+npQWCQSQ3zi/a2G5C824K0UpZ2sRufA==
905 dependencies: 912 dependencies:
906 "@types/magnet-uri" "*" 913 "@types/magnet-uri" "*"
907 "@types/node" "*" 914 "@types/node" "*"
908 "@types/parse-torrent-file" "*" 915 "@types/parse-torrent-file" "*"
909 916
910"@types/pem@^1.9.3": 917"@types/pem@^1.9.3":
911 version "1.9.5" 918 version "1.9.6"
912 resolved "https://registry.yarnpkg.com/@types/pem/-/pem-1.9.5.tgz#cd5548b5e0acb4b41a9e21067e9fcd8c57089c99" 919 resolved "https://registry.yarnpkg.com/@types/pem/-/pem-1.9.6.tgz#c3686832e935947fdd9d848dec3b8fe830068de7"
913 integrity sha512-C0txxEw8B7DCoD85Ko7SEvzUogNd5VDJ5/YBG8XUcacsOGqxr5Oo4g3OUAfdEDUbhXanwUoVh/ZkMFw77FGPQQ== 920 integrity sha512-IC67SxacM9fxEi/w7hf98dTun83OwUMeLMo1NS2gE0wdM9MHeg73iH/Pp9nB02OUCQ7Zb2UuKE/IpFCmQw9jxw==
914 dependencies: 921 dependencies:
915 "@types/node" "*" 922 "@types/node" "*"
916 923
917"@types/qs@*": 924"@types/qs@*":
918 version "6.9.6" 925 version "6.9.7"
919 resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.6.tgz#df9c3c8b31a247ec315e6996566be3171df4b3b1" 926 resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
920 integrity sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA== 927 integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
921 928
922"@types/range-parser@*": 929"@types/range-parser@*":
923 version "1.2.3" 930 version "1.2.4"
924 resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" 931 resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
925 integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== 932 integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
926 933
927"@types/redis@*", "@types/redis@^2.8.5": 934"@types/redis@^2.8.5":
928 version "2.8.30" 935 version "2.8.31"
929 resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.30.tgz#2b63ce9ff93959355d8a1f6d7a45e483b5fe0299" 936 resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.31.tgz#c11c1b269fec132ac2ec9eb891edf72fc549149e"
930 integrity sha512-4D3XwfIc671FSNXNruE/wmf6jWL7QYtyAhiWXJDkY41F4atMnOol4584oP4WqnW3uHe8d+Jn+wDLuQaxbfMgXQ== 937 integrity sha512-daWrrTDYaa5iSDFbgzZ9gOOzyp2AJmYK59OlG/2KGBgYWF3lfs8GDKm1c//tik5Uc93hDD36O+qLPvzDolChbA==
931 dependencies: 938 dependencies:
932 "@types/node" "*" 939 "@types/node" "*"
933 940
934"@types/request@^2.0.3": 941"@types/request@^2.0.3":
935 version "2.48.5" 942 version "2.48.6"
936 resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.5.tgz#019b8536b402069f6d11bee1b2c03e7f232937a0" 943 resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.6.tgz#2300e7fc443108f79efa90e3bdf34c6d60fa89d8"
937 integrity sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ== 944 integrity sha512-vrZaV3Ij7j/l/3hz6OttZFtpRCu7zlq7XgkYHJP6FwVEAZkGQ095WqyJV08/GlW9eyXKVcp/xmtruHm8eHpw1g==
938 dependencies: 945 dependencies:
939 "@types/caseless" "*" 946 "@types/caseless" "*"
940 "@types/node" "*" 947 "@types/node" "*"
@@ -949,31 +956,31 @@
949 "@types/node" "*" 956 "@types/node" "*"
950 957
951"@types/sax@^1.2.1": 958"@types/sax@^1.2.1":
952 version "1.2.1" 959 version "1.2.3"
953 resolved "https://registry.yarnpkg.com/@types/sax/-/sax-1.2.1.tgz#e0248be936ece791a82db1a57f3fb5f7c87e8172" 960 resolved "https://registry.yarnpkg.com/@types/sax/-/sax-1.2.3.tgz#b630ac1403ebd7812e0bf9a10de9bf5077afb348"
954 integrity sha512-dqYdvN7Sbw8QT/0Ci5rhjE4/iCMJEM0Y9rHpCu+gGXD9Lwbz28t6HI2yegsB6BoV1sShRMU6lAmAcgRjmFy7LA== 961 integrity sha512-+QSw6Tqvs/KQpZX8DvIl3hZSjNFLW/OqE5nlyHXtTwODaJvioN2rOWpBNEWZp2HZUFhOh+VohmJku/WxEXU2XA==
955 dependencies: 962 dependencies:
956 "@types/node" "*" 963 "@types/node" "*"
957 964
958"@types/serve-static@*": 965"@types/serve-static@*":
959 version "1.13.9" 966 version "1.13.10"
960 resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.9.tgz#aacf28a85a05ee29a11fb7c3ead935ac56f33e4e" 967 resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9"
961 integrity sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA== 968 integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==
962 dependencies: 969 dependencies:
963 "@types/mime" "^1" 970 "@types/mime" "^1"
964 "@types/node" "*" 971 "@types/node" "*"
965 972
966"@types/simple-peer@*": 973"@types/simple-peer@*":
967 version "9.11.0" 974 version "9.11.1"
968 resolved "https://registry.yarnpkg.com/@types/simple-peer/-/simple-peer-9.11.0.tgz#72f369cb2bebd0023b265aa726d352bf744f9f4b" 975 resolved "https://registry.yarnpkg.com/@types/simple-peer/-/simple-peer-9.11.1.tgz#bef6ff1e75178d83438e33aa6a4df2fd98fded1d"
969 integrity sha512-HjIGo3D5I2tdtl2FpngcDYHgqlDxZuZqmQn7f0TQrpYI4ZbuHbBg9THoiDjzKjCzZsHrG5Ag53m5O/cBYfjQWA== 976 integrity sha512-Pzqbau/WlivSXdRC0He2Wz/ANj2wbi4gzJrtysZz93jvOyI2jo/ibMjUe6AvPllFl/UO6QXT/A0Rcp44bDQB5A==
970 dependencies: 977 dependencies:
971 "@types/node" "*" 978 "@types/node" "*"
972 979
973"@types/superagent@*": 980"@types/superagent@*":
974 version "4.1.11" 981 version "4.1.12"
975 resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.11.tgz#4822bc64a82a0f579261a77097dbca276556c20e" 982 resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.12.tgz#fad68c6712936892ad24cf94f2f7a07cc749fd0f"
976 integrity sha512-cZkWBXZI+jESnUTp8RDGBmk1Zn2MkScP4V5bjD7DyqB7L0WNWpblh4KX5K/6aTqxFZMhfo1bhi2cwoAEDVBBJw== 983 integrity sha512-1GQvD6sySQPD6p9EopDFI3f5OogdICl1sU/2ij3Esobz/RtL9fWZZDPmsuv7eiy5ya+XNiPAxUcI3HIUTJa+3A==
977 dependencies: 984 dependencies:
978 "@types/cookiejar" "*" 985 "@types/cookiejar" "*"
979 "@types/node" "*" 986 "@types/node" "*"
@@ -986,24 +993,24 @@
986 "@types/superagent" "*" 993 "@types/superagent" "*"
987 994
988"@types/tough-cookie@*": 995"@types/tough-cookie@*":
989 version "4.0.0" 996 version "4.0.1"
990 resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d" 997 resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.1.tgz#8f80dd965ad81f3e1bc26d6f5c727e132721ff40"
991 integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A== 998 integrity sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg==
992 999
993"@types/tv4@*": 1000"@types/tv4@*":
994 version "1.2.30" 1001 version "1.2.31"
995 resolved "https://registry.yarnpkg.com/@types/tv4/-/tv4-1.2.30.tgz#3159a6fd5ac90c42d27aecfb20a6a150fb565668" 1002 resolved "https://registry.yarnpkg.com/@types/tv4/-/tv4-1.2.31.tgz#b33f3e6f082782a90f1fc5f414ad8722db8c9baa"
996 integrity sha512-Uj68uOn6T94IQsEGxLRrFiAqYsP4AUaXiYWrm+DR9/PNtIxuXaq/oHwBbGVR0uXHox4UZMKxCuf+z5M40MpoBw== 1003 integrity sha512-P97XU07fcpauSw3/fE2Q7eF6bHl4oHhwkikjnM7zlQLENrdC2rZuHSdNlMBhnW82NyBEsVJHII1Jk3d/MtQsQQ==
997 1004
998"@types/validator@^13.0.0": 1005"@types/validator@^13.0.0":
999 version "13.1.4" 1006 version "13.6.3"
1000 resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.1.4.tgz#d2e3c27523ce1b5d9dc13d16cbce65dc4db2adbe" 1007 resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.6.3.tgz#31ca2e997bf13a0fffca30a25747d5b9f7dbb7de"
1001 integrity sha512-19C02B8mr53HufY7S+HO/EHBD7a/R22IwEwyqiHaR19iwL37dN3o0M8RianVInfSSqP7InVSg/o0mUATM4JWsQ== 1008 integrity sha512-fWG42pMJOL4jKsDDZZREnXLjc3UE0R8LOJfARWYg6U966rxDT7TYejYzLnUF5cvSObGg34nd0+H2wHHU5Omdfw==
1002 1009
1003"@types/webtorrent@^0.109.0": 1010"@types/webtorrent@^0.109.0":
1004 version "0.109.0" 1011 version "0.109.1"
1005 resolved "https://registry.yarnpkg.com/@types/webtorrent/-/webtorrent-0.109.0.tgz#dd377691caf360317738f67fa0c3bce48623df57" 1012 resolved "https://registry.yarnpkg.com/@types/webtorrent/-/webtorrent-0.109.1.tgz#6ca843c3a6d442459b558ec25ce290437f204900"
1006 integrity sha512-c6EgbuFRZqhM4TMnloRuLAcR45j/Qn0kQ6CKWMppXXHfaQpspB1ZeeYx2Bpc22MAgCc3pjlAakTRS2h15stW2A== 1013 integrity sha512-yH0F7Ma9VI7Y1y02ZIOkDlS9WDoTFwesUGUFxjDEE6OFLlSqIKxgdHY72cigr7JHCwDm6uNQnCq+twz0SO6cTw==
1007 dependencies: 1014 dependencies:
1008 "@types/bittorrent-protocol" "*" 1015 "@types/bittorrent-protocol" "*"
1009 "@types/node" "*" 1016 "@types/node" "*"
@@ -1011,79 +1018,79 @@
1011 "@types/simple-peer" "*" 1018 "@types/simple-peer" "*"
1012 1019
1013"@types/ws@^7.2.1": 1020"@types/ws@^7.2.1":
1014 version "7.4.5" 1021 version "7.4.7"
1015 resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.5.tgz#8ff0f7efcd8fea19f51f9dd66cb8b498d172a752" 1022 resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702"
1016 integrity sha512-8mbDgtc8xpxDDem5Gwj76stBDJX35KQ3YBoayxlqUQcL5BZUthiqP/VQ4PQnLHqM4PmlbyO74t98eJpURO+gPA== 1023 integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==
1017 dependencies: 1024 dependencies:
1018 "@types/node" "*" 1025 "@types/node" "*"
1019 1026
1020"@typescript-eslint/eslint-plugin@^4.8.1": 1027"@typescript-eslint/eslint-plugin@^4.8.1":
1021 version "4.28.0" 1028 version "4.28.4"
1022 resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.0.tgz#1a66f03b264844387beb7dc85e1f1d403bd1803f" 1029 resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.4.tgz#e73c8cabbf3f08dee0e1bda65ed4e622ae8f8921"
1023 integrity sha512-KcF6p3zWhf1f8xO84tuBailV5cN92vhS+VT7UJsPzGBm9VnQqfI9AsiMUFUCYHTYPg1uCCo+HyiDnpDuvkAMfQ== 1030 integrity sha512-s1oY4RmYDlWMlcV0kKPBaADn46JirZzvvH7c2CtAqxCY96S538JRBAzt83RrfkDheV/+G/vWNK0zek+8TB3Gmw==
1024 dependencies: 1031 dependencies:
1025 "@typescript-eslint/experimental-utils" "4.28.0" 1032 "@typescript-eslint/experimental-utils" "4.28.4"
1026 "@typescript-eslint/scope-manager" "4.28.0" 1033 "@typescript-eslint/scope-manager" "4.28.4"
1027 debug "^4.3.1" 1034 debug "^4.3.1"
1028 functional-red-black-tree "^1.0.1" 1035 functional-red-black-tree "^1.0.1"
1029 regexpp "^3.1.0" 1036 regexpp "^3.1.0"
1030 semver "^7.3.5" 1037 semver "^7.3.5"
1031 tsutils "^3.21.0" 1038 tsutils "^3.21.0"
1032 1039
1033"@typescript-eslint/experimental-utils@4.28.0": 1040"@typescript-eslint/experimental-utils@4.28.4":
1034 version "4.28.0" 1041 version "4.28.4"
1035 resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.0.tgz#13167ed991320684bdc23588135ae62115b30ee0" 1042 resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.4.tgz#9c70c35ebed087a5c70fb0ecd90979547b7fec96"
1036 integrity sha512-9XD9s7mt3QWMk82GoyUpc/Ji03vz4T5AYlHF9DcoFNfJ/y3UAclRsfGiE2gLfXtyC+JRA3trR7cR296TEb1oiQ== 1043 integrity sha512-OglKWOQRWTCoqMSy6pm/kpinEIgdcXYceIcH3EKWUl4S8xhFtN34GQRaAvTIZB9DD94rW7d/U7tUg3SYeDFNHA==
1037 dependencies: 1044 dependencies:
1038 "@types/json-schema" "^7.0.7" 1045 "@types/json-schema" "^7.0.7"
1039 "@typescript-eslint/scope-manager" "4.28.0" 1046 "@typescript-eslint/scope-manager" "4.28.4"
1040 "@typescript-eslint/types" "4.28.0" 1047 "@typescript-eslint/types" "4.28.4"
1041 "@typescript-eslint/typescript-estree" "4.28.0" 1048 "@typescript-eslint/typescript-estree" "4.28.4"
1042 eslint-scope "^5.1.1" 1049 eslint-scope "^5.1.1"
1043 eslint-utils "^3.0.0" 1050 eslint-utils "^3.0.0"
1044 1051
1045"@typescript-eslint/parser@^4.0.0": 1052"@typescript-eslint/parser@^4.0.0":
1046 version "4.28.0" 1053 version "4.28.4"
1047 resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.0.tgz#2404c16751a28616ef3abab77c8e51d680a12caa" 1054 resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.4.tgz#bc462dc2779afeefdcf49082516afdc3e7b96fab"
1048 integrity sha512-7x4D22oPY8fDaOCvkuXtYYTQ6mTMmkivwEzS+7iml9F9VkHGbbZ3x4fHRwxAb5KeuSkLqfnYjs46tGx2Nour4A== 1055 integrity sha512-4i0jq3C6n+og7/uCHiE6q5ssw87zVdpUj1k6VlVYMonE3ILdFApEzTWgppSRG4kVNB/5jxnH+gTeKLMNfUelQA==
1049 dependencies: 1056 dependencies:
1050 "@typescript-eslint/scope-manager" "4.28.0" 1057 "@typescript-eslint/scope-manager" "4.28.4"
1051 "@typescript-eslint/types" "4.28.0" 1058 "@typescript-eslint/types" "4.28.4"
1052 "@typescript-eslint/typescript-estree" "4.28.0" 1059 "@typescript-eslint/typescript-estree" "4.28.4"
1053 debug "^4.3.1" 1060 debug "^4.3.1"
1054 1061
1055"@typescript-eslint/scope-manager@4.28.0": 1062"@typescript-eslint/scope-manager@4.28.4":
1056 version "4.28.0" 1063 version "4.28.4"
1057 resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.0.tgz#6a3009d2ab64a30fc8a1e257a1a320067f36a0ce" 1064 resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.4.tgz#bdbce9b6a644e34f767bd68bc17bb14353b9fe7f"
1058 integrity sha512-eCALCeScs5P/EYjwo6se9bdjtrh8ByWjtHzOkC4Tia6QQWtQr3PHovxh3TdYTuFcurkYI4rmFsRFpucADIkseg== 1065 integrity sha512-ZJBNs4usViOmlyFMt9X9l+X0WAFcDH7EdSArGqpldXu7aeZxDAuAzHiMAeI+JpSefY2INHrXeqnha39FVqXb8w==
1059 dependencies: 1066 dependencies:
1060 "@typescript-eslint/types" "4.28.0" 1067 "@typescript-eslint/types" "4.28.4"
1061 "@typescript-eslint/visitor-keys" "4.28.0" 1068 "@typescript-eslint/visitor-keys" "4.28.4"
1062 1069
1063"@typescript-eslint/types@4.28.0": 1070"@typescript-eslint/types@4.28.4":
1064 version "4.28.0" 1071 version "4.28.4"
1065 resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.0.tgz#a33504e1ce7ac51fc39035f5fe6f15079d4dafb0" 1072 resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.4.tgz#41acbd79b5816b7c0dd7530a43d97d020d3aeb42"
1066 integrity sha512-p16xMNKKoiJCVZY5PW/AfILw2xe1LfruTcfAKBj3a+wgNYP5I9ZEKNDOItoRt53p4EiPV6iRSICy8EPanG9ZVA== 1073 integrity sha512-3eap4QWxGqkYuEmVebUGULMskR6Cuoc/Wii0oSOddleP4EGx1tjLnZQ0ZP33YRoMDCs5O3j56RBV4g14T4jvww==
1067 1074
1068"@typescript-eslint/typescript-estree@4.28.0": 1075"@typescript-eslint/typescript-estree@4.28.4":
1069 version "4.28.0" 1076 version "4.28.4"
1070 resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.0.tgz#e66d4e5aa2ede66fec8af434898fe61af10c71cf" 1077 resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.4.tgz#252e6863278dc0727244be9e371eb35241c46d00"
1071 integrity sha512-m19UQTRtxMzKAm8QxfKpvh6OwQSXaW1CdZPoCaQuLwAq7VZMNuhJmZR4g5281s2ECt658sldnJfdpSZZaxUGMQ== 1078 integrity sha512-z7d8HK8XvCRyN2SNp+OXC2iZaF+O2BTquGhEYLKLx5k6p0r05ureUtgEfo5f6anLkhCxdHtCf6rPM1p4efHYDQ==
1072 dependencies: 1079 dependencies:
1073 "@typescript-eslint/types" "4.28.0" 1080 "@typescript-eslint/types" "4.28.4"
1074 "@typescript-eslint/visitor-keys" "4.28.0" 1081 "@typescript-eslint/visitor-keys" "4.28.4"
1075 debug "^4.3.1" 1082 debug "^4.3.1"
1076 globby "^11.0.3" 1083 globby "^11.0.3"
1077 is-glob "^4.0.1" 1084 is-glob "^4.0.1"
1078 semver "^7.3.5" 1085 semver "^7.3.5"
1079 tsutils "^3.21.0" 1086 tsutils "^3.21.0"
1080 1087
1081"@typescript-eslint/visitor-keys@4.28.0": 1088"@typescript-eslint/visitor-keys@4.28.4":
1082 version "4.28.0" 1089 version "4.28.4"
1083 resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.0.tgz#255c67c966ec294104169a6939d96f91c8a89434" 1090 resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.4.tgz#92dacfefccd6751cbb0a964f06683bfd72d0c4d3"
1084 integrity sha512-PjJyTWwrlrvM5jazxYF5ZPs/nl0kHDZMVbuIcbpawVXaDPelp3+S9zpOz5RmVUfS/fD5l5+ZXNKnWhNYjPzCvw== 1091 integrity sha512-NIAXAdbz1XdOuzqkJHjNKXKj8QQ4cv5cxR/g0uQhCYf/6//XrmfpaYsM7PnBcNbfvTDLUkqQ5TPNm1sozDdTWg==
1085 dependencies: 1092 dependencies:
1086 "@typescript-eslint/types" "4.28.0" 1093 "@typescript-eslint/types" "4.28.4"
1087 eslint-visitor-keys "^2.0.0" 1094 eslint-visitor-keys "^2.0.0"
1088 1095
1089"@ungap/promise-all-settled@1.1.2": 1096"@ungap/promise-all-settled@1.1.2":
@@ -1092,9 +1099,9 @@
1092 integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== 1099 integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
1093 1100
1094"@uploadx/core@^4.4.0": 1101"@uploadx/core@^4.4.0":
1095 version "4.4.0" 1102 version "4.4.2"
1096 resolved "https://registry.yarnpkg.com/@uploadx/core/-/core-4.4.0.tgz#27ea2b0d28125e81a6bdd65637dc5c7829306cc7" 1103 resolved "https://registry.yarnpkg.com/@uploadx/core/-/core-4.4.2.tgz#13220a449e3ab23ed324e4beaea04dd56e538b10"
1097 integrity sha512-dU0oDURYR5RvuAzf63EL9e/fCY4OOQKOs237UTbZDulbRbiyxwEZR+IpRYYr3hKRjjij03EF/Y5j54VGkebAKg== 1104 integrity sha512-wI9iWjuT+FDV/IjTj55xPs30MwiIMg4buoZlBnTiPAkgyxpwXi9F+6Zchjy2oE5Lfd/9enp0sQz/0RMfqVimAg==
1098 dependencies: 1105 dependencies:
1099 bytes "^3.1.0" 1106 bytes "^3.1.0"
1100 debug "^4.3.1" 1107 debug "^4.3.1"
@@ -1121,9 +1128,9 @@ accepts@~1.3.4, accepts@~1.3.7:
1121 negotiator "0.6.2" 1128 negotiator "0.6.2"
1122 1129
1123acorn-jsx@^5.3.1: 1130acorn-jsx@^5.3.1:
1124 version "5.3.1" 1131 version "5.3.2"
1125 resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" 1132 resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
1126 integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== 1133 integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
1127 1134
1128acorn@^7.1.1, acorn@^7.4.0: 1135acorn@^7.1.1, acorn@^7.4.0:
1129 version "7.4.1" 1136 version "7.4.1"
@@ -1163,9 +1170,9 @@ ajv@^6.10.0, ajv@^6.12.4:
1163 uri-js "^4.2.2" 1170 uri-js "^4.2.2"
1164 1171
1165ajv@^8.0.1: 1172ajv@^8.0.1:
1166 version "8.6.0" 1173 version "8.6.2"
1167 resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.0.tgz#60cc45d9c46a477d80d92c48076d972c342e5720" 1174 resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.2.tgz#2fb45e0e5fcbc0813326c1c3da535d1881bb0571"
1168 integrity sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ== 1175 integrity sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==
1169 dependencies: 1176 dependencies:
1170 fast-deep-equal "^3.1.1" 1177 fast-deep-equal "^3.1.1"
1171 json-schema-traverse "^1.0.0" 1178 json-schema-traverse "^1.0.0"
@@ -1235,7 +1242,7 @@ any-promise@^1.3.0:
1235 resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" 1242 resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
1236 integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= 1243 integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
1237 1244
1238anymatch@~3.1.1, anymatch@~3.1.2: 1245anymatch@~3.1.2:
1239 version "3.1.2" 1246 version "3.1.2"
1240 resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" 1247 resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
1241 integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== 1248 integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
@@ -1243,11 +1250,6 @@ anymatch@~3.1.1, anymatch@~3.1.2:
1243 normalize-path "^3.0.0" 1250 normalize-path "^3.0.0"
1244 picomatch "^2.0.4" 1251 picomatch "^2.0.4"
1245 1252
1246apicache@1.6.2:
1247 version "1.6.2"
1248 resolved "https://registry.yarnpkg.com/apicache/-/apicache-1.6.2.tgz#a0a3d51024fa2814c4ace7e9e7053ebcb0920ee6"
1249 integrity sha512-3z5e+1E2qwZoqzFVgdx5l9nGhSG0kHv3v2G170vnJSz5uj4mCLVZfRw0o37aWwV8pTPXSkB8OBZz3TIur4H26g==
1250
1251append-field@^1.0.0: 1253append-field@^1.0.0:
1252 version "1.0.0" 1254 version "1.0.0"
1253 resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" 1255 resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56"
@@ -1408,9 +1410,9 @@ asynckit@^0.4.0:
1408 integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= 1410 integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
1409 1411
1410autocannon@^7.0.4: 1412autocannon@^7.0.4:
1411 version "7.3.0" 1413 version "7.4.0"
1412 resolved "https://registry.yarnpkg.com/autocannon/-/autocannon-7.3.0.tgz#8e52bb3f07926b573dcf401d3fe365393fbab808" 1414 resolved "https://registry.yarnpkg.com/autocannon/-/autocannon-7.4.0.tgz#7e3ea188501d60162b7a0167c1d9bf90db870c2e"
1413 integrity sha512-RuyTU8fQE1FC6BDgslofLCeI4y9y9RRnnZtvoQ+onwQl+B2rsiGpcCi84/Si0rq3VRkkMFnmPulY3z59zYhX9g== 1415 integrity sha512-X0g/nkJ7oHkfn/B+LUzz9jPjG4nD48tPnu2E24m7LA7p8+Mvd/Clrb+FnHmPgI7pszXPRtzUYWz6QrSyMiEY6w==
1414 dependencies: 1416 dependencies:
1415 chalk "^4.1.0" 1417 chalk "^4.1.0"
1416 char-spinner "^1.0.1" 1418 char-spinner "^1.0.1"
@@ -1575,10 +1577,10 @@ bitfield@^4.0.0:
1575 resolved "https://registry.yarnpkg.com/bitfield/-/bitfield-4.0.0.tgz#3094123c870030dc6198a283d779639bd2a8e256" 1577 resolved "https://registry.yarnpkg.com/bitfield/-/bitfield-4.0.0.tgz#3094123c870030dc6198a283d779639bd2a8e256"
1576 integrity sha512-jtuSG9CQr5yoHFuvhgf50+DH8Aezl3C/mMSfqdG4DqP7Kqe34uBUtCEHPN9oWaldTm96/i7y5e778SnM5ES4rw== 1578 integrity sha512-jtuSG9CQr5yoHFuvhgf50+DH8Aezl3C/mMSfqdG4DqP7Kqe34uBUtCEHPN9oWaldTm96/i7y5e778SnM5ES4rw==
1577 1579
1578bittorrent-dht@^10.0.0: 1580bittorrent-dht@^10.0.0, bittorrent-dht@^10.0.1:
1579 version "10.0.0" 1581 version "10.0.1"
1580 resolved "https://registry.yarnpkg.com/bittorrent-dht/-/bittorrent-dht-10.0.0.tgz#01de59bb03ed86a8847cb533134925d236d7f565" 1582 resolved "https://registry.yarnpkg.com/bittorrent-dht/-/bittorrent-dht-10.0.1.tgz#ff2efe77cdb4d72c819f46b42a162f42ca233793"
1581 integrity sha512-mrM18HMabvd3n/hQa4PYe942nWvBsJCBQb5PfT9kUJLlspNPGiulZYSCgWs7+XarS7nufYrGEp07f9eKTKIrgw== 1583 integrity sha512-aR0vAgm+SgLiwTCEtNgeuqtT2deg+E/xHCTb7iryikvLbqbR58oFHbNYX4CM6EzyNGSKfcdBKp1gWI5Gcn2Aaw==
1582 dependencies: 1584 dependencies:
1583 bencode "^2.0.0" 1585 bencode "^2.0.0"
1584 debug "^4.1.1" 1586 debug "^4.1.1"
@@ -1603,14 +1605,13 @@ bittorrent-peerid@^1.3.3:
1603 resolved "https://registry.yarnpkg.com/bittorrent-peerid/-/bittorrent-peerid-1.3.3.tgz#b8dc79e421f8136d2ffd0b163a18e9d70da09949" 1605 resolved "https://registry.yarnpkg.com/bittorrent-peerid/-/bittorrent-peerid-1.3.3.tgz#b8dc79e421f8136d2ffd0b163a18e9d70da09949"
1604 integrity sha512-tSh9HdQgwyEAfo1jzoGEis6o/zs4CcdRTchG93XVl5jct+DCAN90M5MVUV76k2vJ9Xg3GAzLB5NLsY/vnVTh6w== 1606 integrity sha512-tSh9HdQgwyEAfo1jzoGEis6o/zs4CcdRTchG93XVl5jct+DCAN90M5MVUV76k2vJ9Xg3GAzLB5NLsY/vnVTh6w==
1605 1607
1606bittorrent-protocol@^3.3.1: 1608bittorrent-protocol@^3.4.2:
1607 version "3.4.1" 1609 version "3.4.2"
1608 resolved "https://registry.yarnpkg.com/bittorrent-protocol/-/bittorrent-protocol-3.4.1.tgz#b481d09dbf910fa7fcca5f06a7c1c4246151d4d1" 1610 resolved "https://registry.yarnpkg.com/bittorrent-protocol/-/bittorrent-protocol-3.4.2.tgz#e5194d1acd30273ac02bb272c208977716d0394b"
1609 integrity sha512-3qBW4ZZrUZKN7HzHbX4+kbiphrTNeraMp3i9n3wobicysjibAV8SBDY+sGiBN4SgXV6WvEW4kyRPIjoSqW+khw== 1611 integrity sha512-a7ueJzmCImWIXfKrJ+dT6mgqi5+LFByAXoMXhV/cYt/y8kplaC8N9ZWfpiTidJY4H2o1GTsyMy73o62a/rZ0Ow==
1610 dependencies: 1612 dependencies:
1611 bencode "^2.0.1" 1613 bencode "^2.0.1"
1612 bitfield "^4.0.0" 1614 bitfield "^4.0.0"
1613 buffer-xor "^2.0.2"
1614 debug "^4.3.1" 1615 debug "^4.3.1"
1615 randombytes "^2.1.0" 1616 randombytes "^2.1.0"
1616 rc4 "^0.1.5" 1617 rc4 "^0.1.5"
@@ -1620,9 +1621,9 @@ bittorrent-protocol@^3.3.1:
1620 unordered-array-remove "^1.0.2" 1621 unordered-array-remove "^1.0.2"
1621 1622
1622bittorrent-tracker@^9.0.0: 1623bittorrent-tracker@^9.0.0:
1623 version "9.17.2" 1624 version "9.17.4"
1624 resolved "https://registry.yarnpkg.com/bittorrent-tracker/-/bittorrent-tracker-9.17.2.tgz#1afb02d3d2fb474c13389c45e8a2b6919bff40bd" 1625 resolved "https://registry.yarnpkg.com/bittorrent-tracker/-/bittorrent-tracker-9.17.4.tgz#663f51064a924e945cb6ca19a0c293aca258128b"
1625 integrity sha512-hXjed0OnB16da+ScJUZnrAZbf9gMgSLKqh5rJebtYnTRgN4o1mX0DOPH3Nf5RFCs935ibhSmZN5nwbkh+3MdEA== 1626 integrity sha512-ykhdVQHtLfn4DYSJUQD/zFAbP8YwnF6nGlj2SBnCY4xkW5bhwXPeFZUhryAtdITl0qNL/FpmFOamBZfxIwkbxg==
1626 dependencies: 1627 dependencies:
1627 bencode "^2.0.1" 1628 bencode "^2.0.1"
1628 bittorrent-peerid "^1.3.3" 1629 bittorrent-peerid "^1.3.3"
@@ -1793,13 +1794,6 @@ buffer-writer@2.0.0:
1793 resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" 1794 resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04"
1794 integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw== 1795 integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==
1795 1796
1796buffer-xor@^2.0.2:
1797 version "2.0.2"
1798 resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-2.0.2.tgz#34f7c64f04c777a1f8aac5e661273bb9dd320289"
1799 integrity sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ==
1800 dependencies:
1801 safe-buffer "^5.1.1"
1802
1803buffer@^5.2.0, buffer@^5.5.0: 1797buffer@^5.2.0, buffer@^5.5.0:
1804 version "5.7.1" 1798 version "5.7.1"
1805 resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" 1799 resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
@@ -1824,9 +1818,9 @@ bufferutil@^4.0.3:
1824 node-gyp-build "^4.2.0" 1818 node-gyp-build "^4.2.0"
1825 1819
1826bull@^3.4.2: 1820bull@^3.4.2:
1827 version "3.22.9" 1821 version "3.26.0"
1828 resolved "https://registry.yarnpkg.com/bull/-/bull-3.22.9.tgz#9d86493e1bb4afeb7e6e259c45141185bd78111d" 1822 resolved "https://registry.yarnpkg.com/bull/-/bull-3.26.0.tgz#c6198cf4f3a2fa5f3044cbe462b452c77a3df94f"
1829 integrity sha512-waVaXkjS1+aPxZpmqcKKQX1KeWx7uZXcg9bhe+y5xx/3k8Xu0vqUL1FxMUfp0f3O4KSAHk1EDlD5DGXxBlFDFQ== 1823 integrity sha512-W1ohwMBApLW9dhKHEwgzr8YnpScTOGC9KtKP2DrvjnWTQFWbaEnKlrDHKp3SJwvAB0C3jDsO579O/Hys/UmAiQ==
1830 dependencies: 1824 dependencies:
1831 cron-parser "^2.13.0" 1825 cron-parser "^2.13.0"
1832 debuglog "^1.0.0" 1826 debuglog "^1.0.0"
@@ -1852,6 +1846,14 @@ bytes@3.1.0, bytes@^3.0.0, bytes@^3.1.0:
1852 resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" 1846 resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
1853 integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== 1847 integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
1854 1848
1849cache-chunk-store@^3.2.2:
1850 version "3.2.2"
1851 resolved "https://registry.yarnpkg.com/cache-chunk-store/-/cache-chunk-store-3.2.2.tgz#19bb55d61252cd2174da4686548d52bc2dd44120"
1852 integrity sha512-2lJdWbgHFFxcSth9s2wpId3CR3v1YC63KjP4T9WhpW7LWlY7Hiiei3QwwqzkWqlJTfR8lSy9F5kRQECeyj+yQA==
1853 dependencies:
1854 lru "^3.1.0"
1855 queue-microtask "^1.2.3"
1856
1855cacheable-lookup@^5.0.3: 1857cacheable-lookup@^5.0.3:
1856 version "5.0.4" 1858 version "5.0.4"
1857 resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" 1859 resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005"
@@ -2043,22 +2045,7 @@ cheerio@^1.0.0-rc.3:
2043 parse5-htmlparser2-tree-adapter "^6.0.1" 2045 parse5-htmlparser2-tree-adapter "^6.0.1"
2044 tslib "^2.2.0" 2046 tslib "^2.2.0"
2045 2047
2046chokidar@3.5.1: 2048chokidar@3.5.2, chokidar@^3.2.2, chokidar@^3.4.2:
2047 version "3.5.1"
2048 resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a"
2049 integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==
2050 dependencies:
2051 anymatch "~3.1.1"
2052 braces "~3.0.2"
2053 glob-parent "~5.1.0"
2054 is-binary-path "~2.1.0"
2055 is-glob "~4.0.1"
2056 normalize-path "~3.0.0"
2057 readdirp "~3.5.0"
2058 optionalDependencies:
2059 fsevents "~2.3.1"
2060
2061chokidar@^3.2.2, chokidar@^3.4.2:
2062 version "3.5.2" 2049 version "3.5.2"
2063 resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" 2050 resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
2064 integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== 2051 integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
@@ -2235,9 +2222,9 @@ color-name@^1.0.0, color-name@~1.1.4:
2235 integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 2222 integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
2236 2223
2237color-string@^1.5.2: 2224color-string@^1.5.2:
2238 version "1.5.5" 2225 version "1.6.0"
2239 resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014" 2226 resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.6.0.tgz#c3915f61fe267672cb7e1e064c9d692219f6c312"
2240 integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg== 2227 integrity sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==
2241 dependencies: 2228 dependencies:
2242 color-name "^1.0.0" 2229 color-name "^1.0.0"
2243 simple-swizzle "^0.2.2" 2230 simple-swizzle "^0.2.2"
@@ -2607,9 +2594,9 @@ dateformat@^3.0.3:
2607 integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== 2594 integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
2608 2595
2609dayjs@^1.10.4: 2596dayjs@^1.10.4:
2610 version "1.10.5" 2597 version "1.10.6"
2611 resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.5.tgz#5600df4548fc2453b3f163ebb2abbe965ccfb986" 2598 resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.6.tgz#288b2aa82f2d8418a6c9d4df5898c0737ad02a63"
2612 integrity sha512-BUFis41ikLz+65iH6LHQCDm4YPMj5r1YFLdupPIyM4SGcXMmtiLQ7U37i+hGS8urIuqe7I/ou3IS1jVc4nbN4g== 2599 integrity sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw==
2613 2600
2614debug@2.6.9, debug@^2.2.0, debug@^2.6.9: 2601debug@2.6.9, debug@^2.2.0, debug@^2.6.9:
2615 version "2.6.9" 2602 version "2.6.9"
@@ -2618,7 +2605,14 @@ debug@2.6.9, debug@^2.2.0, debug@^2.6.9:
2618 dependencies: 2605 dependencies:
2619 ms "2.0.0" 2606 ms "2.0.0"
2620 2607
2621debug@4, debug@4.3.1, debug@^4.0.0, debug@^4.0.1, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@~4.3.1: 2608debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@~4.3.1:
2609 version "4.3.2"
2610 resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
2611 integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
2612 dependencies:
2613 ms "2.1.2"
2614
2615debug@4.3.1:
2622 version "4.3.1" 2616 version "4.3.1"
2623 resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" 2617 resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
2624 integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== 2618 integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
@@ -2978,7 +2972,7 @@ engine.io-client@~3.3.1:
2978 xmlhttprequest-ssl "~1.5.4" 2972 xmlhttprequest-ssl "~1.5.4"
2979 yeast "0.1.2" 2973 yeast "0.1.2"
2980 2974
2981engine.io-client@~5.1.1: 2975engine.io-client@~5.1.2:
2982 version "5.1.2" 2976 version "5.1.2"
2983 resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-5.1.2.tgz#27108da9b39ae03262443d945caf2caa3655c4cb" 2977 resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-5.1.2.tgz#27108da9b39ae03262443d945caf2caa3655c4cb"
2984 integrity sha512-blRrgXIE0A/eurWXRzvfCLG7uUFJqfTGFsyJzXSK71srMMGJ2VraBLg8Mdw28uUxSpVicepBN9X7asqpD1mZcQ== 2978 integrity sha512-blRrgXIE0A/eurWXRzvfCLG7uUFJqfTGFsyJzXSK71srMMGJ2VraBLg8Mdw28uUxSpVicepBN9X7asqpD1mZcQ==
@@ -3023,7 +3017,7 @@ engine.io@~3.3.1:
3023 engine.io-parser "~2.1.0" 3017 engine.io-parser "~2.1.0"
3024 ws "~6.1.0" 3018 ws "~6.1.0"
3025 3019
3026engine.io@~5.1.0: 3020engine.io@~5.1.1:
3027 version "5.1.1" 3021 version "5.1.1"
3028 resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-5.1.1.tgz#a1f97e51ddf10cbd4db8b5ff4b165aad3760cdd3" 3022 resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-5.1.1.tgz#a1f97e51ddf10cbd4db8b5ff4b165aad3760cdd3"
3029 integrity sha512-aMWot7H5aC8L4/T8qMYbLdvKlZOdJTH54FxfdFunTGvhMx1BHkJOntWArsVfgAZVwAO9LC2sryPWRcEeUzCe5w== 3023 integrity sha512-aMWot7H5aC8L4/T8qMYbLdvKlZOdJTH54FxfdFunTGvhMx1BHkJOntWArsVfgAZVwAO9LC2sryPWRcEeUzCe5w==
@@ -3280,12 +3274,13 @@ eslint-visitor-keys@^2.0.0:
3280 integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== 3274 integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
3281 3275
3282eslint@^7.2.0: 3276eslint@^7.2.0:
3283 version "7.29.0" 3277 version "7.31.0"
3284 resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.29.0.tgz#ee2a7648f2e729485e4d0bd6383ec1deabc8b3c0" 3278 resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.31.0.tgz#f972b539424bf2604907a970860732c5d99d3aca"
3285 integrity sha512-82G/JToB9qIy/ArBzIWG9xvvwL3R86AlCjtGw+A29OMZDqhTybz/MByORSukGxeI+YPCR4coYyITKk8BFH9nDA== 3279 integrity sha512-vafgJpSh2ia8tnTkNUkwxGmnumgckLh5aAbLa1xRmIn9+owi8qBNGKL+B881kNKNTy7FFqTEkpNkUvmw0n6PkA==
3286 dependencies: 3280 dependencies:
3287 "@babel/code-frame" "7.12.11" 3281 "@babel/code-frame" "7.12.11"
3288 "@eslint/eslintrc" "^0.4.2" 3282 "@eslint/eslintrc" "^0.4.3"
3283 "@humanwhocodes/config-array" "^0.5.0"
3289 ajv "^6.10.0" 3284 ajv "^6.10.0"
3290 chalk "^4.0.0" 3285 chalk "^4.0.0"
3291 cross-spawn "^7.0.2" 3286 cross-spawn "^7.0.2"
@@ -3424,9 +3419,9 @@ exif-parser@^0.1.12:
3424 integrity sha1-WKnS1ywCwfbwKg70qRZicrd2CSI= 3419 integrity sha1-WKnS1ywCwfbwKg70qRZicrd2CSI=
3425 3420
3426express-rate-limit@^5.0.0: 3421express-rate-limit@^5.0.0:
3427 version "5.2.6" 3422 version "5.3.0"
3428 resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-5.2.6.tgz#b454e1be8a252081bda58460e0a25bf43ee0f7b0" 3423 resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-5.3.0.tgz#e7b9d3c2e09ece6e0406a869b2ce00d03fe48aea"
3429 integrity sha512-nE96xaxGfxiS5jP3tD3kIW1Jg9yQgX0rXCs3rCkZtmbWHEGyotwaezkLj7bnB41Z0uaOLM8W4AX6qHao4IZ2YA== 3424 integrity sha512-qJhfEgCnmteSeZAeuOKQ2WEIFTX5ajrzE0xS6gCOBCoRQcU+xEzQmgYQQTpzCcqUAAzTEtu4YEih4pnLfvNtew==
3430 3425
3431express-validator@^6.4.0: 3426express-validator@^6.4.0:
3432 version "6.12.0" 3427 version "6.12.0"
@@ -3514,16 +3509,15 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
3514 integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 3509 integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
3515 3510
3516fast-glob@^3.1.1: 3511fast-glob@^3.1.1:
3517 version "3.2.5" 3512 version "3.2.7"
3518 resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" 3513 resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1"
3519 integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== 3514 integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==
3520 dependencies: 3515 dependencies:
3521 "@nodelib/fs.stat" "^2.0.2" 3516 "@nodelib/fs.stat" "^2.0.2"
3522 "@nodelib/fs.walk" "^1.2.3" 3517 "@nodelib/fs.walk" "^1.2.3"
3523 glob-parent "^5.1.0" 3518 glob-parent "^5.1.2"
3524 merge2 "^1.3.0" 3519 merge2 "^1.3.0"
3525 micromatch "^4.0.2" 3520 micromatch "^4.0.4"
3526 picomatch "^2.2.1"
3527 3521
3528fast-json-stable-stringify@^2.0.0: 3522fast-json-stable-stringify@^2.0.0:
3529 version "2.1.0" 3523 version "2.1.0"
@@ -3535,20 +3529,25 @@ fast-levenshtein@^2.0.6:
3535 resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 3529 resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
3536 integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= 3530 integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
3537 3531
3538fast-safe-stringify@2.0.7, fast-safe-stringify@^2.0.4, fast-safe-stringify@^2.0.7: 3532fast-safe-stringify@2.0.7:
3539 version "2.0.7" 3533 version "2.0.7"
3540 resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" 3534 resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
3541 integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== 3535 integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
3542 3536
3537fast-safe-stringify@^2.0.4, fast-safe-stringify@^2.0.7:
3538 version "2.0.8"
3539 resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.8.tgz#dc2af48c46cf712b683e849b2bbd446b32de936f"
3540 integrity sha512-lXatBjf3WPjmWD6DpIZxkeSsCOwqI0maYMpgDlx8g4U2qi4lbjA9oH/HD2a87G+KfsUmo5WbJFmqBZlPxtptag==
3541
3543fast-xml-parser@^3.19.0: 3542fast-xml-parser@^3.19.0:
3544 version "3.19.0" 3543 version "3.19.0"
3545 resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01" 3544 resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01"
3546 integrity sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg== 3545 integrity sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg==
3547 3546
3548fastq@^1.6.0: 3547fastq@^1.6.0:
3549 version "1.11.0" 3548 version "1.11.1"
3550 resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" 3549 resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807"
3551 integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== 3550 integrity sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==
3552 dependencies: 3551 dependencies:
3553 reusify "^1.0.4" 3552 reusify "^1.0.4"
3554 3553
@@ -3653,9 +3652,9 @@ flat@^5.0.0, flat@^5.0.2:
3653 integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== 3652 integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
3654 3653
3655flatted@^3.1.0: 3654flatted@^3.1.0:
3656 version "3.1.1" 3655 version "3.2.1"
3657 resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" 3656 resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.1.tgz#bbef080d95fca6709362c73044a1634f7c6e7d05"
3658 integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== 3657 integrity sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==
3659 3658
3660fluent-ffmpeg@^2.1.0: 3659fluent-ffmpeg@^2.1.0:
3661 version "2.1.2" 3660 version "2.1.2"
@@ -3762,7 +3761,7 @@ fs.realpath@^1.0.0:
3762 resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 3761 resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
3763 integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 3762 integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
3764 3763
3765fsevents@~2.3.1, fsevents@~2.3.2: 3764fsevents@~2.3.2:
3766 version "2.3.2" 3765 version "2.3.2"
3767 resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 3766 resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
3768 integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 3767 integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
@@ -3866,7 +3865,7 @@ gifwrap@^0.9.2:
3866 image-q "^1.1.1" 3865 image-q "^1.1.1"
3867 omggif "^1.0.10" 3866 omggif "^1.0.10"
3868 3867
3869glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2: 3868glob-parent@^5.1.2, glob-parent@~5.1.2:
3870 version "5.1.2" 3869 version "5.1.2"
3871 resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 3870 resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
3872 integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 3871 integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
@@ -3913,9 +3912,9 @@ global@~4.4.0:
3913 process "^0.11.10" 3912 process "^0.11.10"
3914 3913
3915globals@^13.6.0, globals@^13.9.0: 3914globals@^13.6.0, globals@^13.9.0:
3916 version "13.9.0" 3915 version "13.10.0"
3917 resolved "https://registry.yarnpkg.com/globals/-/globals-13.9.0.tgz#4bf2bf635b334a173fb1daf7c5e6b218ecdc06cb" 3916 resolved "https://registry.yarnpkg.com/globals/-/globals-13.10.0.tgz#60ba56c3ac2ca845cfbf4faeca727ad9dd204676"
3918 integrity sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA== 3917 integrity sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==
3919 dependencies: 3918 dependencies:
3920 type-fest "^0.20.2" 3919 type-fest "^0.20.2"
3921 3920
@@ -4198,11 +4197,11 @@ human-signals@^2.1.0:
4198 integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== 4197 integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
4199 4198
4200hyperid@^2.0.3: 4199hyperid@^2.0.3:
4201 version "2.1.0" 4200 version "2.3.1"
4202 resolved "https://registry.yarnpkg.com/hyperid/-/hyperid-2.1.0.tgz#2f5ed7537e87e8fddd344710a610be501b3d2da6" 4201 resolved "https://registry.yarnpkg.com/hyperid/-/hyperid-2.3.1.tgz#70cc2c917b6367c9f7307718be243bc28b258353"
4203 integrity sha512-cSakhxbUsaIuqjfvvcUuvl/Fl342J65xgLLYrYxSSr9qmJ/EJK+S8crS6mIlQd/a7i+Pe4D0MgSrtZPLze+aCw== 4202 integrity sha512-mIbI7Ymn6MCdODaW1/6wdf5lvvXzmPsARN4zTLakMmcziBOuP4PxCBJvHF6kbAIHX6H4vAELx/pDmt0j6Th5RQ==
4204 dependencies: 4203 dependencies:
4205 uuid "^3.4.0" 4204 uuid "^8.3.2"
4206 uuid-parse "^1.1.0" 4205 uuid-parse "^1.1.0"
4207 4206
4208i18n-locales@^0.0.5: 4207i18n-locales@^0.0.5:
@@ -4336,10 +4335,10 @@ ini@~1.3.0:
4336 resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" 4335 resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
4337 integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== 4336 integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
4338 4337
4339inquirer@8.1.1: 4338inquirer@8.1.2:
4340 version "8.1.1" 4339 version "8.1.2"
4341 resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.1.1.tgz#7c53d94c6d03011c7bb2a947f0dca3b98246c26a" 4340 resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.1.2.tgz#65b204d2cd7fb63400edd925dfe428bafd422e3d"
4342 integrity sha512-hUDjc3vBkh/uk1gPfMAD/7Z188Q8cvTGl0nxwaCdwSbzFh6ZKkZh+s2ozVxbE5G9ZNRyeY0+lgbAIOUFsFf98w== 4341 integrity sha512-DHLKJwLPNgkfwNmsuEUKSejJFbkv0FMO9SMiQbjI3n5NQuCrSIBqP66ggqyz2a6t2qEolKrMjhQ3+W/xXgUQ+Q==
4343 dependencies: 4342 dependencies:
4344 ansi-escapes "^4.2.1" 4343 ansi-escapes "^4.2.1"
4345 chalk "^4.1.1" 4344 chalk "^4.1.1"
@@ -4351,7 +4350,7 @@ inquirer@8.1.1:
4351 mute-stream "0.0.8" 4350 mute-stream "0.0.8"
4352 ora "^5.3.0" 4351 ora "^5.3.0"
4353 run-async "^2.4.0" 4352 run-async "^2.4.0"
4354 rxjs "^6.6.6" 4353 rxjs "^7.2.0"
4355 string-width "^4.1.0" 4354 string-width "^4.1.0"
4356 strip-ansi "^6.0.0" 4355 strip-ansi "^6.0.0"
4357 through "^2.3.6" 4356 through "^2.3.6"
@@ -4468,9 +4467,9 @@ is-cidr@^4.0.0:
4468 cidr-regex "^3.1.1" 4467 cidr-regex "^3.1.1"
4469 4468
4470is-core-module@^2.2.0, is-core-module@^2.4.0: 4469is-core-module@^2.2.0, is-core-module@^2.4.0:
4471 version "2.4.0" 4470 version "2.5.0"
4472 resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" 4471 resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491"
4473 integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== 4472 integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==
4474 dependencies: 4473 dependencies:
4475 has "^1.0.3" 4474 has "^1.0.3"
4476 4475
@@ -4785,14 +4784,7 @@ json-stable-stringify-without-jsonify@^1.0.1:
4785 resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" 4784 resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
4786 integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= 4785 integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
4787 4786
4788json5@^1.0.1: 4787json5@^2.1.1, json5@^2.2.0:
4789 version "1.0.1"
4790 resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
4791 integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
4792 dependencies:
4793 minimist "^1.2.0"
4794
4795json5@^2.1.1:
4796 version "2.2.0" 4788 version "2.2.0"
4797 resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" 4789 resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
4798 integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== 4790 integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
@@ -5252,9 +5244,9 @@ markdown-it-emoji@^2.0.0:
5252 integrity sha512-39j7/9vP/CPCKbEI44oV8yoPJTpvfeReTn/COgRhSpNrjWF3PfP/JUxxB0hxV6ynOY8KH8Y8aX9NMDdo6z+6YQ== 5244 integrity sha512-39j7/9vP/CPCKbEI44oV8yoPJTpvfeReTn/COgRhSpNrjWF3PfP/JUxxB0hxV6ynOY8KH8Y8aX9NMDdo6z+6YQ==
5253 5245
5254markdown-it@^12.0.4: 5246markdown-it@^12.0.4:
5255 version "12.0.6" 5247 version "12.1.0"
5256 resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.0.6.tgz#adcc8e5fe020af292ccbdf161fe84f1961516138" 5248 resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.1.0.tgz#7ad572caddd336bd27a68d20e86bac1fafe8fb20"
5257 integrity sha512-qv3sVLl4lMT96LLtR7xeRJX11OUFjsaD5oVat2/SNBIb21bJXwal2+SklcRbTwGwqWpWH/HRtYavOoJE+seL8w== 5249 integrity sha512-7temG6IFOOxfU0SgzhqR+vr2diuMhyO5uUIEZ3C5NbXhqC9uFUHoU41USYuDFoZRsaY7BEIEei874Z20VMLF6A==
5258 dependencies: 5250 dependencies:
5259 argparse "^2.0.1" 5251 argparse "^2.0.1"
5260 entities "~2.1.0" 5252 entities "~2.1.0"
@@ -5268,9 +5260,9 @@ marked-man@^0.7.0:
5268 integrity sha512-zxK5E4jbuARALc+fIUAanM2njVGnrd9YvKrqoDHUg2XwNLJijo39EzMIg59LecHBHsIHNtPqepqnJp4SmL/EVg== 5260 integrity sha512-zxK5E4jbuARALc+fIUAanM2njVGnrd9YvKrqoDHUg2XwNLJijo39EzMIg59LecHBHsIHNtPqepqnJp4SmL/EVg==
5269 5261
5270marked@^2.0.1: 5262marked@^2.0.1:
5271 version "2.1.2" 5263 version "2.1.3"
5272 resolved "https://registry.yarnpkg.com/marked/-/marked-2.1.2.tgz#59579e17b02443312caa1509994d5a0b18ae38e1" 5264 resolved "https://registry.yarnpkg.com/marked/-/marked-2.1.3.tgz#bd017cef6431724fd4b27e0657f5ceb14bff3753"
5273 integrity sha512-ueJhIvklJJw04qxQbGIAu63EXwwOCYc7yKMBjgagTM4rjC5QtWyqSNgW7jCosV1/Km/1TUfs5qEpAqcGG0Mo5g== 5265 integrity sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==
5274 5266
5275math-interval-parser@^2.0.1: 5267math-interval-parser@^2.0.1:
5276 version "2.0.1" 5268 version "2.0.1"
@@ -5385,7 +5377,7 @@ methods@^1.1.2, methods@~1.1.2:
5385 resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 5377 resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
5386 integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= 5378 integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
5387 5379
5388micromatch@^4.0.2: 5380micromatch@^4.0.4:
5389 version "4.0.4" 5381 version "4.0.4"
5390 resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" 5382 resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9"
5391 integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== 5383 integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==
@@ -5495,14 +5487,14 @@ mkdirp@^1.0.3, mkdirp@~1.0.4:
5495 integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== 5487 integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
5496 5488
5497mocha@^9.0.0: 5489mocha@^9.0.0:
5498 version "9.0.1" 5490 version "9.0.2"
5499 resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.0.1.tgz#01e66b7af0012330c0a38c4b6eaa6d92b8a81bf9" 5491 resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.0.2.tgz#e84849b61f406a680ced85af76425f6f3108d1a0"
5500 integrity sha512-9zwsavlRO+5csZu6iRtl3GHImAbhERoDsZwdRkdJ/bE+eVplmoxNKE901ZJ9LdSchYBjSCPbjKc5XvcAri2ylw== 5492 integrity sha512-FpspiWU+UT9Sixx/wKimvnpkeW0mh6ROAKkIaPokj3xZgxeRhcna/k5X57jJghEr8X+Cgu/Vegf8zCX5ugSuTA==
5501 dependencies: 5493 dependencies:
5502 "@ungap/promise-all-settled" "1.1.2" 5494 "@ungap/promise-all-settled" "1.1.2"
5503 ansi-colors "4.1.1" 5495 ansi-colors "4.1.1"
5504 browser-stdout "1.3.1" 5496 browser-stdout "1.3.1"
5505 chokidar "3.5.1" 5497 chokidar "3.5.2"
5506 debug "4.3.1" 5498 debug "4.3.1"
5507 diff "5.0.0" 5499 diff "5.0.0"
5508 escape-string-regexp "4.0.0" 5500 escape-string-regexp "4.0.0"
@@ -5515,12 +5507,12 @@ mocha@^9.0.0:
5515 minimatch "3.0.4" 5507 minimatch "3.0.4"
5516 ms "2.1.3" 5508 ms "2.1.3"
5517 nanoid "3.1.23" 5509 nanoid "3.1.23"
5518 serialize-javascript "5.0.1" 5510 serialize-javascript "6.0.0"
5519 strip-json-comments "3.1.1" 5511 strip-json-comments "3.1.1"
5520 supports-color "8.1.1" 5512 supports-color "8.1.1"
5521 which "2.0.2" 5513 which "2.0.2"
5522 wide-align "1.1.3" 5514 wide-align "1.1.3"
5523 workerpool "6.1.4" 5515 workerpool "6.1.5"
5524 yargs "16.2.0" 5516 yargs "16.2.0"
5525 yargs-parser "20.2.4" 5517 yargs-parser "20.2.4"
5526 yargs-unparser "2.0.0" 5518 yargs-unparser "2.0.0"
@@ -5711,15 +5703,16 @@ node-gyp-build@^4.2.0:
5711 integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== 5703 integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==
5712 5704
5713node-media-server@^2.1.4: 5705node-media-server@^2.1.4:
5714 version "2.2.8" 5706 version "2.3.8"
5715 resolved "https://registry.yarnpkg.com/node-media-server/-/node-media-server-2.2.8.tgz#586569690733ff76309f8ff50cdf302f30207009" 5707 resolved "https://registry.yarnpkg.com/node-media-server/-/node-media-server-2.3.8.tgz#05ad4d1ea9372d4dd5f7b72fb5f1c00da44ce78b"
5716 integrity sha512-sLusJjFVJ3mWeDMHnkyx1gitPjsMcWbhzlXT7wC8gRRjP0HKmY3d8wGLjl0s+JVPB6ruwSZw0mAwntOwrOCnRw== 5708 integrity sha512-IWji6X4RQHoiAu0mTojtQ7dznrdsHRahXG51d8um0bjFKkmtJHN3W9oA2fVtFtDrAYMC5bG9GvGDO2qdZtgzww==
5717 dependencies: 5709 dependencies:
5718 basic-auth-connect "^1.0.0" 5710 basic-auth-connect "^1.0.0"
5719 chalk "^2.4.2" 5711 chalk "^2.4.2"
5720 dateformat "^3.0.3" 5712 dateformat "^3.0.3"
5721 express "^4.16.4" 5713 express "^4.16.4"
5722 lodash ">=4.17.13" 5714 lodash ">=4.17.13"
5715 minimist "^1.2.5"
5723 mkdirp "1.0.3" 5716 mkdirp "1.0.3"
5724 ws "^7.4.6" 5717 ws "^7.4.6"
5725 5718
@@ -5739,14 +5732,14 @@ nodemailer@^3.1.1:
5739 integrity sha1-/r+sy0vSc2eEc6MJxstLSi88SOM= 5732 integrity sha1-/r+sy0vSc2eEc6MJxstLSi88SOM=
5740 5733
5741nodemailer@^6.0.0, nodemailer@^6.5.0, nodemailer@^6.6.0: 5734nodemailer@^6.0.0, nodemailer@^6.5.0, nodemailer@^6.6.0:
5742 version "6.6.2" 5735 version "6.6.3"
5743 resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.6.2.tgz#e184c9ed5bee245a3e0bcabc7255866385757114" 5736 resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.6.3.tgz#31fb53dd4d8ae16fc088a65cb9ffa8d928a69b48"
5744 integrity sha512-YSzu7TLbI+bsjCis/TZlAXBoM4y93HhlIgo0P5oiA2ua9Z4k+E2Fod//ybIzdJxOlXGRcHIh/WaeCBehvxZb/Q== 5737 integrity sha512-faZFufgTMrphYoDjvyVpbpJcYzwyFnbAMmQtj1lVBYAUSm3SOy2fIdd9+Mr4UxPosBa0JRw9bJoIwQn+nswiew==
5745 5738
5746nodemon@^2.0.1: 5739nodemon@^2.0.1:
5747 version "2.0.7" 5740 version "2.0.12"
5748 resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.7.tgz#6f030a0a0ebe3ea1ba2a38f71bf9bab4841ced32" 5741 resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.12.tgz#5dae4e162b617b91f1873b3bfea215dd71e144d5"
5749 integrity sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA== 5742 integrity sha512-egCTmNZdObdBxUBw6ZNwvZ/xzk24CKRs5K6d+5zbmrMr7rOpPmfPeF6OxM3DDpaRx331CQRFEktn+wrFFfBSOA==
5750 dependencies: 5743 dependencies:
5751 chokidar "^3.2.2" 5744 chokidar "^3.2.2"
5752 debug "^3.2.6" 5745 debug "^3.2.6"
@@ -5862,9 +5855,9 @@ object-hash@2.1.1:
5862 integrity sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ== 5855 integrity sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==
5863 5856
5864object-inspect@^1.10.3, object-inspect@^1.9.0: 5857object-inspect@^1.10.3, object-inspect@^1.9.0:
5865 version "1.10.3" 5858 version "1.11.0"
5866 resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" 5859 resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1"
5867 integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== 5860 integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==
5868 5861
5869object-keys@^1.0.12, object-keys@^1.1.1: 5862object-keys@^1.0.12, object-keys@^1.1.1:
5870 version "1.1.1" 5863 version "1.1.1"
@@ -6394,9 +6387,9 @@ pngjs@^3.0.0, pngjs@^3.3.3:
6394 integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== 6387 integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==
6395 6388
6396postcss@^8.0.2: 6389postcss@^8.0.2:
6397 version "8.3.5" 6390 version "8.3.6"
6398 resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709" 6391 resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.6.tgz#2730dd76a97969f37f53b9a6096197be311cc4ea"
6399 integrity sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA== 6392 integrity sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==
6400 dependencies: 6393 dependencies:
6401 colorette "^1.2.2" 6394 colorette "^1.2.2"
6402 nanoid "^3.1.23" 6395 nanoid "^3.1.23"
@@ -6847,13 +6840,6 @@ readable-wrap@^1.0.0:
6847 dependencies: 6840 dependencies:
6848 readable-stream "^1.1.13-1" 6841 readable-stream "^1.1.13-1"
6849 6842
6850readdirp@~3.5.0:
6851 version "3.5.0"
6852 resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e"
6853 integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==
6854 dependencies:
6855 picomatch "^2.2.1"
6856
6857readdirp@~3.6.0: 6843readdirp@~3.6.0:
6858 version "3.6.0" 6844 version "3.6.0"
6859 resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" 6845 resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
@@ -6962,9 +6948,9 @@ require-main-filename@^2.0.0:
6962 integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== 6948 integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
6963 6949
6964resolve-alpn@^1.0.0: 6950resolve-alpn@^1.0.0:
6965 version "1.1.2" 6951 version "1.2.0"
6966 resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.2.tgz#30b60cfbb0c0b8dc897940fe13fe255afcdd4d28" 6952 resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.0.tgz#058bb0888d1cd4d12474e9a4b6eb17bdd5addc44"
6967 integrity sha512-8OyfzhAtA32LVUsJSke3auIyINcwdh5l3cvYKdKO0nvsYSKuiLfTM5i78PJswFPT8y6cPW+L1v6/hE95chcpDA== 6953 integrity sha512-e4FNQs+9cINYMO5NMFc6kOUCdohjqFPSgMuwuZAOUWqrfWsen+Yjy5qZFkV5K7VO7tFSLKcUL97olkED7sCBHA==
6968 6954
6969resolve-from@^4.0.0: 6955resolve-from@^4.0.0:
6970 version "4.0.0" 6956 version "4.0.0"
@@ -7066,14 +7052,14 @@ rusha@^0.8.13:
7066 resolved "https://registry.yarnpkg.com/rusha/-/rusha-0.8.14.tgz#a977d0de9428406138b7bb90d3de5dcd024e2f68" 7052 resolved "https://registry.yarnpkg.com/rusha/-/rusha-0.8.14.tgz#a977d0de9428406138b7bb90d3de5dcd024e2f68"
7067 integrity sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA== 7053 integrity sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA==
7068 7054
7069rxjs@7.1.0: 7055rxjs@7.2.0, rxjs@^7.2.0:
7070 version "7.1.0" 7056 version "7.2.0"
7071 resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.1.0.tgz#94202d27b19305ef7b1a4f330277b2065df7039e" 7057 resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.2.0.tgz#5cd12409639e9514a71c9f5f9192b2c4ae94de31"
7072 integrity sha512-gCFO5iHIbRPwznl6hAYuwNFld8W4S2shtSJIqG27ReWXo9IWrCyEICxUA+6vJHwSR/OakoenC4QsDxq50tzYmw== 7058 integrity sha512-aX8w9OpKrQmiPKfT1bqETtUr9JygIz6GZ+gql8v7CijClsP0laoFUdKzxFAoWuRdSlOdU2+crss+cMf+cqMTnw==
7073 dependencies: 7059 dependencies:
7074 tslib "~2.1.0" 7060 tslib "~2.1.0"
7075 7061
7076rxjs@^6.6.3, rxjs@^6.6.6: 7062rxjs@^6.6.3:
7077 version "6.6.7" 7063 version "6.6.7"
7078 resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" 7064 resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
7079 integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== 7065 integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==
@@ -7199,10 +7185,10 @@ sequelize@6.6.2:
7199 validator "^10.11.0" 7185 validator "^10.11.0"
7200 wkx "^0.5.0" 7186 wkx "^0.5.0"
7201 7187
7202serialize-javascript@5.0.1: 7188serialize-javascript@6.0.0:
7203 version "5.0.1" 7189 version "6.0.0"
7204 resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" 7190 resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
7205 integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== 7191 integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
7206 dependencies: 7192 dependencies:
7207 randombytes "^2.1.0" 7193 randombytes "^2.1.0"
7208 7194
@@ -7385,7 +7371,7 @@ socket.io-adapter@~1.1.0:
7385 resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9" 7371 resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9"
7386 integrity sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g== 7372 integrity sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==
7387 7373
7388socket.io-adapter@~2.3.0: 7374socket.io-adapter@~2.3.1:
7389 version "2.3.1" 7375 version "2.3.1"
7390 resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.3.1.tgz#a442720cb09a4823cfb81287dda1f9b52d4ccdb2" 7376 resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.3.1.tgz#a442720cb09a4823cfb81287dda1f9b52d4ccdb2"
7391 integrity sha512-8cVkRxI8Nt2wadkY6u60Y4rpW3ejA1rxgcK2JuyIhmF+RMNpTy1QRtkHIDUOf3B4HlQwakMsWbKftMv/71VMmw== 7377 integrity sha512-8cVkRxI8Nt2wadkY6u60Y4rpW3ejA1rxgcK2JuyIhmF+RMNpTy1QRtkHIDUOf3B4HlQwakMsWbKftMv/71VMmw==
@@ -7411,15 +7397,15 @@ socket.io-client@2.2.0:
7411 to-array "0.1.4" 7397 to-array "0.1.4"
7412 7398
7413socket.io-client@^4.0.1: 7399socket.io-client@^4.0.1:
7414 version "4.1.2" 7400 version "4.1.3"
7415 resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.1.2.tgz#95ad7113318ea01fba0860237b96d71e1b1fd2eb" 7401 resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.1.3.tgz#236daa642a9f229932e00b7221e843bf74232a62"
7416 integrity sha512-RDpWJP4DQT1XeexmeDyDkm0vrFc0+bUsHDKiVGaNISJvJonhQQOMqV9Vwfg0ZpPJ27LCdan7iqTI92FRSOkFWQ== 7402 integrity sha512-hISFn6PDpgDifVUiNklLHVPTMv1LAk8poHArfIUdXa+gKgbr0MZbAlquDFqCqsF30yBqa+jg42wgos2FK50BHA==
7417 dependencies: 7403 dependencies:
7418 "@types/component-emitter" "^1.2.10" 7404 "@types/component-emitter" "^1.2.10"
7419 backo2 "~1.0.2" 7405 backo2 "~1.0.2"
7420 component-emitter "~1.3.0" 7406 component-emitter "~1.3.0"
7421 debug "~4.3.1" 7407 debug "~4.3.1"
7422 engine.io-client "~5.1.1" 7408 engine.io-client "~5.1.2"
7423 parseuri "0.0.6" 7409 parseuri "0.0.6"
7424 socket.io-parser "~4.0.4" 7410 socket.io-parser "~4.0.4"
7425 7411
@@ -7432,7 +7418,7 @@ socket.io-parser@~3.3.0:
7432 debug "~3.1.0" 7418 debug "~3.1.0"
7433 isarray "2.0.1" 7419 isarray "2.0.1"
7434 7420
7435socket.io-parser@~4.0.3, socket.io-parser@~4.0.4: 7421socket.io-parser@~4.0.4:
7436 version "4.0.4" 7422 version "4.0.4"
7437 resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.0.4.tgz#9ea21b0d61508d18196ef04a2c6b9ab630f4c2b0" 7423 resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.0.4.tgz#9ea21b0d61508d18196ef04a2c6b9ab630f4c2b0"
7438 integrity sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g== 7424 integrity sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==
@@ -7454,19 +7440,19 @@ socket.io@2.2.0:
7454 socket.io-parser "~3.3.0" 7440 socket.io-parser "~3.3.0"
7455 7441
7456socket.io@^4.0.1: 7442socket.io@^4.0.1:
7457 version "4.1.2" 7443 version "4.1.3"
7458 resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.1.2.tgz#f90f9002a8d550efe2aa1d320deebb9a45b83233" 7444 resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.1.3.tgz#d114328ef27ab31b889611792959c3fa6d502500"
7459 integrity sha512-xK0SD1C7hFrh9+bYoYCdVt+ncixkSLKtNLCax5aEy1o3r5PaO5yQhVb97exIe67cE7lAK+EpyMytXWTWmyZY8w== 7445 integrity sha512-tLkaY13RcO4nIRh1K2hT5iuotfTaIQw7cVIe0FUykN3SuQi0cm7ALxuyT5/CtDswOMWUzMGTibxYNx/gU7In+Q==
7460 dependencies: 7446 dependencies:
7461 "@types/cookie" "^0.4.0" 7447 "@types/cookie" "^0.4.0"
7462 "@types/cors" "^2.8.8" 7448 "@types/cors" "^2.8.10"
7463 "@types/node" ">=10.0.0" 7449 "@types/node" ">=10.0.0"
7464 accepts "~1.3.4" 7450 accepts "~1.3.4"
7465 base64id "~2.0.0" 7451 base64id "~2.0.0"
7466 debug "~4.3.1" 7452 debug "~4.3.1"
7467 engine.io "~5.1.0" 7453 engine.io "~5.1.1"
7468 socket.io-adapter "~2.3.0" 7454 socket.io-adapter "~2.3.1"
7469 socket.io-parser "~4.0.3" 7455 socket.io-parser "~4.0.4"
7470 7456
7471source-map-js@^0.6.2: 7457source-map-js@^0.6.2:
7472 version "0.6.2" 7458 version "0.6.2"
@@ -7809,9 +7795,9 @@ superagent@^6.1.0:
7809 semver "^7.3.2" 7795 semver "^7.3.2"
7810 7796
7811supertest@^6.0.1: 7797supertest@^6.0.1:
7812 version "6.1.3" 7798 version "6.1.4"
7813 resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.1.3.tgz#3f49ea964514c206c334073e8dc4e70519c7403f" 7799 resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.1.4.tgz#ea8953343e0ca316e80e975b39340934f754eb06"
7814 integrity sha512-v2NVRyP73XDewKb65adz+yug1XMtmvij63qIWHZzSX8tp6wiq6xBLUy4SUAd2NII6wIipOmHT/FD9eicpJwdgQ== 7800 integrity sha512-giC9Zm+Bf1CZP09ciPdUyl+XlMAu6rbch79KYiYKOGcbK2R1wH8h+APul1i/3wN6RF1XfWOIF+8X1ga+7SBrug==
7815 dependencies: 7801 dependencies:
7816 methods "^1.1.2" 7802 methods "^1.1.2"
7817 superagent "^6.1.0" 7803 superagent "^6.1.0"
@@ -8063,10 +8049,10 @@ triple-beam@^1.2.0, triple-beam@^1.3.0:
8063 resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" 8049 resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
8064 integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== 8050 integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==
8065 8051
8066ts-node@10.0.0: 8052ts-node@10.1.0:
8067 version "10.0.0" 8053 version "10.1.0"
8068 resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.0.0.tgz#05f10b9a716b0b624129ad44f0ea05dac84ba3be" 8054 resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.1.0.tgz#e656d8ad3b61106938a867f69c39a8ba6efc966e"
8069 integrity sha512-ROWeOIUvfFbPZkoDis0L/55Fk+6gFQNZwwKPLinacRl6tsxstTF1DbAcLKkovwnpKMVvOMHP1TIbnwXwtLg1gg== 8055 integrity sha512-6szn3+J9WyG2hE+5W8e0ruZrzyk1uFLYye6IGMBadnOzDh8aP7t8CbFpsfCiEx2+wMixAhjFt7lOZC4+l+WbEA==
8070 dependencies: 8056 dependencies:
8071 "@tsconfig/node10" "^1.0.7" 8057 "@tsconfig/node10" "^1.0.7"
8072 "@tsconfig/node12" "^1.0.7" 8058 "@tsconfig/node12" "^1.0.7"
@@ -8080,12 +8066,11 @@ ts-node@10.0.0:
8080 yn "3.1.1" 8066 yn "3.1.1"
8081 8067
8082tsconfig-paths@^3.9.0: 8068tsconfig-paths@^3.9.0:
8083 version "3.9.0" 8069 version "3.10.1"
8084 resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" 8070 resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz#79ae67a68c15289fdf5c51cb74f397522d795ed7"
8085 integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== 8071 integrity sha512-rETidPDgCpltxF7MjBZlAFPUHv5aHH2MymyPvh+vEyWAED4Eb/WeMbsnD/JDr4OKPOA1TssDHgIcpTN5Kh0p6Q==
8086 dependencies: 8072 dependencies:
8087 "@types/json5" "^0.0.29" 8073 json5 "^2.2.0"
8088 json5 "^1.0.1"
8089 minimist "^1.2.0" 8074 minimist "^1.2.0"
8090 strip-bom "^3.0.0" 8075 strip-bom "^3.0.0"
8091 8076
@@ -8194,9 +8179,9 @@ typedarray@^0.0.6:
8194 integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= 8179 integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
8195 8180
8196typescript@^4.0.5: 8181typescript@^4.0.5:
8197 version "4.3.4" 8182 version "4.3.5"
8198 resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.4.tgz#3f85b986945bcf31071decdd96cf8bfa65f9dcbc" 8183 resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4"
8199 integrity sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew== 8184 integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
8200 8185
8201uc.micro@^1.0.1, uc.micro@^1.0.5: 8186uc.micro@^1.0.1, uc.micro@^1.0.5:
8202 version "1.0.6" 8187 version "1.0.6"
@@ -8322,7 +8307,7 @@ ut_metadata@^3.5.2:
8322 debug "^4.2.0" 8307 debug "^4.2.0"
8323 simple-sha1 "^3.0.1" 8308 simple-sha1 "^3.0.1"
8324 8309
8325ut_pex@^3.0.0: 8310ut_pex@^3.0.1:
8326 version "3.0.1" 8311 version "3.0.1"
8327 resolved "https://registry.yarnpkg.com/ut_pex/-/ut_pex-3.0.1.tgz#fb8b6e066f8f6f6de3e6b3e28e7d18e697be5854" 8312 resolved "https://registry.yarnpkg.com/ut_pex/-/ut_pex-3.0.1.tgz#fb8b6e066f8f6f6de3e6b3e28e7d18e697be5854"
8328 integrity sha512-t1MHIDHSISgOJcmq8UM6Qv9/hRQYVaUvzqSNnXa5ATDbS9hXfhBpyBo2HcSyJtwPSHsmMtNui8G6yKirwJ8vow== 8313 integrity sha512-t1MHIDHSISgOJcmq8UM6Qv9/hRQYVaUvzqSNnXa5ATDbS9hXfhBpyBo2HcSyJtwPSHsmMtNui8G6yKirwJ8vow==
@@ -8378,10 +8363,10 @@ utils-merge@1.0.1:
8378 resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" 8363 resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
8379 integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= 8364 integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
8380 8365
8381utp-native@^2.4.0: 8366utp-native@^2.5.3:
8382 version "2.5.1" 8367 version "2.5.3"
8383 resolved "https://registry.yarnpkg.com/utp-native/-/utp-native-2.5.1.tgz#445a3fcf23db0a841a48c4e353f8900ea852b3e8" 8368 resolved "https://registry.yarnpkg.com/utp-native/-/utp-native-2.5.3.tgz#7c04c2a8c2858716555a77d10adb9819e3119b25"
8384 integrity sha512-wbrJwR8DZx8N9s1ffTcMuBK7tMBQ9tvKpIL+mWHrDvGUYfV7ivroEGFTXUr4meqy/PVbUdMfURSoBbwuGtt/YQ== 8369 integrity sha512-sWTrWYXPhhWJh+cS2baPzhaZc89zwlWCfwSthUjGhLkZztyPhcQllo+XVVCbNGi7dhyRlxkWxN4NKU6FbA9Y8w==
8385 dependencies: 8370 dependencies:
8386 napi-macros "^2.0.0" 8371 napi-macros "^2.0.0"
8387 node-gyp-build "^4.2.0" 8372 node-gyp-build "^4.2.0"
@@ -8407,11 +8392,6 @@ uuid@8.3.2, uuid@^8.1.0, uuid@^8.3.0, uuid@^8.3.2:
8407 resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" 8392 resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
8408 integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== 8393 integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
8409 8394
8410uuid@^3.4.0:
8411 version "3.4.0"
8412 resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
8413 integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
8414
8415v8-compile-cache@^2.0.3: 8395v8-compile-cache@^2.0.3:
8416 version "2.3.0" 8396 version "2.3.0"
8417 resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" 8397 resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
@@ -8503,19 +8483,20 @@ webfinger.js@^2.6.6:
8503 xhr2 "^0.1.4" 8483 xhr2 "^0.1.4"
8504 8484
8505webtorrent@^1.0.0: 8485webtorrent@^1.0.0:
8506 version "1.0.2" 8486 version "1.2.5"
8507 resolved "https://registry.yarnpkg.com/webtorrent/-/webtorrent-1.0.2.tgz#acd0ae3bedcfa8cb732043489ff506d07d90140a" 8487 resolved "https://registry.yarnpkg.com/webtorrent/-/webtorrent-1.2.5.tgz#edea45c53b98787a472381ff91d41c9164b8ac51"
8508 integrity sha512-uv9fq5md/98JyeDDyziy1H28Wc/idO80AKv+9pQ4XK0WNNjdE3FMtCKfrvU2VNS1PdAOrA6sFuUq2x0mV7k7WQ== 8488 integrity sha512-EvtAQ3rK4c7Kf4ZGxYOGvi8Jih8qsZka1IgNB8T5Vxw5UzSNG1nxTVNNTXL0jFhQUMsyRwIOkTgd7ZkJY6bqsw==
8509 dependencies: 8489 dependencies:
8510 addr-to-ip-port "^1.5.1" 8490 addr-to-ip-port "^1.5.1"
8511 bitfield "^4.0.0" 8491 bitfield "^4.0.0"
8512 bittorrent-dht "^10.0.0" 8492 bittorrent-dht "^10.0.1"
8513 bittorrent-protocol "^3.3.1" 8493 bittorrent-protocol "^3.4.2"
8494 cache-chunk-store "^3.2.2"
8514 chrome-net "^3.3.4" 8495 chrome-net "^3.3.4"
8515 chunk-store-stream "^4.3.0" 8496 chunk-store-stream "^4.3.0"
8516 cpus "^1.0.3" 8497 cpus "^1.0.3"
8517 create-torrent "^4.7.0" 8498 create-torrent "^4.7.0"
8518 debug "^4.3.1" 8499 debug "^4.3.2"
8519 end-of-stream "^1.4.4" 8500 end-of-stream "^1.4.4"
8520 escape-html "^1.0.3" 8501 escape-html "^1.0.3"
8521 fs-chunk-store "^2.0.3" 8502 fs-chunk-store "^2.0.3"
@@ -8549,9 +8530,9 @@ webtorrent@^1.0.0:
8549 torrent-piece "^2.0.1" 8530 torrent-piece "^2.0.1"
8550 unordered-array-remove "^1.0.2" 8531 unordered-array-remove "^1.0.2"
8551 ut_metadata "^3.5.2" 8532 ut_metadata "^3.5.2"
8552 ut_pex "^3.0.0" 8533 ut_pex "^3.0.1"
8553 optionalDependencies: 8534 optionalDependencies:
8554 utp-native "^2.4.0" 8535 utp-native "^2.5.3"
8555 8536
8556which-boxed-primitive@^1.0.2: 8537which-boxed-primitive@^1.0.2:
8557 version "1.0.2" 8538 version "1.0.2"
@@ -8659,10 +8640,10 @@ word-wrap@^1.2.3:
8659 resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" 8640 resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
8660 integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== 8641 integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
8661 8642
8662workerpool@6.1.4: 8643workerpool@6.1.5:
8663 version "6.1.4" 8644 version "6.1.5"
8664 resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.4.tgz#6a972b6df82e38d50248ee2820aa98e2d0ad3090" 8645 resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581"
8665 integrity sha512-jGWPzsUqzkow8HoAvqaPWTUPCrlPJaJ5tY8Iz7n1uCz3tTp6s3CDG0FF1NsX42WNlkRSW6Mr+CDZGnNoSsKa7g== 8646 integrity sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==
8666 8647
8667wrap-ansi@^6.2.0: 8648wrap-ansi@^6.2.0:
8668 version "6.2.0" 8649 version "6.2.0"
@@ -8698,9 +8679,9 @@ write-file-atomic@^3.0.0:
8698 typedarray-to-buffer "^3.1.5" 8679 typedarray-to-buffer "^3.1.5"
8699 8680
8700ws@^7.0.0, ws@^7.4.2, ws@^7.4.5, ws@^7.4.6: 8681ws@^7.0.0, ws@^7.4.2, ws@^7.4.5, ws@^7.4.6:
8701 version "7.5.0" 8682 version "7.5.3"
8702 resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.0.tgz#0033bafea031fb9df041b2026fc72a571ca44691" 8683 resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74"
8703 integrity sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw== 8684 integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==
8704 8685
8705ws@~6.1.0: 8686ws@~6.1.0:
8706 version "6.1.4" 8687 version "6.1.4"