Merge branch 'main' into pr/301

This commit is contained in:
Michael Shamoon 2022-11-03 20:36:27 -07:00
commit f7fa14edff
57 changed files with 1645 additions and 244 deletions

View File

@ -1,30 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: "[Bug] "
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Configuration**
If applicable,
```yaml
# Please provide your service, widget or otherwise related configuration here
```
**Additional context**
Add any other context about the problem here. This includes things like:
- Service version or API version
- Docker version
- Deployment method
- Sample YAML configurations

66
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@ -0,0 +1,66 @@
name: Bug report
description: Create a report to help us improve
title: "[Bug] "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Have a question? 👉 [Start a new discussion](https://github.com/benphelps/homepage/discussions/new) or [ask in chat](https://discord.gg/SaPGSzrEZC).
Before opening an issue, please double check:
- [The troubleshooting guide](https://gethomepage.dev/en/more/troubleshooting/).
- [The homepage documentation](https://gethomepage.dev/)
- [Existing issues](https://github.com/benphelps/homepage/search?q=&type=issues) and [discussions](https://github.com/benphelps/homepage/search?q=&type=discussions).
- type: textarea
id: description
attributes:
label: Description
description: A clear and concise description of what the bug is. If applicable, add screenshots to help explain your problem.
placeholder: |
Currently homepage does not work when...
[Screenshot if applicable]
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: Steps to reproduce the behavior.
placeholder: |
1. Go to '...'
2. Click on '....'
3. See error
validations:
required: true
- type: input
id: version
attributes:
label: homepage version
placeholder: e.g. v0.4.18 (4ea2798)
validations:
required: true
- type: dropdown
id: install-method
attributes:
label: Installation method
options:
- Docker
- Unraid
- Source
- Other (please describe above)
validations:
required: true
- type: textarea
id: config
attributes:
label: Configuration
description: Please provide any relevant service, widget or otherwise related configuration here
render: yaml
- type: textarea
id: other
attributes:
label: Other
description: Any other relevant details. E.g. service version or API version, docker version, etc.

View File

@ -1,17 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[Feature Request] "
labels: ''
assignees: ''
---
**Is your feature request related to a service? Please describe.**
A clear and concise description of what you would like to see from this service.
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I would like it if [...]
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -0,0 +1,17 @@
name: Feature request
description: Suggest an idea for this project
title: "[Feature Request] "
labels: ["enhancement"]
body:
- type: textarea
id: description
attributes:
label: Description
description: A clear and concise description of what you would like to see.
validations:
required: true
- type: textarea
id: other
attributes:
label: Other
description: Add any other context or information about the feature request here.

View File

@ -43,7 +43,7 @@ jobs:
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@main
with:
cosign-release: 'v1.11.0' # optional
cosign-release: 'v1.13.1' # optional
# Setup QEMU
# https://github.com/marketplace/actions/docker-setup-buildx#with-qemu
@ -100,7 +100,7 @@ jobs:
REVISION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}
# https://github.com/docker/setup-qemu-action#about
# platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6
platforms: linux/amd64,linux/arm64,linux/arm/v7
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
@ -109,13 +109,13 @@ jobs:
# repository is public to avoid leaking data. If you would like to publish
# transparency data even for private images, pass --force to cosign below.
# https://github.com/sigstore/cosign
- name: Sign the published Docker image
if: ${{ github.event_name != 'pull_request' }}
env:
COSIGN_EXPERIMENTAL: "true"
# This step uses the identity token to provision an ephemeral certificate
# against the sigstore community Fulcio instance.
run: echo "${{ steps.meta.outputs.tags }}" | xargs -I {} cosign sign {}@${{ steps.build-and-push.outputs.digest }}
# - name: Sign the published Docker image
# if: ${{ github.event_name != 'pull_request' }}
# env:
# COSIGN_EXPERIMENTAL: "true"
# # This step uses the identity token to provision an ephemeral certificate
# # against the sigstore community Fulcio instance.
# run: echo "${{ steps.meta.outputs.tags }}" | xargs -I {} cosign sign {}@${{ steps.build-and-push.outputs.digest }}
# Temp fix
# https://github.com/docker/build-push-action/issues/252

View File

@ -1,7 +1,7 @@
# syntax = docker/dockerfile:latest
# Install dependencies only when needed
FROM node:current-alpine AS deps
FROM docker.io/node:18-alpine AS deps
WORKDIR /app
@ -19,7 +19,7 @@ RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store pnpm f
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store pnpm install -r --offline
# Rebuild the source code only when needed
FROM node:current-alpine AS builder
FROM docker.io/node:18-alpine AS builder
WORKDIR /app
ARG BUILDTIME
@ -37,7 +37,7 @@ RUN <<EOF
EOF
# Production image, copy all the files and run next
FROM node:current-alpine AS runner
FROM docker.io/node:18-alpine AS runner
LABEL org.opencontainers.image.title "Homepage"
LABEL org.opencontainers.image.description "A self-hosted services landing page, with docker and service integrations."
LABEL org.opencontainers.image.url="https://github.com/benphelps/homepage"

View File

@ -38,7 +38,7 @@
- Images built for AMD64 (x86_64), ARM64, ARMv7 and ARMv6
- Supports all Raspberry Pi's, most SBCs & Apple Silicon
- Full i18n support with automatic language detection
- Translations for Catalan, Chinese, Dutch, Finnish, French, German, Hebrew, Hungarian, Norwegian Bokmål, Polish, Portuguese, Portuguese (Brazil), Romainian, Russian, Spanish, Swedish and Yue
- Translations for Catalan, Chinese, Dutch, Finnish, French, German, Hebrew, Hungarian, Norwegian Bokmål, Polish, Portuguese, Portuguese (Brazil), Romanian, Russian, Spanish, Swedish and Yue
- Want to help translate? [Join the Weblate project](https://hosted.weblate.org/engage/homepage/)
- Service & Web Bookmarks
- Docker Integration
@ -67,7 +67,7 @@ If you have a more specific issue, please open an issue on the [Issues](https://
## Getting Started
For configuration options, examples and more, [please check out the Wiki](https://github.com/benphelps/homepage/wiki).
For configuration options, examples and more, [please check out the homepage site](http://gethomepage.dev).
### With Docker
@ -122,7 +122,7 @@ Configuration files will be genereted and placed on the first request.
Configuration is done in the /config directory using .yaml files. Refer to each config for
the specific configuration options.
You may also check [the wiki](https://github.com/benphelps/homepage/wiki) for detailed configuration instructions, examples and more.
You may also check [the homepage site](http://gethomepage.dev) for detailed configuration instructions, examples and more.
## Development

View File

@ -0,0 +1,302 @@
{
"widget": {
"missing_type": "نوع القطعة مفقود: {{type}}",
"api_error": "API خطأ",
"status": "الحالة"
},
"weather": {
"current": "الموقع الحالي",
"allow": "اضغط للسماح",
"updating": "جاري التحديث",
"wait": "الرجاء الانتظار"
},
"search": {
"placeholder": "بحث …"
},
"resources": {
"cpu": "المعالج",
"total": "المجموع",
"free": "متاح",
"used": "مستخدم",
"load": "الضغط"
},
"unifi": {
"users": "المستخدمون",
"uptime": "مدة تشغيل النظام",
"days": "ايام",
"wan": "WAN",
"lan": "LAN",
"wlan": "WLAN",
"devices": "الاجهزة",
"lan_devices": "LAN اجهزة",
"wlan_devices": "WLAN احهزة",
"lan_users": "LAN مستخدمين",
"wlan_users": "WLAN مستخدمين",
"up": "اعلي",
"down": "اسفل",
"wait": "الرجاء الانتظار"
},
"wmo": {
"73-day": "Snow",
"0-day": "Sunny",
"0-night": "Clear",
"1-day": "Mainly Sunny",
"1-night": "Mainly Clear",
"2-day": "Partly Cloudy",
"2-night": "Partly Cloudy",
"3-day": "Cloudy",
"3-night": "Cloudy",
"45-day": "Foggy",
"45-night": "Foggy",
"48-day": "Foggy",
"48-night": "Foggy",
"51-day": "Light Drizzle",
"51-night": "Light Drizzle",
"53-day": "Drizzle",
"53-night": "Drizzle",
"55-day": "Heavy Drizzle",
"55-night": "Heavy Drizzle",
"56-day": "Light Freezing Drizzle",
"56-night": "Light Freezing Drizzle",
"57-day": "Freezing Drizzle",
"57-night": "Freezing Drizzle",
"61-day": "Light Rain",
"61-night": "Light Rain",
"63-day": "Rain",
"63-night": "Rain",
"65-day": "Heavy Rain",
"65-night": "Heavy Rain",
"66-day": "Freezing Rain",
"66-night": "Freezing Rain",
"67-day": "Freezing Rain",
"67-night": "Freezing Rain",
"71-day": "Light Snow",
"71-night": "Light Snow",
"73-night": "Snow",
"75-day": "Heavy Snow",
"75-night": "Heavy Snow",
"77-day": "Snow Grains",
"77-night": "Snow Grains",
"80-day": "Light Showers",
"80-night": "Light Showers",
"81-day": "Showers",
"81-night": "Showers",
"82-day": "Heavy Showers",
"82-night": "Heavy Showers",
"85-day": "Snow Showers",
"85-night": "Snow Showers",
"86-day": "Snow Showers",
"86-night": "Snow Showers",
"95-day": "Thunderstorm",
"95-night": "Thunderstorm",
"96-day": "Thunderstorm With Hail",
"96-night": "Thunderstorm With Hail",
"99-day": "Thunderstorm With Hail",
"99-night": "Thunderstorm With Hail"
},
"docker": {
"rx": "RX",
"tx": "TX",
"mem": "الرام",
"cpu": "المعالج",
"offline": "غير متصل"
},
"emby": {
"playing": "يعمل الان",
"transcoding": "التحويل",
"bitrate": "معدل البت",
"no_active": "No Active Streams"
},
"changedetectionio": {
"totalObserved": "Total Observed",
"diffsDetected": "Diffs Detected"
},
"tautulli": {
"playing": "Playing",
"transcoding": "Transcoding",
"bitrate": "Bitrate",
"no_active": "No Active Streams"
},
"nzbget": {
"rate": "Rate",
"remaining": "Remaining",
"downloaded": "Downloaded"
},
"plex": {
"streams": "Active Streams",
"movies": "Movies",
"tv": "TV Shows"
},
"sabnzbd": {
"rate": "Rate",
"queue": "Queue",
"timeleft": "Time Left"
},
"rutorrent": {
"active": "Active",
"upload": "Upload",
"download": "Download"
},
"transmission": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"qbittorrent": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"sonarr": {
"wanted": "Wanted",
"queued": "Queued",
"series": "Series"
},
"radarr": {
"wanted": "Wanted",
"missing": "Missing",
"queued": "Queued",
"movies": "Movies"
},
"lidarr": {
"wanted": "Wanted",
"queued": "Queued",
"albums": "Albums"
},
"readarr": {
"wanted": "Wanted",
"queued": "Queued",
"books": "Books"
},
"bazarr": {
"missingEpisodes": "Missing Episodes",
"missingMovies": "Missing Movies"
},
"ombi": {
"pending": "Pending",
"approved": "Approved",
"available": "Available"
},
"jellyseerr": {
"pending": "Pending",
"approved": "Approved",
"available": "Available"
},
"overseerr": {
"pending": "Pending",
"approved": "Approved",
"available": "Available"
},
"pihole": {
"queries": "Queries",
"blocked": "Blocked",
"gravity": "Gravity"
},
"adguard": {
"queries": "Queries",
"blocked": "Blocked",
"filtered": "Filtered",
"latency": "Latency"
},
"speedtest": {
"upload": "Upload",
"download": "Download",
"ping": "Ping"
},
"portainer": {
"running": "Running",
"stopped": "Stopped",
"total": "Total"
},
"traefik": {
"routers": "Routers",
"services": "Services",
"middleware": "Middleware"
},
"npm": {
"enabled": "Enabled",
"disabled": "Disabled",
"total": "Total"
},
"coinmarketcap": {
"configure": "Configure one or more crypto currencies to track",
"1hour": "1 Hour",
"1day": "1 Day",
"7days": "7 Days",
"30days": "30 Days"
},
"gotify": {
"apps": "Applications",
"clients": "Clients",
"messages": "Messages"
},
"prowlarr": {
"enableIndexers": "Indexers",
"numberOfGrabs": "Grabs",
"numberOfQueries": "Queries",
"numberOfFailGrabs": "Fail Grabs",
"numberOfFailQueries": "Fail Queries"
},
"jackett": {
"configured": "Configured",
"errored": "Errored"
},
"strelaysrv": {
"numActiveSessions": "Sessions",
"numConnections": "Connections",
"dataRelayed": "Relayed",
"transferRate": "Rate"
},
"mastodon": {
"user_count": "Users",
"status_count": "Posts",
"domain_count": "Domains"
},
"authentik": {
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
},
"glances": {
"cpu": "CPU",
"mem": "MEM",
"wait": "Please wait"
},
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers",
"approvedPushes": "Approved"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -284,5 +284,30 @@
"96-night": "Thunderstorm With Hail",
"99-day": "Thunderstorm With Hail",
"99-night": "Thunderstorm With Hail"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"watchtower":{
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -209,36 +209,36 @@
"wait": "Espere por favor"
},
"changedetectionio": {
"totalObserved": "Observados en Total",
"diffsDetected": "Diffs Detected"
"totalObserved": "Total Observados",
"diffsDetected": "Diferencias detectadas"
},
"wmo": {
"48-night": "Niebla",
"51-day": "Light Drizzle",
"51-night": "Light Drizzle",
"2-day": "Parcialmente Nuboso",
"51-day": "Llovizna ligera",
"51-night": "Llovizna ligera",
"2-day": "Parcialmente nuboso",
"0-day": "Soleado",
"0-night": "Despejado",
"1-day": "Mayormente Soleado",
"1-night": "Mayormente Despejado",
"2-night": "Parcialmente Nuboso",
"3-day": "Nubaldo",
"1-day": "Mayormente soleado",
"1-night": "Mayormente despejado",
"2-night": "Parcialmente nuboso",
"3-day": "Nublado",
"3-night": "Nublado",
"45-day": "Niebla",
"45-night": "Niebla",
"48-day": "Niebla",
"53-day": "Drizzle",
"53-night": "Drizzle",
"55-day": "Heavy Drizzle",
"55-night": "Heavy Drizzle",
"56-day": "Light Freezing Drizzle",
"56-night": "Light Freezing Drizzle",
"57-day": "Freezing Drizzle",
"57-night": "Freezing Drizzle",
"61-day": "Light Rain",
"61-night": "Light Rain",
"53-day": "Llovizna",
"53-night": "Llovizna",
"55-day": "Llovizna pesada",
"55-night": "Llovizna pesada",
"56-day": "Llovizna helada ligera",
"56-night": "Llovizna helada ligera",
"57-day": "Llovizna helada",
"57-night": "Llovizna helada",
"61-day": "Lluvia ligera",
"61-night": "Lluvia ligera",
"67-day": "Granizo",
"63-day": "Rain",
"63-day": "Lluvia",
"63-night": "Lluvia",
"65-day": "Chubascos",
"65-night": "Chubascos",
@ -260,18 +260,43 @@
"82-day": "Chubascos",
"82-night": "Chubascos",
"85-day": "Chubascos",
"85-night": "Snow Showers",
"86-day": "Snow Showers",
"86-night": "Snow Showers",
"95-day": "Thunderstorm",
"95-night": "Thunderstorm",
"96-day": "Thunderstorm With Hail",
"96-night": "Thunderstorm With Hail",
"99-day": "Thunderstorm With Hail",
"99-night": "Thunderstorm With Hail"
"85-night": "Lluvia de nieve",
"86-day": "Lluvia de nieve",
"86-night": "Lluvia de nieve",
"95-day": "Tormenta",
"95-night": "Tormenta",
"96-day": "Tormenta con granizo",
"96-night": "Tormenta con granizo",
"99-day": "Tormenta con granizo",
"99-night": "Tormenta con granizo"
},
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
"bookmark": "Marcadores",
"service": "Servicio"
},
"homebridge": {
"available_update": "Sistema",
"updates": "Actualizaciones",
"update_available": "Actualización disponible",
"up_to_date": "Actualizado",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Aprobado",
"rejectedPushes": "Rechazado",
"filters": "Filtros",
"indexers": "Indexadores"
},
"watchtower": {
"containers_scanned": "Escaneado",
"containers_updated": "Actualizado",
"containers_failed": "Fallido"
},
"tubearchivist": {
"downloads": "Cola",
"videos": "Vídeos",
"channels": "Canales",
"playlists": "Listas de reproducción"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -271,7 +271,32 @@
"0-day": "Ensoleillé"
},
"quicklaunch": {
"bookmark": "Bookmark",
"bookmark": "Signet",
"service": "Service"
},
"homebridge": {
"available_update": "Système",
"updates": "Mises à jour",
"update_available": "Mise à jour disponible",
"up_to_date": "À jour",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approuvé",
"rejectedPushes": "Rejeté",
"filters": "Filtres",
"indexers": "Indexeur"
},
"watchtower": {
"containers_scanned": "Scanné",
"containers_updated": "Mis à jour",
"containers_failed": "Échoué"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Vidéos",
"channels": "Chaînes",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"rejectedPushes": "Rejected",
"approvedPushes": "Approved",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -220,8 +220,8 @@
"1-day": "Mainly Sunny",
"1-night": "Mainly Clear",
"2-day": "Parzialmente Nuvoloso",
"3-day": "Cloudy",
"3-night": "Cloudy",
"3-day": "Nuvoloso",
"3-night": "Nuvoloso",
"45-day": "Nebbioso",
"45-night": "Nebbioso",
"48-day": "Nebbioso",
@ -241,10 +241,10 @@
"63-day": "Pioggia",
"63-night": "Pioggia",
"65-night": "Heavy Rain",
"66-day": "Freezing Rain",
"66-night": "Freezing Rain",
"67-day": "Freezing Rain",
"67-night": "Freezing Rain",
"66-day": "Grandine",
"66-night": "Grandine",
"67-day": "Grandine",
"67-night": "Grandine",
"71-day": "Light Snow",
"71-night": "Light Snow",
"73-day": "Neve",
@ -263,15 +263,40 @@
"85-night": "Snow Showers",
"86-day": "Snow Showers",
"86-night": "Snow Showers",
"95-day": "Thunderstorm",
"95-night": "Thunderstorm",
"96-day": "Thunderstorm With Hail",
"96-night": "Thunderstorm With Hail",
"99-day": "Thunderstorm With Hail",
"99-night": "Thunderstorm With Hail"
"95-day": "Temporale",
"95-night": "Temporale",
"96-day": "Temporale con grandine",
"96-night": "Temporale con grandine",
"99-day": "Temporale con grandine",
"99-night": "Temporale con grandine"
},
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
"service": "Servizio"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -149,14 +149,14 @@
"errored": "Błędne"
},
"adguard": {
"queries": "Queries",
"blocked": "Blocked",
"filtered": "Filtered",
"latency": "Latency"
"queries": "Zapytania",
"blocked": "Zablokowane",
"filtered": "Przefiltrowane",
"latency": "Opóźnienia"
},
"qbittorrent": {
"download": "Download",
"upload": "Upload",
"download": "Pobieranie",
"upload": "Wysyłanie",
"leech": "Leech",
"seed": "Seed"
},
@ -187,26 +187,26 @@
"uptime": "Czas pracy systemu",
"days": "Dni",
"wan": "WAN",
"lan_users": "LAN Users",
"wlan_users": "WLAN Users",
"up": "UP",
"down": "DOWN",
"wait": "Please wait",
"lan_users": "Użytkownicy LAN",
"wlan_users": "Użytkownicy WLAN",
"up": "Wysyłanie",
"down": "Pobieranie",
"wait": "Proszę czekać",
"lan": "LAN",
"wlan": "WLAN",
"devices": "Devices",
"lan_devices": "LAN Devices",
"wlan_devices": "WLAN Devices"
"devices": "Urządzenia",
"lan_devices": "Urządzenia LAN",
"wlan_devices": "Urządzenia WLAN"
},
"plex": {
"streams": "Active Streams",
"movies": "Movies",
"tv": "TV Shows"
"movies": "Filmy",
"tv": "Seriale"
},
"glances": {
"cpu": "CPU",
"mem": "MEM",
"wait": "Please wait"
"wait": "Proszę czekać"
},
"changedetectionio": {
"diffsDetected": "Diffs Detected",
@ -218,60 +218,85 @@
"0-night": "Clear",
"1-day": "Mainly Sunny",
"1-night": "Mainly Clear",
"2-day": "Partly Cloudy",
"2-night": "Partly Cloudy",
"3-day": "Cloudy",
"3-night": "Cloudy",
"45-day": "Foggy",
"45-night": "Foggy",
"48-day": "Foggy",
"48-night": "Foggy",
"51-day": "Light Drizzle",
"51-night": "Light Drizzle",
"53-day": "Drizzle",
"53-night": "Drizzle",
"55-day": "Heavy Drizzle",
"55-night": "Heavy Drizzle",
"2-day": "Częściowo pochmurnie",
"2-night": "Częściowo pochmurnie",
"3-day": "Pochmurnie",
"3-night": "Pochmurnie",
"45-day": "Mgliście",
"45-night": "Mgliście",
"48-day": "Mgliście",
"48-night": "Mgliście",
"51-day": "Lekka mżawka",
"51-night": "Lekka mżawka",
"53-day": "Mżawka",
"53-night": "Mżawka",
"55-day": "Ciężka mżawka",
"55-night": "Ciężka mżawka",
"56-day": "Light Freezing Drizzle",
"56-night": "Light Freezing Drizzle",
"57-day": "Freezing Drizzle",
"57-night": "Freezing Drizzle",
"61-day": "Light Rain",
"61-night": "Light Rain",
"63-day": "Rain",
"63-night": "Rain",
"65-day": "Heavy Rain",
"65-night": "Heavy Rain",
"66-day": "Freezing Rain",
"66-night": "Freezing Rain",
"67-day": "Freezing Rain",
"67-night": "Freezing Rain",
"71-day": "Light Snow",
"71-night": "Light Snow",
"73-day": "Snow",
"73-night": "Snow",
"75-day": "Heavy Snow",
"75-night": "Heavy Snow",
"61-day": "Lekki deszcz",
"61-night": "Lekki deszcz",
"63-day": "Deszcz",
"63-night": "Deszcz",
"65-day": "Ciężki deszcz",
"65-night": "Ciężki deszcz",
"66-day": "Mroźny deszcz",
"66-night": "Mroźny deszcz",
"67-day": "Mroźny deszcz",
"67-night": "Mroźny deszcz",
"71-day": "Lekki śnieg",
"71-night": "Lekki śnieg",
"73-day": "Śnieg",
"73-night": "Śnieg",
"75-day": "Ciężki śnieg",
"75-night": "Ciężki śnieg",
"77-night": "Snow Grains",
"80-day": "Light Showers",
"80-night": "Light Showers",
"81-day": "Showers",
"81-night": "Showers",
"82-day": "Heavy Showers",
"82-night": "Heavy Showers",
"85-day": "Snow Showers",
"85-night": "Snow Showers",
"86-day": "Snow Showers",
"86-night": "Snow Showers",
"95-day": "Thunderstorm",
"95-night": "Thunderstorm",
"96-day": "Thunderstorm With Hail",
"96-night": "Thunderstorm With Hail",
"99-day": "Thunderstorm With Hail",
"99-night": "Thunderstorm With Hail"
"80-day": "Lekkie opady",
"80-night": "Lekkie opady",
"81-day": "Opady",
"81-night": "Opady",
"82-day": "Ciężkie opady",
"82-night": "Ciężkie opady",
"85-day": "Opady śniegu",
"85-night": "Opady śniegu",
"86-day": "Opady śniegu",
"86-night": "Opady śniegu",
"95-day": "Burze z piorunami",
"95-night": "Burze z piorunami",
"96-day": "Burza z gradobiciem",
"96-night": "Burza z gradobiciem",
"99-day": "Burza z gradobiciem",
"99-night": "Burza z gradobiciem"
},
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
"service": "Usługi"
},
"homebridge": {
"available_update": "System",
"updates": "Aktualizacje",
"update_available": "Dostępna aktualizacja",
"up_to_date": "Aktualny",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -284,5 +284,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"available_update": "System",
"updates": "Updates",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges_status": "{{ok}}/{{total}}",
"child_bridges": "Child Bridges"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -209,69 +209,94 @@
"wait": "దయచేసి వేచి ఉండండి"
},
"changedetectionio": {
"totalObserved": "Total Observed",
"diffsDetected": "Diffs Detected"
"totalObserved": "మొత్తం గమనించబడింది",
"diffsDetected": "తేడాలు గుర్తించబడ్డాయి"
},
"wmo": {
"0-day": "Sunny",
"0-night": "Clear",
"57-night": "Freezing Drizzle",
"73-day": "Snow",
"73-night": "Snow",
"75-day": "Heavy Snow",
"75-night": "Heavy Snow",
"77-day": "Snow Grains",
"77-night": "Snow Grains",
"80-day": "Light Showers",
"80-night": "Light Showers",
"81-day": "Showers",
"81-night": "Showers",
"82-day": "Heavy Showers",
"82-night": "Heavy Showers",
"85-day": "Snow Showers",
"85-night": "Snow Showers",
"1-day": "Mainly Sunny",
"1-night": "Mainly Clear",
"2-day": "Partly Cloudy",
"2-night": "Partly Cloudy",
"3-day": "Cloudy",
"3-night": "Cloudy",
"45-day": "Foggy",
"45-night": "Foggy",
"48-day": "Foggy",
"48-night": "Foggy",
"51-day": "Light Drizzle",
"51-night": "Light Drizzle",
"53-day": "Drizzle",
"53-night": "Drizzle",
"55-day": "Heavy Drizzle",
"55-night": "Heavy Drizzle",
"56-day": "Light Freezing Drizzle",
"56-night": "Light Freezing Drizzle",
"57-day": "Freezing Drizzle",
"61-day": "Light Rain",
"61-night": "Light Rain",
"63-day": "Rain",
"63-night": "Rain",
"65-day": "Heavy Rain",
"65-night": "Heavy Rain",
"66-day": "Freezing Rain",
"66-night": "Freezing Rain",
"67-day": "Freezing Rain",
"67-night": "Freezing Rain",
"71-day": "Light Snow",
"71-night": "Light Snow",
"86-day": "Snow Showers",
"86-night": "Snow Showers",
"95-day": "Thunderstorm",
"95-night": "Thunderstorm",
"96-day": "Thunderstorm With Hail",
"96-night": "Thunderstorm With Hail",
"99-day": "Thunderstorm With Hail",
"99-night": "Thunderstorm With Hail"
"0-day": "సన్నీ",
"0-night": "స్పష్టమైన",
"57-night": "గడ్డకట్టే చినుకులు",
"73-day": "మంచు",
"73-night": "మంచు",
"75-day": "భారీ మంచు",
"75-night": "భారీ మంచు",
"77-day": "మంచు గింజలు",
"77-night": "మంచు గింజలు",
"80-day": "తేలికపాటి జల్లులు",
"80-night": "తేలికపాటి జల్లులు",
"81-day": "జల్లులు",
"81-night": "జల్లులు",
"82-day": "భారీ వర్షాలు",
"82-night": "భారీ వర్షాలు",
"85-day": "మంచు జల్లులు",
"85-night": "మంచు జల్లులు",
"1-day": "ప్రధానంగా ఎండ",
"1-night": "ప్రధానంగా స్పష్టంగా",
"2-day": "పాక్షికంగా మేఘావృతమై ఉంటుంది",
"2-night": "పాక్షికంగా మేఘావృతమై ఉంటుంది",
"3-day": "మేఘావృతం",
"3-night": "మేఘావృతం",
"45-day": "పొగమంచు",
"45-night": "పొగమంచు",
"48-day": "పొగమంచు",
"48-night": "పొగమంచు",
"51-day": "తేలికపాటి చినుకులు",
"51-night": "తేలికపాటి చినుకులు",
"53-day": "చినుకులు",
"53-night": "చినుకులు",
"55-day": "భారీ చినుకులు",
"55-night": "భారీ చినుకులు",
"56-day": "తేలికపాటి గడ్డకట్టే చినుకులు",
"56-night": "తేలికపాటి గడ్డకట్టే చినుకులు",
"57-day": "గడ్డకట్టే చినుకులు",
"61-day": "తేలికపాటి వర్షం",
"61-night": "తేలికపాటి వర్షం",
"63-day": "వర్షం",
"63-night": "వర్షం",
"65-day": "భారీవర్షం",
"65-night": "భారీవర్షం",
"66-day": "గడ్డకట్టే వర్షం",
"66-night": "గడ్డకట్టే వర్షం",
"67-day": "గడ్డకట్టే వర్షం",
"67-night": "గడ్డకట్టే వర్షం",
"71-day": "తేలికపాటి మంచు",
"71-night": "తేలికపాటి మంచు",
"86-day": "మంచు జల్లులు",
"86-night": "మంచు జల్లులు",
"95-day": "ఉరుము",
"95-night": "ఉరుము",
"96-day": "వడగళ్లతో కూడిన ఉరుములతో కూడిన వర్షం",
"96-night": "వడగళ్లతో కూడిన ఉరుములతో కూడిన వర్షం",
"99-day": "వడగళ్లతో కూడిన ఉరుములతో కూడిన వర్షం",
"99-night": "వడగళ్లతో కూడిన ఉరుములతో కూడిన వర్షం"
},
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
"bookmark": "బుక్మార్క్",
"service": "సేవ"
},
"homebridge": {
"available_update": "వ్యవస్థ",
"updates": "నవీకరణలు",
"update_available": "అందుబాటులో నవీకరణ",
"up_to_date": "తాజాగా",
"child_bridges": "పిల్ల వంతెనలు",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"rejectedPushes": "తిరస్కరించారు",
"approvedPushes": "ఆమోదించబడింది",
"filters": "ఫిల్టర్లు",
"indexers": "సూచికలు"
},
"watchtower": {
"containers_scanned": "స్కాన్ చేశారు",
"containers_updated": "నవీకరించబడింది",
"containers_failed": "విఫలమయ్యారు"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -273,5 +273,30 @@
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
}
}

View File

@ -27,10 +27,12 @@ export default function DateTime({ options }) {
const dateFormat = new Intl.DateTimeFormat(i18n.language, { ...format });
return (
<div className="flex flex-row items-center grow justify-end">
<span className={`text-theme-800 dark:text-theme-200 ${textSizes[textSize || "lg"]}`}>
{dateFormat.format(date)}
</span>
<div className="flex flex-col justify-center first:ml-0 ml-4">
<div className="flex flex-row items-center grow justify-end">
<span className={`text-theme-800 dark:text-theme-200 ${textSizes[textSize || "lg"]}`}>
{dateFormat.format(date)}
</span>
</div>
</div>
);
}

View File

@ -1,6 +1,7 @@
/* eslint-disable react/jsx-props-no-spreading */
import { SWRConfig } from "swr";
import { appWithTranslation } from "next-i18next";
import Head from "next/head";
import "styles/globals.css";
import "styles/theme.css";
@ -18,6 +19,10 @@ function MyApp({ Component, pageProps }) {
fetcher: (resource, init) => fetch(resource, init).then((res) => res.json()),
}}
>
<Head>
{/* https://nextjs.org/docs/messages/no-document-viewport-meta */}
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
</Head>
<ColorProvider>
<ThemeProvider>
<SettingsProvider>

View File

@ -9,7 +9,6 @@ export default function Document() {
content="A highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations."
/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png?v=4" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png?v=4" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png?v=4" />

View File

@ -35,7 +35,7 @@ const Version = dynamic(() => import("components/version"), {
ssr: false,
});
const rightAlignedWidgets = ["weatherapi", "openweathermap", "weather", "search", "datetime"];
const rightAlignedWidgets = ["weatherapi", "openweathermap", "weather", "openmeteo", "search", "datetime"];
export async function getStaticProps() {
let logger;
@ -272,7 +272,7 @@ function Home({ initialSettings }) {
)}
{bookmarks && (
<div className="grow flex flex-wrap pt-0 p-4 sm:p-8">
<div className="grow flex flex-wrap pt-0 p-4 sm:p-8 gap-x-2">
{bookmarks.map((group) => (
<BookmarksGroup key={group.name} group={group} />
))}

View File

@ -1,6 +1,6 @@
---
# For configuration options and examples, please see:
# https://github.com/benphelps/homepage/wiki/Bookmarks
# https://gethomepage.dev/en/configs/bookmarks
- Developer:
- Github:

View File

@ -1,6 +1,6 @@
---
# For configuration options and examples, please see:
# https://github.com/benphelps/homepage/wiki/Docker-Integration
# https://gethomepage.dev/en/configs/docker/
# my-docker:
# host: 127.0.0.1

View File

@ -1,6 +1,6 @@
---
# For configuration options and examples, please see:
# https://github.com/benphelps/homepage/wiki/Services
# https://gethomepage.dev/en/configs/services
- My First Group:
- My First Service:

View File

@ -1,6 +1,6 @@
---
# For configuration options and examples, please see:
# https://github.com/benphelps/homepage/wiki/Settings
# https://gethomepage.dev/en/configs/settings
providers:
openweathermap: openweathermapapikey

View File

@ -1,6 +1,6 @@
---
# For configuration options and examples, please see:
# https://github.com/benphelps/homepage/wiki/Information-Widgets
# https://gethomepage.dev/en/configs/widgets
- resources:
cpu: true

View File

@ -31,6 +31,10 @@ export default async function credentialedProxyHandler(req, res) {
headers.Authorization = `Bearer ${widget.key}`;
} else if (widget.type === "proxmox") {
headers.Authorization = `PVEAPIToken=${widget.username}=${widget.password}`;
} else if (widget.type === "autobrr") {
headers["X-API-Token"] = `${widget.key}`;
} else if (widget.type === "tubearchivist") {
headers.Authorization = `Token ${widget.key}`;
} else {
headers["X-API-Key"] = `${widget.key}`;
}

View File

@ -0,0 +1,39 @@
import { useTranslation } from "next-i18next";
import Container from "components/services/widget/container";
import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api";
export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service;
const { data: statsData, error: statsError } = useWidgetAPI(widget, "stats");
const { data: filtersData, error: filtersError } = useWidgetAPI(widget, "filters");
const { data: indexersData, error: indexersError } = useWidgetAPI(widget, "indexers");
if (statsError || filtersError || indexersError) {
return <Container error={t("widget.api_error")} />;
}
if (!statsData || !filtersData || !indexersData) {
return (
<Container service={service}>
<Block label="autobrr.approvedPushes" />
<Block label="autobrr.rejectedPushes" />
<Block label="autobrr.filters" />
<Block label="autobrr.indexers" />
</Container>
);
}
return (
<Container service={service}>
<Block label="autobrr.approvedPushes" value={t("common.number", { value: statsData.push_approved_count })} />
<Block label="autobrr.rejectedPushes" value={t("common.number", { value: statsData.push_rejected_count })} />
<Block label="autobrr.filters" value={t("common.number", { value: filtersData.length })} />
<Block label="autobrr.indexers" value={t("common.number", { value: indexersData.length })} />
</Container>
);
}

View File

@ -0,0 +1,20 @@
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
const widget = {
api: "{url}/api/{endpoint}",
proxyHandler: credentialedProxyHandler,
mappings: {
stats: {
endpoint: "release/stats",
},
filters: {
endpoint: "filters",
},
indexers: {
endpoint: "release/indexers",
},
},
};
export default widget;

View File

@ -3,12 +3,14 @@ import dynamic from "next/dynamic";
const components = {
adguard: dynamic(() => import("./adguard/component")),
authentik: dynamic(() => import("./authentik/component")),
autobrr: dynamic(() => import("./autobrr/component")),
bazarr: dynamic(() => import("./bazarr/component")),
changedetectionio: dynamic(() => import("./changedetectionio/component")),
coinmarketcap: dynamic(() => import("./coinmarketcap/component")),
docker: dynamic(() => import("./docker/component")),
emby: dynamic(() => import("./emby/component")),
gotify: dynamic(() => import("./gotify/component")),
homebridge: dynamic(() => import("./homebridge/component")),
jackett: dynamic(() => import("./jackett/component")),
jellyfin: dynamic(() => import("./emby/component")),
jellyseerr: dynamic(() => import("./jellyseerr/component")),
@ -34,7 +36,9 @@ const components = {
tautulli: dynamic(() => import("./tautulli/component")),
traefik: dynamic(() => import("./traefik/component")),
transmission: dynamic(() => import("./transmission/component")),
tubearchivist: dynamic(() => import("./tubearchivist/component")),
unifi: dynamic(() => import("./unifi/component")),
watchtower: dynamic(() => import("./watchtower/component")),
};
export default components;

View File

@ -10,7 +10,7 @@ const widget = {
},
PlayControl: {
method: "POST",
enpoint: "Sessions/{sessionId}/Playing/{command}",
endpoint: "Sessions/{sessionId}/Playing/{command}",
segments: ["sessionId", "command"],
},
},

View File

@ -0,0 +1,51 @@
import { useTranslation } from "next-i18next";
import Container from "components/services/widget/container";
import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api";
export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service;
const { data: homebridgeData, error: homebridgeError } = useWidgetAPI(widget, "info");
if (homebridgeError || homebridgeData?.error) {
return <Container error={t("widget.api_error")} />;
}
if (!homebridgeData) {
return (
<Container service={service}>
<Block label="widget.status" />
<Block label="homebridge.updates" />
<Block label="homebridge.child_bridges" />
</Container>
);
}
return (
<Container service={service}>
<Block
label="widget.status"
value={`${homebridgeData.status[0].toUpperCase()}${homebridgeData.status.substr(1)}`}
/>
<Block
label="homebridge.updates"
value={
(homebridgeData.updateAvailable || homebridgeData.plugins?.updatesAvailable)
? t("homebridge.update_available")
: t("homebridge.up_to_date")}
/>
{homebridgeData?.childBridges?.total > 0 &&
<Block
label="homebridge.child_bridges"
value={t("homebridge.child_bridges_status", {
total: homebridgeData.childBridges.total,
ok: homebridgeData.childBridges.running
})}
/>}
</Container>
);
}

View File

@ -0,0 +1,106 @@
import cache from "memory-cache";
import { httpProxy } from "utils/proxy/http";
import { formatApiCall } from "utils/proxy/api-helpers";
import getServiceWidget from "utils/config/service-helpers";
import createLogger from "utils/logger";
import widgets from "widgets/widgets";
const proxyName = "homebridgeProxyHandler";
const sessionTokenCacheKey = `${proxyName}__sessionToken`;
const logger = createLogger(proxyName);
async function login(widget) {
const endpoint = "auth/login";
const api = widgets?.[widget.type]?.api
const loginUrl = new URL(formatApiCall(api, { endpoint, ...widget }));
const loginBody = { username: widget.username, password: widget.password };
const headers = { "Content-Type": "application/json" };
// eslint-disable-next-line no-unused-vars
const [status, contentType, data, responseHeaders] = await httpProxy(loginUrl, {
method: "POST",
body: JSON.stringify(loginBody),
headers,
});
try {
const { access_token: accessToken, expires_in: expiresIn } = JSON.parse(data.toString());
cache.put(sessionTokenCacheKey, accessToken, (expiresIn * 1000) - 5 * 60 * 1000); // expiresIn (s) - 5m
return { accessToken };
} catch (e) {
logger.error("Unable to login to Homebridge API: %s", e);
}
return { accessToken: false };
}
async function apiCall(widget, endpoint) {
const headers = {
"content-type": "application/json",
"Authorization": `Bearer ${cache.get(sessionTokenCacheKey)}`,
}
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
const method = "GET";
let [status, contentType, data, responseHeaders] = await httpProxy(url, {
method,
headers,
});
if (status === 401) {
logger.debug("Homebridge API rejected the request, attempting to obtain new session token");
const { accessToken } = login(widget);
headers.Authorization = `Bearer ${accessToken}`;
// retry the request, now with the new session token
[status, contentType, data, responseHeaders] = await httpProxy(url, {
method,
headers,
});
}
if (status !== 200) {
logger.error("Error getting data from Homebridge: %d. Data: %s", status, data);
}
return { status, contentType, data: JSON.parse(data.toString()), responseHeaders };
}
export default async function homebridgeProxyHandler(req, res) {
const { group, service } = req.query;
if (!group || !service) {
logger.debug("Invalid or missing service '%s' or group '%s'", service, group);
return res.status(400).json({ error: "Invalid proxy service type" });
}
const widget = await getServiceWidget(group, service);
if (!widget) {
logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group);
return res.status(400).json({ error: "Invalid proxy service type" });
}
if (!cache.get(sessionTokenCacheKey)) {
await login(widget);
}
const { data: statusData } = await apiCall(widget, "status/homebridge");
const { data: versionData } = await apiCall(widget, "status/homebridge-version");
const { data: childBridgeData } = await apiCall(widget, "status/homebridge/child-bridges");
const { data: pluginsData } = await apiCall(widget, "plugins");
return res.status(200).send({
status: statusData?.status,
updateAvailable: versionData?.updateAvailable,
plugins: {
updatesAvailable: pluginsData?.filter(p => p.updateAvailable).length,
},
childBridges: {
running: childBridgeData?.filter(cb => cb.status === "ok").length,
total: childBridgeData?.length
}
});
}

View File

@ -0,0 +1,14 @@
import homebridgeProxyHandler from "./proxy";
const widget = {
api: "{url}/api/{endpoint}",
proxyHandler: homebridgeProxyHandler,
mappings: {
info: {
endpoint: "/",
}
},
};
export default widget;

View File

@ -0,0 +1,40 @@
import { useTranslation } from "next-i18next";
import Container from "components/services/widget/container";
import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api";
export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service;
const { data: downloadsData, error: downloadsError } = useWidgetAPI(widget, "downloads");
const { data: videosData, error: videosError } = useWidgetAPI(widget, "videos");
const { data: channelsData, error: channelsError } = useWidgetAPI(widget, "channels");
const { data: playlistsData, error: playlistsError } = useWidgetAPI(widget, "playlists");
if (downloadsError || videosError || channelsError || playlistsError) {
return <Container error={t("widget.api_error")} />;
}
if (!downloadsData || !videosData || !channelsData || !playlistsData) {
return (
<Container service={service}>
<Block label="tubearchivist.downloads" />
<Block label="tubearchivist.videos" />
<Block label="tubearchivist.channels" />
<Block label="tubearchivist.playlists" />
</Container>
);
}
return (
<Container service={service}>
<Block label="tubearchivist.downloads" value={t("common.number", { value: downloadsData?.paginate?.total_hits })} />
<Block label="tubearchivist.videos" value={t("common.number", { value: videosData?.paginate?.total_hits })} />
<Block label="tubearchivist.channels" value={t("common.number", { value: channelsData?.paginate?.total_hits })} />
<Block label="tubearchivist.playlists" value={t("common.number", { value: playlistsData?.paginate?.total_hits })} />
</Container>
);
}

View File

@ -0,0 +1,23 @@
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
const widget = {
api: "{url}/api/{endpoint}",
proxyHandler: credentialedProxyHandler,
mappings: {
downloads: {
endpoint: "download",
},
videos: {
endpoint: "video",
},
channels: {
endpoint: "channel",
},
playlists: {
endpoint: "playlist",
},
},
};
export default widget;

View File

@ -0,0 +1,36 @@
import { useTranslation } from "next-i18next";
import Container from "components/services/widget/container";
import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api";
export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service;
const { data: watchData, error: watchError } = useWidgetAPI(widget, "watchtower");
if (watchError || !watchData) {
return <Container error={t("widget.api_error")} />;
}
if (!watchData) {
return (
<Container service={service}>
<Block label="watchtower.containers_scanned " />
<Block label="watchtower.containers_updated" />
<Block label="watchtower.containers_failed" />
</Container>
);
}
return (
<Container service={service}>
<Block label="watchtower.containers_scanned" value={t("common.number", { value: watchData.watchtower_containers_scanned })} />
<Block label="watchtower.containers_updated" value={t("common.number", { value: watchData.watchtower_containers_updated })} />
<Block label="watchtower.containers_failed" value={t("common.number", { value: watchData.watchtower_containers_failed })} />
</Container>
);
}

View File

@ -0,0 +1,48 @@
import { httpProxy } from "utils/proxy/http";
import { formatApiCall } from "utils/proxy/api-helpers";
import getServiceWidget from "utils/config/service-helpers";
import createLogger from "utils/logger";
import widgets from "widgets/widgets";
const proxyName = "watchtowerProxyHandler";
const logger = createLogger(proxyName);
export default async function watchtowerProxyHandler(req, res) {
const { group, service, endpoint } = req.query;
if (!group || !service) {
logger.debug("Invalid or missing service '%s' or group '%s'", service, group);
return res.status(400).json({ error: "Invalid proxy service type" });
}
const widget = await getServiceWidget(group, service);
if (!widget) {
logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group);
return res.status(400).json({ error: "Invalid proxy service type" });
}
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
const [status, contentType, data] = await httpProxy(url, {
method: "GET",
headers: {
"Authorization": `Bearer ${widget.key}`,
}
});
if (status !== 200 || !data) {
logger.error("Error getting data from WatchTower: %d. Data: %s", status, data);
}
const cleanData = data.toString().split("\n").filter(s => s.startsWith("watchtower"))
const jsonRes = {}
cleanData.map(e => e.split(" ")).forEach(strArray => {
const [key, value] = strArray
jsonRes[key] = value
})
if (contentType) res.setHeader("Content-Type", contentType);
return res.status(status).send(jsonRes);
}

View File

@ -0,0 +1,14 @@
import watchtowerProxyHandler from "./proxy";
const widget = {
api: "{url}/{endpoint}",
proxyHandler: watchtowerProxyHandler,
mappings: {
"watchtower": {
endpoint: "v1/metrics",
},
},
};
export default widget;

View File

@ -1,10 +1,12 @@
import adguard from "./adguard/widget";
import authentik from "./authentik/widget";
import autobrr from "./autobrr/widget";
import bazarr from "./bazarr/widget";
import changedetectionio from "./changedetectionio/widget";
import coinmarketcap from "./coinmarketcap/widget";
import emby from "./emby/widget";
import gotify from "./gotify/widget";
import homebridge from "./homebridge/widget";
import jackett from "./jackett/widget";
import jellyseerr from "./jellyseerr/widget";
import lidarr from "./lidarr/widget";
@ -29,16 +31,20 @@ import strelaysrv from "./strelaysrv/widget";
import tautulli from "./tautulli/widget";
import traefik from "./traefik/widget";
import transmission from "./transmission/widget";
import tubearchivist from "./tubearchivist/widget";
import unifi from "./unifi/widget";
import watchtower from './watchtower/widget'
const widgets = {
adguard,
authentik,
autobrr,
bazarr,
changedetectionio,
coinmarketcap,
emby,
gotify,
homebridge,
jackett,
jellyfin: emby,
jellyseerr,
@ -64,8 +70,10 @@ const widgets = {
tautulli,
traefik,
transmission,
tubearchivist,
unifi,
unifi_console: unifi,
watchtower,
};
export default widgets;